termux-app

Форк
0
830 строк · 36.3 Кб
1
package com.termux.shared.android;
2

3
import android.app.ActivityManager;
4
import android.app.admin.DevicePolicyManager;
5
import android.content.ComponentName;
6
import android.content.Context;
7
import android.content.Intent;
8
import android.content.pm.ApplicationInfo;
9
import android.content.pm.PackageInfo;
10
import android.content.pm.PackageManager;
11
import android.os.Build;
12
import android.os.UserHandle;
13
import android.os.UserManager;
14

15
import androidx.annotation.NonNull;
16
import androidx.annotation.Nullable;
17
import androidx.annotation.RequiresApi;
18

19
import com.termux.shared.R;
20
import com.termux.shared.data.DataUtils;
21
import com.termux.shared.interact.MessageDialogUtils;
22
import com.termux.shared.logger.Logger;
23
import com.termux.shared.reflection.ReflectionUtils;
24

25
import java.lang.reflect.Field;
26
import java.security.MessageDigest;
27
import java.util.List;
28

29
public class PackageUtils {
30

31
    private static final String LOG_TAG = "PackageUtils";
32

33
    /**
34
     * Get the {@link Context} for the package name with {@link Context#CONTEXT_RESTRICTED} flags.
35
     *
36
     * @param context The {@link Context} to use to get the {@link Context} of the {@code packageName}.
37
     * @param packageName The package name whose {@link Context} to get.
38
     * @return Returns the {@link Context}. This will {@code null} if an exception is raised.
39
     */
40
    @Nullable
41
    public static Context getContextForPackage(@NonNull final Context context, String packageName) {
42
       return getContextForPackage(context, packageName, Context.CONTEXT_RESTRICTED);
43
    }
44

45
    /**
46
     * Get the {@link Context} for the package name.
47
     *
48
     * @param context The {@link Context} to use to get the {@link Context} of the {@code packageName}.
49
     * @param packageName The package name whose {@link Context} to get.
50
     * @param flags The flags for {@link Context} type.
51
     * @return Returns the {@link Context}. This will {@code null} if an exception is raised.
52
     */
53
    @Nullable
54
    public static Context getContextForPackage(@NonNull final Context context, String packageName, int flags) {
55
        try {
56
            return context.createPackageContext(packageName, flags);
57
        } catch (Exception e) {
58
            Logger.logVerbose(LOG_TAG, "Failed to get \"" + packageName + "\" package context with flags " + flags + ": " + e.getMessage());
59
            return null;
60
        }
61
    }
62

63
    /**
64
     * Get the {@link Context} for a package name.
65
     *
66
     * @param context The {@link Context} to use to get the {@link Context} of the {@code packageName}.
67
     * @param packageName The package name whose {@link Context} to get.
68
     * @param exitAppOnError If {@code true} and failed to get package context, then a dialog will
69
     *                       be shown which when dismissed will exit the app.
70
     * @param helpUrl The help user to add to {@link R.string#error_get_package_context_failed_help_url_message}.
71
     * @return Returns the {@link Context}. This will {@code null} if an exception is raised.
72
     */
73
    @Nullable
74
    public static Context getContextForPackageOrExitApp(@NonNull Context context, String packageName,
75
                                                        final boolean exitAppOnError, @Nullable String helpUrl) {
76
        Context packageContext = getContextForPackage(context, packageName);
77

78
        if (packageContext == null && exitAppOnError) {
79
            String errorMessage = context.getString(R.string.error_get_package_context_failed_message,
80
                packageName);
81
            if (!DataUtils.isNullOrEmpty(helpUrl))
82
                errorMessage += "\n" + context.getString(R.string.error_get_package_context_failed_help_url_message, helpUrl);
83
            Logger.logError(LOG_TAG, errorMessage);
84
            MessageDialogUtils.exitAppWithErrorMessage(context,
85
                context.getString(R.string.error_get_package_context_failed_title),
86
                errorMessage);
87
        }
88

89
        return packageContext;
90
    }
91

92

93

94
    /**
95
     * Get the {@link PackageInfo} for the package associated with the {@code context}.
96
     *
97
     * @param context The {@link Context} for the package.
98
     * @return Returns the {@link PackageInfo}. This will be {@code null} if an exception is raised.
99
     */
100
    public static PackageInfo getPackageInfoForPackage(@NonNull final Context context) {
101
        return getPackageInfoForPackage(context, context.getPackageName());
102
    }
103

104
    /**
105
     * Get the {@link PackageInfo} for the package associated with the {@code context}.
106
     *
107
     * @param context The {@link Context} for the package.
108
     * @param flags The flags to pass to {@link PackageManager#getPackageInfo(String, int)}.
109
     * @return Returns the {@link PackageInfo}. This will be {@code null} if an exception is raised.
110
     */
111
    @Nullable
112
    public static PackageInfo getPackageInfoForPackage(@NonNull final Context context, final int flags) {
113
        return getPackageInfoForPackage(context, context.getPackageName(), flags);
114
    }
115

116
    /**
117
     * Get the {@link PackageInfo} for the package associated with the {@code packageName}.
118
     *
119
     * @param context The {@link Context} for operations.
120
     * @param packageName The package name of the package.
121
     * @return Returns the {@link PackageInfo}. This will be {@code null} if an exception is raised.
122
     */
123
    public static PackageInfo getPackageInfoForPackage(@NonNull final Context context, @NonNull final String packageName) {
124
        return getPackageInfoForPackage(context, packageName, 0);
125
    }
126

127
    /**
128
     * Get the {@link PackageInfo} for the package associated with the {@code packageName}.
129
     *
130
     * Also check {@link #isAppInstalled(Context, String, String) if targetting targeting sdk
131
     * `30` (android `11`) since {@link PackageManager.NameNotFoundException} may be thrown.
132
     *
133
     * @param context The {@link Context} for operations.
134
     * @param packageName The package name of the package.
135
     * @param flags The flags to pass to {@link PackageManager#getPackageInfo(String, int)}.
136
     * @return Returns the {@link PackageInfo}. This will be {@code null} if an exception is raised.
137
     */
138
    @Nullable
139
    public static PackageInfo getPackageInfoForPackage(@NonNull final Context context, @NonNull final String packageName, final int flags) {
140
        try {
141
            return context.getPackageManager().getPackageInfo(packageName, flags);
142
        } catch (final Exception e) {
143
            return null;
144
        }
145
    }
146

147

148

149
    /**
150
     * Get the {@link ApplicationInfo} for the {@code packageName}.
151
     *
152
     * @param context The {@link Context} for operations.
153
     * @param packageName The package name of the package.
154
     * @return Returns the {@link ApplicationInfo}. This will be {@code null} if an exception is raised.
155
     */
156
    @Nullable
157
    public static ApplicationInfo getApplicationInfoForPackage(@NonNull final Context context, @NonNull final String packageName) {
158
        return getApplicationInfoForPackage(context, packageName, 0);
159
    }
160

161
    /**
162
     * Get the {@link ApplicationInfo} for the {@code packageName}.
163
     *
164
     * Also check {@link #isAppInstalled(Context, String, String) if targetting targeting sdk
165
     * `30` (android `11`) since {@link PackageManager.NameNotFoundException} may be thrown.
166
     *
167
     * @param context The {@link Context} for operations.
168
     * @param packageName The package name of the package.
169
     * @param flags The flags to pass to {@link PackageManager#getApplicationInfo(String, int)}.
170
     * @return Returns the {@link ApplicationInfo}. This will be {@code null} if an exception is raised.
171
     */
172
    @Nullable
173
    public static ApplicationInfo getApplicationInfoForPackage(@NonNull final Context context, @NonNull final String packageName, final int flags) {
174
        try {
175
            return context.getPackageManager().getApplicationInfo(packageName, flags);
176
        } catch (final Exception e) {
177
            return null;
178
        }
179
    }
180

181
    /**
182
     * Get the {@code privateFlags} {@link Field} of the {@link ApplicationInfo} class.
183
     *
184
     * @param applicationInfo The {@link ApplicationInfo} for the package.
185
     * @return Returns the private flags or {@code null} if an exception was raised.
186
     */
187
    @Nullable
188
    public static Integer getApplicationInfoPrivateFlagsForPackage(@NonNull final ApplicationInfo applicationInfo) {
189
        ReflectionUtils.bypassHiddenAPIReflectionRestrictions();
190
        try {
191
            return (Integer) ReflectionUtils.invokeField(ApplicationInfo.class, "privateFlags", applicationInfo).value;
192
        } catch (Exception e) {
193
            // ClassCastException may be thrown
194
            Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get privateFlags field value for ApplicationInfo class", e);
195
            return null;
196
        }
197
    }
198

199
    /**
200
     * Get the {@code seInfo} {@link Field} of the {@link ApplicationInfo} class.
201
     *
202
     * String retrieved from the seinfo tag found in selinux policy. This value can be set through
203
     * the mac_permissions.xml policy construct. This value is used for setting an SELinux security
204
     * context on the process as well as its data directory.
205
     *
206
     * https://cs.android.com/android/platform/superproject/+/android-7.1.0_r1:frameworks/base/core/java/android/content/pm/ApplicationInfo.java;l=609
207
     * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r32:frameworks/base/core/java/android/content/pm/ApplicationInfo.java;l=981
208
     * https://cs.android.com/android/platform/superproject/+/android-7.0.0_r1:frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java;l=282
209
     * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r32:frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java;l=375
210
     * https://cs.android.com/android/_/android/platform/frameworks/base/+/be0b8896d1bc385d4c8fb54c21929745935dcbea
211
     *
212
     * @param applicationInfo The {@link ApplicationInfo} for the package.
213
     * @return Returns the selinux info or {@code null} if an exception was raised.
214
     */
215
    @Nullable
216
    public static String getApplicationInfoSeInfoForPackage(@NonNull final ApplicationInfo applicationInfo) {
217
        ReflectionUtils.bypassHiddenAPIReflectionRestrictions();
218
        try {
219
            return (String) ReflectionUtils.invokeField(ApplicationInfo.class, Build.VERSION.SDK_INT < Build.VERSION_CODES.O ? "seinfo" : "seInfo", applicationInfo).value;
220
        } catch (Exception e) {
221
            // ClassCastException may be thrown
222
            Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get seInfo field value for ApplicationInfo class", e);
223
            return null;
224
        }
225
    }
226

227
    /**
228
     * Get the {@code seInfoUser} {@link Field} of the {@link ApplicationInfo} class.
229
     *
230
     * Also check {@link #getApplicationInfoSeInfoForPackage(ApplicationInfo)}.
231
     *
232
     * @param applicationInfo The {@link ApplicationInfo} for the package.
233
     * @return Returns the selinux info user or {@code null} if an exception was raised.
234
     */
235
    @Nullable
236
    public static String getApplicationInfoSeInfoUserForPackage(@NonNull final ApplicationInfo applicationInfo) {
237
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return null;
238
        ReflectionUtils.bypassHiddenAPIReflectionRestrictions();
239
        try {
240
            return (String) ReflectionUtils.invokeField(ApplicationInfo.class, "seInfoUser", applicationInfo).value;
241
        } catch (Exception e) {
242
            // ClassCastException may be thrown
243
            Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get seInfoUser field value for ApplicationInfo class", e);
244
            return null;
245
        }
246
    }
247

248
    /**
249
     * Get the {@code privateFlags} {@link Field} of the {@link ApplicationInfo} class.
250
     *
251
     * @param fieldName The name of the field to get.
252
     * @return Returns the field value or {@code null} if an exception was raised.
253
     */
254
    @Nullable
255
    public static Integer getApplicationInfoStaticIntFieldValue(@NonNull String fieldName) {
256
        ReflectionUtils.bypassHiddenAPIReflectionRestrictions();
257
        try {
258
            return (Integer) ReflectionUtils.invokeField(ApplicationInfo.class, fieldName, null).value;
259
        } catch (Exception e) {
260
            // ClassCastException may be thrown
261
            Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get \"" + fieldName + "\" field value for ApplicationInfo class", e);
262
            return null;
263
        }
264
    }
