termux-app
830 строк · 36.3 Кб
1package com.termux.shared.android;
2
3import android.app.ActivityManager;
4import android.app.admin.DevicePolicyManager;
5import android.content.ComponentName;
6import android.content.Context;
7import android.content.Intent;
8import android.content.pm.ApplicationInfo;
9import android.content.pm.PackageInfo;
10import android.content.pm.PackageManager;
11import android.os.Build;
12import android.os.UserHandle;
13import android.os.UserManager;
14
15import androidx.annotation.NonNull;
16import androidx.annotation.Nullable;
17import androidx.annotation.RequiresApi;
18
19import com.termux.shared.R;
20import com.termux.shared.data.DataUtils;
21import com.termux.shared.interact.MessageDialogUtils;
22import com.termux.shared.logger.Logger;
23import com.termux.shared.reflection.ReflectionUtils;
24
25import java.lang.reflect.Field;
26import java.security.MessageDigest;
27import java.util.List;
28
29public class PackageUtils {
30
31private 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
41public static Context getContextForPackage(@NonNull final Context context, String packageName) {
42return 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
54public static Context getContextForPackage(@NonNull final Context context, String packageName, int flags) {
55try {
56return context.createPackageContext(packageName, flags);
57} catch (Exception e) {
58Logger.logVerbose(LOG_TAG, "Failed to get \"" + packageName + "\" package context with flags " + flags + ": " + e.getMessage());
59return 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
74public static Context getContextForPackageOrExitApp(@NonNull Context context, String packageName,
75final boolean exitAppOnError, @Nullable String helpUrl) {
76Context packageContext = getContextForPackage(context, packageName);
77
78if (packageContext == null && exitAppOnError) {
79String errorMessage = context.getString(R.string.error_get_package_context_failed_message,
80packageName);
81if (!DataUtils.isNullOrEmpty(helpUrl))
82errorMessage += "\n" + context.getString(R.string.error_get_package_context_failed_help_url_message, helpUrl);
83Logger.logError(LOG_TAG, errorMessage);
84MessageDialogUtils.exitAppWithErrorMessage(context,
85context.getString(R.string.error_get_package_context_failed_title),
86errorMessage);
87}
88
89return 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*/
100public static PackageInfo getPackageInfoForPackage(@NonNull final Context context) {
101return 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
112public static PackageInfo getPackageInfoForPackage(@NonNull final Context context, final int flags) {
113return 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*/
123public static PackageInfo getPackageInfoForPackage(@NonNull final Context context, @NonNull final String packageName) {
124return 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
139public static PackageInfo getPackageInfoForPackage(@NonNull final Context context, @NonNull final String packageName, final int flags) {
140try {
141return context.getPackageManager().getPackageInfo(packageName, flags);
142} catch (final Exception e) {
143return 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
157public static ApplicationInfo getApplicationInfoForPackage(@NonNull final Context context, @NonNull final String packageName) {
158return 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
173public static ApplicationInfo getApplicationInfoForPackage(@NonNull final Context context, @NonNull final String packageName, final int flags) {
174try {
175return context.getPackageManager().getApplicationInfo(packageName, flags);
176} catch (final Exception e) {
177return 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
188public static Integer getApplicationInfoPrivateFlagsForPackage(@NonNull final ApplicationInfo applicationInfo) {
189ReflectionUtils.bypassHiddenAPIReflectionRestrictions();
190try {
191return (Integer) ReflectionUtils.invokeField(ApplicationInfo.class, "privateFlags", applicationInfo).value;
192} catch (Exception e) {
193// ClassCastException may be thrown
194Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get privateFlags field value for ApplicationInfo class", e);
195return 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
216public static String getApplicationInfoSeInfoForPackage(@NonNull final ApplicationInfo applicationInfo) {
217ReflectionUtils.bypassHiddenAPIReflectionRestrictions();
218try {
219return (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
222Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get seInfo field value for ApplicationInfo class", e);
223return 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
236public static String getApplicationInfoSeInfoUserForPackage(@NonNull final ApplicationInfo applicationInfo) {
237if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return null;
238ReflectionUtils.bypassHiddenAPIReflectionRestrictions();
239try {
240return (String) ReflectionUtils.invokeField(ApplicationInfo.class, "seInfoUser", applicationInfo).value;
241} catch (Exception e) {
242// ClassCastException may be thrown
243Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get seInfoUser field value for ApplicationInfo class", e);
244return 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
255public static Integer getApplicationInfoStaticIntFieldValue(@NonNull String fieldName) {
256ReflectionUtils.bypassHiddenAPIReflectionRestrictions();
257try {
258return (Integer) ReflectionUtils.invokeField(ApplicationInfo.class, fieldName, null).value;
259} catch (Exception e) {
260// ClassCastException may be thrown
261Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get \"" + fieldName + "\" field value for ApplicationInfo class", e);
262return 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
275public static Boolean isApplicationInfoPrivateFlagSetForPackage(@NonNull String flagToCheckName, @NonNull final ApplicationInfo applicationInfo) {
276Integer privateFlags = getApplicationInfoPrivateFlagsForPackage(applicationInfo);
277if (privateFlags == null) return null;
278
279Integer flagToCheck = getApplicationInfoStaticIntFieldValue(flagToCheckName);
280if (flagToCheck == null) return null;
281
282return ( 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*/
295public static String getAppNameForPackage(@NonNull final Context context) {
296return 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*/
306public static String getAppNameForPackage(@NonNull final Context context, @NonNull final ApplicationInfo applicationInfo) {
307return 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*/
318public static String getPackageNameForPackage(@NonNull final Context context) {
319return 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*/
328public static String getPackageNameForPackage(@NonNull final ApplicationInfo applicationInfo) {
329return 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*/
340public static int getUidForPackage(@NonNull final Context context) {
341return 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*/
350public static int getUidForPackage(@NonNull final ApplicationInfo applicationInfo) {
351return 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*/
362public static int getTargetSDKForPackage(@NonNull final Context context) {
363return 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*/
372public static int getTargetSDKForPackage(@NonNull final ApplicationInfo applicationInfo) {
373return 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*/
384public static String getBaseAPKPathForPackage(@NonNull final Context context) {
385return 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*/
394public static String getBaseAPKPathForPackage(@NonNull final ApplicationInfo applicationInfo) {
395return 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*/
407public static boolean isAppForPackageADebuggableBuild(@NonNull final Context context) {
408return 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*/
418public static boolean isAppForPackageADebuggableBuild(@NonNull final ApplicationInfo applicationInfo) {
419return ( 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*/
431public static boolean isAppInstalledOnExternalStorage(@NonNull final Context context) {
432return 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*/
442public static boolean isAppInstalledOnExternalStorage(@NonNull final ApplicationInfo applicationInfo) {
443return ( 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
458public static Boolean hasRequestedLegacyExternalStorage(@NonNull final Context context) {
459return 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
472public static Boolean hasRequestedLegacyExternalStorage(@NonNull final ApplicationInfo applicationInfo) {
473return 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
485public static Integer getVersionCodeForPackage(@NonNull final Context context) {
486return 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
497public static Integer getVersionCodeForPackage(@NonNull final Context context, @NonNull final String packageName) {
498return 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
508public static Integer getVersionCodeForPackage(@Nullable final PackageInfo packageInfo) {
509return 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
521public static String getVersionNameForPackage(@NonNull final Context context) {
522return 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
533public static String getVersionNameForPackage(@NonNull final Context context, @NonNull final String packageName) {
534return 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
545public static String getVersionNameForPackage(@Nullable final PackageInfo packageInfo) {
546return 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
558public static String getSigningCertificateSHA256DigestForPackage(@NonNull final Context context) {
559return 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
570public static String getSigningCertificateSHA256DigestForPackage(@NonNull final Context context, @NonNull final String packageName) {
571try {
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*/
580PackageInfo packageInfo = getPackageInfoForPackage(context, packageName, PackageManager.GET_SIGNATURES);
581if (packageInfo == null) return null;
582return DataUtils.bytesToHex(MessageDigest.getInstance("SHA-256").digest(packageInfo.signatures[0].toByteArray()));
583} catch (final Exception e) {
584return 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
598public static Long getUserIdForPackage(@NonNull Context context) {
599UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
600if (userManager == null) return null;
601return 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)
612public static boolean isCurrentUserThePrimaryUser(@NonNull Context context) {
613Long userId = getUserIdForPackage(context);
614return 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
625public static String getProfileOwnerPackageNameForUser(@NonNull Context context) {
626DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
627if (devicePolicyManager == null) return null;
628List<ComponentName> activeAdmins = devicePolicyManager.getActiveAdmins();
629if (activeAdmins != null){
630for (ComponentName admin:activeAdmins){
631String packageName = admin.getPackageName();
632if(devicePolicyManager.isProfileOwnerApp(packageName))
633return packageName;
634}
635}
636return 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
651public static String getPackagePID(final Context context, String packageName) {
652ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
653if (activityManager != null) {
654List<ActivityManager.RunningAppProcessInfo> processInfos = activityManager.getRunningAppProcesses();
655if (processInfos != null) {
656ActivityManager.RunningAppProcessInfo processInfo;
657for (int i = 0; i < processInfos.size(); i++) {
658processInfo = processInfos.get(i);
659if (processInfo.processName.equals(packageName))
660return String.valueOf(processInfo.pid);
661}
662}
663}
664return 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*/
699public static String isAppInstalled(@NonNull final Context context, String appName, String packageName) {
700String errmsg = null;
701
702ApplicationInfo applicationInfo = getApplicationInfoForPackage(context, packageName);
703boolean isAppEnabled = (applicationInfo != null && applicationInfo.enabled);
704
705// If app is not installed or is disabled
706if (!isAppEnabled)
707errmsg = context.getString(R.string.error_app_not_installed_or_disabled_warning, appName, packageName);
708
709return errmsg;
710}
711
712
713/** Wrapper for {@link #setComponentState(Context, String, String, boolean, String, boolean, boolean)} with
714* {@code alwaysShowToast} {@code true}. */
715public static String setComponentState(@NonNull final Context context, @NonNull String packageName,
716@NonNull String className, boolean newState, String toastString,
717boolean showErrorMessage) {
718return 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
735public static String setComponentState(@NonNull final Context context, @NonNull String packageName,
736@NonNull String className, boolean newState, String toastString,
737boolean alwaysShowToast, boolean showErrorMessage) {
738try {
739PackageManager packageManager = context.getPackageManager();
740if (packageManager != null) {
741if (toastString != null && alwaysShowToast) {
742Logger.showToast(context, toastString, true);
743toastString = null;
744}
745
746Boolean currentlyDisabled = PackageUtils.isComponentDisabled(context, packageName, className, false);
747if (currentlyDisabled == null)
748throw new UnsupportedOperationException("Failed to find if component currently disabled");
749
750Boolean setState = null;
751if (newState && currentlyDisabled)
752setState = true;
753else if (!newState && !currentlyDisabled)
754setState = false;
755
756if (setState == null) return null;
757
758if (toastString != null) Logger.showToast(context, toastString, true);
759ComponentName componentName = new ComponentName(packageName, className);
760packageManager.setComponentEnabledSetting(componentName,
761setState ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
762PackageManager.DONT_KILL_APP);
763}
764return null;
765} catch (final Exception e) {
766String errmsg = context.getString(
767newState ? R.string.error_enable_component_failed : R.string.error_disable_component_failed,
768packageName, className) + ": " + e.getMessage();
769if (showErrorMessage)
770Logger.showToast(context, errmsg, true);
771return 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*/
786public static Boolean isComponentDisabled(@NonNull final Context context, @NonNull String packageName,
787@NonNull String className, boolean logErrorMessage) {
788try {
789PackageManager packageManager = context.getPackageManager();
790if (packageManager != null) {
791ComponentName 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.
794return packageManager.getComponentEnabledSetting(componentName) == PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
795}
796} catch (final Exception e) {
797if (logErrorMessage)
798Logger.logStackTraceWithMessage(LOG_TAG, context.getString(R.string.error_get_component_state_failed, packageName, className), e);
799}
800
801return 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*/
814public static boolean doesActivityComponentExist(@NonNull final Context context, @NonNull String packageName,
815@NonNull String className, int flags) {
816try {
817PackageManager packageManager = context.getPackageManager();
818if (packageManager != null) {
819Intent intent = new Intent();
820intent.setClassName(packageName, className);
821return packageManager.queryIntentActivities(intent, flags).size() > 0;
822}
823} catch (final Exception e) {
824// ignore
825}
826
827return false;
828}
829
830}
831