265

266
    /**
267
     * Check if the app associated with the {@code applicationInfo} has a specific flag set.
268
     *
269
     * @param flagToCheckName The name of the field for the flag to check.
270
     * @param applicationInfo The {@link ApplicationInfo} for the package.
271
     * @return Returns {@code true} if app has flag is set, otherwise {@code false}. This will be
272
     * {@code null} if an exception is raised.
273
     */
274
    @Nullable
275
    public static Boolean isApplicationInfoPrivateFlagSetForPackage(@NonNull String flagToCheckName, @NonNull final ApplicationInfo applicationInfo) {
276
        Integer privateFlags = getApplicationInfoPrivateFlagsForPackage(applicationInfo);
277
        if (privateFlags == null) return null;
278

279
        Integer flagToCheck = getApplicationInfoStaticIntFieldValue(flagToCheckName);
280
        if (flagToCheck == null) return null;
281

282
        return ( 0 != ( privateFlags & flagToCheck ) );
283
    }
284

285

286

287

288

289
    /**
290
     * Get the app name for the package associated with the {@code context}.
291
     *
292
     * @param context The {@link Context} for the package.
293
     * @return Returns the {@code android:name} attribute.
294
     */
295
    public static String getAppNameForPackage(@NonNull final Context context) {
296
        return getAppNameForPackage(context, context.getApplicationInfo());
297
    }
298

299
    /**
300
     * Get the app name for the package associated with the {@code applicationInfo}.
301
     *
302
     * @param context The {@link Context} for operations.
303
     * @param applicationInfo The {@link ApplicationInfo} for the package.
304
     * @return Returns the {@code android:name} attribute.
305
     */
306
    public static String getAppNameForPackage(@NonNull final Context context, @NonNull final ApplicationInfo applicationInfo) {
307
        return applicationInfo.loadLabel(context.getPackageManager()).toString();
308
    }
309

310

311

312
    /**
313
     * Get the package name for the package associated with the {@code context}.
314
     *
315
     * @param context The {@link Context} for the package.
316
     * @return Returns the package name.
317
     */
318
    public static String getPackageNameForPackage(@NonNull final Context context) {
319
        return getPackageNameForPackage(context.getApplicationInfo());
320
    }
321

322
    /**
323
     * Get the package name for the package associated with the {@code applicationInfo}.
324
     *
325
     * @param applicationInfo The {@link ApplicationInfo} for the package.
326
     * @return Returns the package name.
327
     */
328
    public static String getPackageNameForPackage(@NonNull final ApplicationInfo applicationInfo) {
329
        return applicationInfo.packageName;
330
    }
331

332

333

334
    /**
335
     * Get the uid for the package associated with the {@code context}.
336
     *
337
     * @param context The {@link Context} for the package.
338
     * @return Returns the uid.
339
     */
340
    public static int getUidForPackage(@NonNull final Context context) {
341
        return getUidForPackage(context.getApplicationInfo());
342
    }
343

344
    /**
345
     * Get the uid for the package associated with the {@code applicationInfo}.
346
     *
347
     * @param applicationInfo The {@link ApplicationInfo} for the package.
348
     * @return Returns the uid.
349
     */
350
    public static int getUidForPackage(@NonNull final ApplicationInfo applicationInfo) {
351
        return applicationInfo.uid;
352
    }
353

354

355

356
    /**
357
     * Get the {@code targetSdkVersion} for the package associated with the {@code context}.
358
     *
359
     * @param context The {@link Context} for the package.
360
     * @return Returns the {@code targetSdkVersion}.
361
     */
362
    public static int getTargetSDKForPackage(@NonNull final Context context) {
363
        return getTargetSDKForPackage(context.getApplicationInfo());
364
    }
365

366
    /**
367
     * Get the {@code targetSdkVersion} for the package associated with the {@code applicationInfo}.
368
     *
369
     * @param applicationInfo The {@link ApplicationInfo} for the package.
370
     * @return Returns the {@code targetSdkVersion}.
371
     */
372
    public static int getTargetSDKForPackage(@NonNull final ApplicationInfo applicationInfo) {
373
        return applicationInfo.targetSdkVersion;
374
    }
375

376

377

378
    /**
379
     * Get the base apk path for the package associated with the {@code context}.
380
     *
381
     * @param context The {@link Context} for the package.
382
     * @return Returns the base apk path.
383
     */
384
    public static String getBaseAPKPathForPackage(@NonNull final Context context) {
385
        return getBaseAPKPathForPackage(context.getApplicationInfo());
386
    }
387

388
    /**
389
     * Get the base apk path for the package associated with the {@code applicationInfo}.
390
     *
391
     * @param applicationInfo The {@link ApplicationInfo} for the package.
392
     * @return Returns the base apk path.
393
     */
394
    public static String getBaseAPKPathForPackage(@NonNull final ApplicationInfo applicationInfo) {
395
        return applicationInfo.publicSourceDir;
396
    }
397

398

399

400
    /**
401
     * Check if the app associated with the {@code context} has {@link ApplicationInfo#FLAG_DEBUGGABLE}
402
     * set.
403
     *
404
     * @param context The {@link Context} for the package.
405
     * @return Returns {@code true} if app is debuggable, otherwise {@code false}.
406
     */
407
    public static boolean isAppForPackageADebuggableBuild(@NonNull final Context context) {
408
        return isAppForPackageADebuggableBuild(context.getApplicationInfo());
409
    }
410

411
    /**
412
     * Check if the app associated with the {@code applicationInfo} has {@link ApplicationInfo#FLAG_DEBUGGABLE}
413
     * set.
414
     *
415
     * @param applicationInfo The {@link ApplicationInfo} for the package.
416
     * @return Returns {@code true} if app is debuggable, otherwise {@code false}.
417
     */
418
    public static boolean isAppForPackageADebuggableBuild(@NonNull final ApplicationInfo applicationInfo) {
419
        return ( 0 != ( applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE ) );
420
    }
421

422

423

424
    /**
425
     * Check if the app associated with the {@code context} has {@link ApplicationInfo#FLAG_EXTERNAL_STORAGE}
426
     * set.
427
     *
428
     * @param context The {@link Context} for the package.
429
     * @return Returns {@code true} if app is installed on external storage, otherwise {@code false}.
430
     */
431
    public static boolean isAppInstalledOnExternalStorage(@NonNull final Context context) {
432
        return isAppInstalledOnExternalStorage(context.getApplicationInfo());
433
    }
434

435
    /**
436
     * Check if the app associated with the {@code applicationInfo} has {@link ApplicationInfo#FLAG_EXTERNAL_STORAGE}
437
     * set.
438
     *
439
     * @param applicationInfo The {@link ApplicationInfo} for the package.
440
     * @return Returns {@code true} if app is installed on external storage, otherwise {@code false}.
441
     */
442
    public static boolean isAppInstalledOnExternalStorage(@NonNull final ApplicationInfo applicationInfo) {
443
        return ( 0 != ( applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE ) );
444
    }
445

446

447

448
    /**
449
     * Check if the app associated with the {@code context} has
450
     * ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE (requestLegacyExternalStorage)
451
     * set to {@code true} in app manifest.
452
     *
453
     * @param context The {@link Context} for the package.
454
     * @return Returns {@code true} if app has requested legacy external storage, otherwise
455
     * {@code false}. This will be {@code null} if an exception is raised.
456
     */
457
    @Nullable
458
    public static Boolean hasRequestedLegacyExternalStorage(@NonNull final Context context) {
459
        return hasRequestedLegacyExternalStorage(context.getApplicationInfo());
460
    }
461

462
    /**
463
     * Check if the app associated with the {@code applicationInfo} has
464
     * ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE (requestLegacyExternalStorage)
465
     * set to {@code true} in app manifest.
466
     *
467
     * @param applicationInfo The {@link ApplicationInfo} for the package.
468
     * @return Returns {@code true} if app has requested legacy external storage, otherwise
469
     * {@code false}. This will be {@code null} if an exception is raised.
470
     */
471
    @Nullable
472
    public static Boolean hasRequestedLegacyExternalStorage(@NonNull final ApplicationInfo applicationInfo) {
473
        return isApplicationInfoPrivateFlagSetForPackage("PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE", applicationInfo);
474
    }
475

476

477

478
    /**
479
     * Get the {@code versionCode} for the package associated with the {@code context}.
480
     *
481
     * @param context The {@link Context} for the package.
482
     * @return Returns the {@code versionCode}. This will be {@code null} if an exception is raised.
483
     */
484
    @Nullable
485
    public static Integer getVersionCodeForPackage(@NonNull final Context context) {
486
        return getVersionCodeForPackage(context, context.getPackageName());
487
    }
488

489
    /**
490
     * Get the {@code versionCode} for the {@code packageName}.
491
     *
492
     * @param context The {@link Context} for operations.
493
     * @param packageName The package name of the package.
494
     * @return Returns the {@code versionCode}. This will be {@code null} if an exception is raised.
495
     */
496
    @Nullable
497
    public static Integer getVersionCodeForPackage(@NonNull final Context context, @NonNull final String packageName) {
498
        return getVersionCodeForPackage(getPackageInfoForPackage(context, packageName));
499
    }
500

501
    /**
502
     * Get the {@code versionCode} for the {@code packageName}.
503
     *
504
     * @param packageInfo The {@link PackageInfo} for the package.
505
     * @return Returns the {@code versionCode}. This will be {@code null} if an exception is raised.
506
     */
507
    @Nullable
508
    public static Integer getVersionCodeForPackage(@Nullable final PackageInfo packageInfo) {
509
        return packageInfo != null ? packageInfo.versionCode : null;
510
    }
511

512

513

514
    /**
515
     * Get the {@code versionName} for the package associated with the {@code context}.
516
     *
517
     * @param context The {@link Context} for the package.
518
     * @return Returns the {@code versionName}. This will be {@code null} if an exception is raised.
519
     */
520
    @Nullable
521
    public static String getVersionNameForPackage(@NonNull final Context context) {
522
        return getVersionNameForPackage(context, context.getPackageName());
523
    }
524

525
    /**
526
     * Get the {@code versionName} for the {@code packageName}.
527
     *
528
     * @param context The {@link Context} for operations.
529
     * @param packageName The package name of the package.
530
     * @return Returns the {@code versionName}. This will be {@code null} if an exception is raised.
531
     */
532
    @Nullable
533
    public static String getVersionNameForPackage(@NonNull final Context context, @NonNull final String packageName) {
534
        return getVersionNameForPackage(getPackageInfoForPackage(context, packageName));
535
    }
536

537
    /**
538
     * Get the {@code versionName} for the {@code packageName}.
539
     *
540
     * @param packageInfo The {@link PackageInfo} for the package.
541
     * @return Returns the {@code versionName}. This will be {@code null} if an {@code packageInfo}
542
     * is {@code null}.
543
     */
544
    @Nullable
545
    public static String getVersionNameForPackage(@Nullable final PackageInfo packageInfo) {
546
        return packageInfo != null ? packageInfo.versionName : null;
547
    }
548

549

550

551
    /**
552
     * Get the {@code SHA-256 digest} of signing certificate for the package associated with the {@code context}.
553
     *
554
     * @param context The {@link Context} for the package.
555
     * @return Returns the {@code SHA-256 digest}. This will be {@code null} if an exception is raised.
556
     */
557
    @Nullable
558
    public static String getSigningCertificateSHA256DigestForPackage(@NonNull final Context context) {
559
        return getSigningCertificateSHA256DigestForPackage(context, context.getPackageName());
560
    }
561

562
    /**
563
     * Get the {@code SHA-256 digest} of signing certificate for the {@code packageName}.
564
     *
565
     * @param context The {@link Context} for operations.
566
     * @param packageName The package name of the package.
567
     * @return Returns the {@code SHA-256 digest}. This will be {@code null} if an exception is raised.
568
     */
569
    @Nullable
570
    public static String getSigningCertificateSHA256DigestForPackage(@NonNull final Context context, @NonNull final String packageName) {
571
        try {
572
            /*
573
             * Todo: We may need AndroidManifest queries entries if package is installed but with a different signature on android 11
574
             * https://developer.android.com/training/package-visibility
575
             * Need a device that allows (manual) installation of apk with mismatched signature of
576
             * sharedUserId apps to test. Currently, if its done, PackageManager just doesn't load
577
             * the package and removes its apk automatically if its installed as a user app instead of system app
578
             * W/PackageManager: Failed to parse /path/to/com.termux.tasker.apk: Signature mismatch for shared user: SharedUserSetting{xxxxxxx com.termux/10xxx}
579
             */
580
            PackageInfo packageInfo = getPackageInfoForPackage(context, packageName, PackageManager.GET_SIGNATURES);
581
            if (packageInfo == null) return null;
582
            return DataUtils.bytesToHex(MessageDigest.getInstance("SHA-256").digest(packageInfo.signatures[0].toByteArray()));
583
        } catch (final Exception e) {
584
            return null;
585
        }
586
    }
587

588

589

590
    /**
591
     * Get the serial number for the user for the package associated with the {@code context}.
592
     *
593
     * @param context The {@link Context} for the package.
594
     * @return Returns the serial number. This will be {@code null} if failed to get it.
595
     */
596
    @RequiresApi(api = Build.VERSION_CODES.N)
597
    @Nullable
598
    public static Long getUserIdForPackage(@NonNull Context context) {
599
        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
600
        if (userManager == null) return null;
601
        return userManager.getSerialNumberForUser(UserHandle.getUserHandleForUid(getUidForPackage(context)));
602
    }
603

604
    /**
605
     * Check if the current user is the primary user. This is done by checking if the the serial
606
     * number for the current user equals 0.
607
     *
608
     * @param context The {@link Context} for operations.
609
     * @return Returns {@code true} if the current user is the primary user, otherwise [@code false}.
610
     */
611
    @RequiresApi(api = Build.VERSION_CODES.N)
612
    public static boolean isCurrentUserThePrimaryUser(@NonNull Context context) {
613
        Long userId = getUserIdForPackage(context);
614
        return userId != null && userId == 0;
615
    }
616

617
    /**
618
     * Get the profile owner package name for the current user.
619
     *
620
     * @param context The {@link Context} for operations.
621
     * @return Returns the profile owner package name. This will be {@code null} if failed to get it
622
     * or no profile owner for the current user.
623
     */
624
    @Nullable
625
    public static String getProfileOwnerPackageNameForUser(@NonNull Context context) {
626
        DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
627
        if (devicePolicyManager == null) return null;
628
        List<ComponentName> activeAdmins = devicePolicyManager.getActiveAdmins();
629
        if (activeAdmins != null){
630
            for (ComponentName admin:activeAdmins){
631
                String packageName = admin.getPackageName();
632
                if(devicePolicyManager.isProfileOwnerApp(packageName))
633
                    return packageName;
634
            }
635
        }
636
        return null;
637
    }
638

639

640

641
    /**
642
     * Get the process id of the main app process of a package. This will work for sharedUserId. Note
643
     * that some apps have multiple processes for the app like with `android:process=":background"`
644
     * attribute in AndroidManifest.xml.
645
     *
646
     * @param context The {@link Context} for operations.
647
     * @param packageName The package name of the process.
648
     * @return Returns the process if found and running, otherwise {@code null}.
649
     */
650
    @Nullable
651
    public static String getPackagePID(final Context context, String packageName) {
652
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
653
        if (activityManager != null) {
654
            List<ActivityManager.RunningAppProcessInfo> processInfos = activityManager.getRunningAppProcesses();
655
            if (processInfos != null) {
656
                ActivityManager.RunningAppProcessInfo processInfo;
657
                for (int i = 0; i < processInfos.size(); i++) {
658
                    processInfo = processInfos.get(i);
659
                    if (processInfo.processName.equals(packageName))
660
                        return String.valueOf(processInfo.pid);
661
                }
662
            }
663
        }
664
        return null;
665
    }
666

667

668

669
    /**
670
     * Check if app is installed and enabled. This can be used by external apps that don't
671
     * share `sharedUserId` with the an app.
672
     *
673
     * If your third-party app is targeting sdk `30` (android `11`), then it needs to add package
674
     * name to the `queries` element or request `QUERY_ALL_PACKAGES` permission in its
675
     * `AndroidManifest.xml`. Otherwise it will get `PackageSetting{...... package_name/......} BLOCKED`
676
     * errors in `logcat` and  {@link PackageManager.NameNotFoundException} may be thrown.
677
     * `RUN_COMMAND` intent won't work either.
678
     * Check [package-visibility](https://developer.android.com/training/basics/intents/package-visibility#package-name),
679
     * `QUERY_ALL_PACKAGES` [googleplay policy](https://support.google.com/googleplay/android-developer/answer/10158779
680
     * and this [article](https://medium.com/androiddevelopers/working-with-package-visibility-dc252829de2d) for more info.
681
     *
682
     * {@code
683
     * <manifest
684
     *     <queries>
685
     *         <package android:name="com.termux" />
686
     *    </queries>
687
     *
688
     *    <application
689
     *        ....
690
     *    </application>
691
     * </manifest>
692
     * }
693
     *
694
     * @param context The context for operations.
695
     * @param appName The name of the app.
696
     * @param packageName The package name of the package.
697
     * @return Returns {@code errmsg} if {@code packageName} is not installed or disabled, otherwise {@code null}.
698
     */
699
    public static String isAppInstalled(@NonNull final Context context, String appName, String packageName) {
700
        String errmsg = null;
701

702
        ApplicationInfo applicationInfo = getApplicationInfoForPackage(context, packageName);
703
        boolean isAppEnabled = (applicationInfo != null && applicationInfo.enabled);
704

705
        // If app is not installed or is disabled
706
        if (!isAppEnabled)
707
            errmsg = context.getString(R.string.error_app_not_installed_or_disabled_warning, appName, packageName);
708

709
        return errmsg;
710
    }
711

712

713
    /** Wrapper for {@link #setComponentState(Context, String, String, boolean, String, boolean, boolean)} with
714
     * {@code alwaysShowToast} {@code true}. */
715
    public static String setComponentState(@NonNull final Context context, @NonNull String packageName,
716
                                           @NonNull String className, boolean newState, String toastString,
717
                                           boolean showErrorMessage) {
718
        return setComponentState(context, packageName, className, newState, toastString, showErrorMessage, true);
719
    }
720

721
    /**
722
     * Enable or disable a {@link ComponentName} with a call to
723
     * {@link PackageManager#setComponentEnabledSetting(ComponentName, int, int)}.
724
     *
725
     * @param context The {@link Context} for operations.
726
     * @param packageName The package name of the component.
727
     * @param className The {@link Class} name of the component.
728
     * @param newState If component should be enabled or disabled.
729
     * @param toastString If this is not {@code null} or empty, then a toast before setting state.
730
     * @param showErrorMessage If an error message toast should be shown.
731
     * @param alwaysShowToast If toast should always be shown even if current state matches new state.
732
     * @return Returns the errmsg if failed to set state, otherwise {@code null}.
733
     */
734
    @Nullable
735
    public static String setComponentState(@NonNull final Context context, @NonNull String packageName,
736
                                           @NonNull String className, boolean newState, String toastString,
737
                                           boolean alwaysShowToast, boolean showErrorMessage) {
738
        try {
739
            PackageManager packageManager = context.getPackageManager();
740
            if (packageManager != null) {
741
                if (toastString != null && alwaysShowToast) {
742
                    Logger.showToast(context, toastString, true);
743
                    toastString = null;
744
                }
745

746
                Boolean currentlyDisabled = PackageUtils.isComponentDisabled(context, packageName, className, false);
747
                if (currentlyDisabled == null)
748
                    throw new UnsupportedOperationException("Failed to find if component currently disabled");
749

750
                Boolean setState = null;
751
                if (newState && currentlyDisabled)
752
                    setState = true;
753
                else if (!newState && !currentlyDisabled)
754
                    setState = false;
755

756
                if (setState == null) return null;
757

758
                if (toastString != null) Logger.showToast(context, toastString, true);
759
                ComponentName componentName = new ComponentName(packageName, className);
760
                packageManager.setComponentEnabledSetting(componentName,
761
                    setState ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
762
                    PackageManager.DONT_KILL_APP);
763
            }
764
            return null;
765
        } catch (final Exception e) {
766
            String errmsg = context.getString(
767
                newState ? R.string.error_enable_component_failed : R.string.error_disable_component_failed,
768
                packageName, className) + ": " + e.getMessage();
769
            if (showErrorMessage)
770
                Logger.showToast(context, errmsg, true);
771
            return errmsg;
772
        }
773
    }
774

775
    /**
776
     * Check if state of a {@link ComponentName} is {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED}
777
     * with a call to {@link PackageManager#getComponentEnabledSetting(ComponentName)}.
778
     *
779
     * @param context The {@link Context} for operations.
780
     * @param packageName The package name of the component.
781
     * @param className The {@link Class} name of the component.
782
     * @param logErrorMessage If an error message should be logged.
783
     * @return Returns {@code true} if disabled, {@code false} if not and {@code null} if failed to
784
     * get the state.
785
     */
786
    public static Boolean isComponentDisabled(@NonNull final Context context, @NonNull String packageName,
787
                                              @NonNull String className, boolean logErrorMessage) {
788
        try {
789
            PackageManager packageManager = context.getPackageManager();
790
            if (packageManager != null) {
791
                ComponentName componentName = new ComponentName(packageName, className);
792
                // Will throw IllegalArgumentException: Unknown component: ComponentInfo{} if app
793
                // for context is not installed or component does not exist.
794
                return packageManager.getComponentEnabledSetting(componentName) == PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
795
            }
796
        } catch (final Exception e) {
797
            if (logErrorMessage)
798
                Logger.logStackTraceWithMessage(LOG_TAG, context.getString(R.string.error_get_component_state_failed, packageName, className), e);
799
        }
800

801
        return null;
802
    }
803

804
    /**
805
     * Check if an {@link android.app.Activity} {@link ComponentName} can be called by calling
806
     * {@link PackageManager#queryIntentActivities(Intent, int)}.
807
     *
808
     * @param context The {@link Context} for operations.
809
     * @param packageName The package name of the component.
810
     * @param className The {@link Class} name of the component.
811
     * @param flags The flags to filter results.
812
     * @return Returns {@code true} if it exists, otherwise {@code false}.
813
     */
814
    public static boolean doesActivityComponentExist(@NonNull final Context context, @NonNull String packageName,
815
                                                     @NonNull String className, int flags) {
816
        try {
817
            PackageManager packageManager = context.getPackageManager();
818
            if (packageManager != null) {
819
                Intent intent = new Intent();
820
                intent.setClassName(packageName, className);
821
                return packageManager.queryIntentActivities(intent, flags).size() > 0;
822
            }
823
        } catch (final Exception e) {
824
            // ignore
825
        }
826

827
        return false;
828
    }
829

830
}
831

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.