diff --git a/termux-shared/src/main/java/com/termux/shared/packages/PackageUtils.java b/termux-shared/src/main/java/com/termux/shared/packages/PackageUtils.java index 60997b27..de1aae98 100644 --- a/termux-shared/src/main/java/com/termux/shared/packages/PackageUtils.java +++ b/termux-shared/src/main/java/com/termux/shared/packages/PackageUtils.java @@ -68,6 +68,8 @@ public class PackageUtils { return packageContext; } + + /** * Get the {@link PackageInfo} for the package associated with the {@code context}. * @@ -75,7 +77,7 @@ public class PackageUtils { * @return Returns the {@link PackageInfo}. This will be {@code null} if an exception is raised. */ public static PackageInfo getPackageInfoForPackage(@NonNull final Context context) { - return getPackageInfoForPackage(context, 0); + return getPackageInfoForPackage(context, context.getPackageName()); } /** @@ -87,13 +89,76 @@ public class PackageUtils { */ @Nullable public static PackageInfo getPackageInfoForPackage(@NonNull final Context context, final int flags) { + return getPackageInfoForPackage(context, context.getPackageName(), flags); + } + + /** + * Get the {@link PackageInfo} for the package associated with the {@code packageName}. + * + * @param context The {@link Context} for operations. + * @param packageName The package name of the package. + * @return Returns the {@link PackageInfo}. This will be {@code null} if an exception is raised. + */ + public static PackageInfo getPackageInfoForPackage(@NonNull final Context context, @NonNull final String packageName) { + return getPackageInfoForPackage(context, packageName, 0); + } + + /** + * Get the {@link PackageInfo} for the package associated with the {@code packageName}. + * + * Also check {@link #isAppInstalled(Context, String, String) if targetting targeting sdk + * `30` (android `11`) since {@link PackageManager.NameNotFoundException} may be thrown. + * + * @param context The {@link Context} for operations. + * @param packageName The package name of the package. + * @param flags The flags to pass to {@link PackageManager#getPackageInfo(String, int)}. + * @return Returns the {@link PackageInfo}. This will be {@code null} if an exception is raised. + */ + @Nullable + public static PackageInfo getPackageInfoForPackage(@NonNull final Context context, @NonNull final String packageName, final int flags) { try { - return context.getPackageManager().getPackageInfo(context.getPackageName(), flags); + return context.getPackageManager().getPackageInfo(packageName, flags); } catch (final Exception e) { return null; } } + + + /** + * Get the {@link ApplicationInfo} for the {@code packageName}. + * + * @param context The {@link Context} for operations. + * @param packageName The package name of the package. + * @return Returns the {@link ApplicationInfo}. This will be {@code null} if an exception is raised. + */ + @Nullable + public static ApplicationInfo getApplicationInfoForPackage(@NonNull final Context context, @NonNull final String packageName) { + return getApplicationInfoForPackage(context, packageName, 0); + } + + /** + * Get the {@link ApplicationInfo} for the {@code packageName}. + * + * Also check {@link #isAppInstalled(Context, String, String) if targetting targeting sdk + * `30` (android `11`) since {@link PackageManager.NameNotFoundException} may be thrown. + * + * @param context The {@link Context} for operations. + * @param packageName The package name of the package. + * @param flags The flags to pass to {@link PackageManager#getApplicationInfo(String, int)}. + * @return Returns the {@link ApplicationInfo}. This will be {@code null} if an exception is raised. + */ + @Nullable + public static ApplicationInfo getApplicationInfoForPackage(@NonNull final Context context, @NonNull final String packageName, final int flags) { + try { + return context.getPackageManager().getApplicationInfo(packageName, flags); + } catch (final Exception e) { + return null; + } + } + + + /** * Get the app name for the package associated with the {@code context}. * @@ -101,9 +166,22 @@ public class PackageUtils { * @return Returns the {@code android:name} attribute. */ public static String getAppNameForPackage(@NonNull final Context context) { - return context.getApplicationInfo().loadLabel(context.getPackageManager()).toString(); + return getAppNameForPackage(context, context.getApplicationInfo()); } + /** + * Get the app name for the package associated with the {@code applicationInfo}. + * + * @param context The {@link Context} for operations. + * @param applicationInfo The {@link ApplicationInfo} for the package. + * @return Returns the {@code android:name} attribute. + */ + public static String getAppNameForPackage(@NonNull final Context context, @NonNull final ApplicationInfo applicationInfo) { + return applicationInfo.loadLabel(context.getPackageManager()).toString(); + } + + + /** * Get the package name for the package associated with the {@code context}. * @@ -111,9 +189,21 @@ public class PackageUtils { * @return Returns the package name. */ public static String getPackageNameForPackage(@NonNull final Context context) { - return context.getApplicationInfo().packageName; + return getPackageNameForPackage(context.getApplicationInfo()); } + /** + * Get the package name for the package associated with the {@code applicationInfo}. + * + * @param applicationInfo The {@link ApplicationInfo} for the package. + * @return Returns the package name. + */ + public static String getPackageNameForPackage(@NonNull final ApplicationInfo applicationInfo) { + return applicationInfo.packageName; + } + + + /** * Get the {@code targetSdkVersion} for the package associated with the {@code context}. * @@ -121,9 +211,21 @@ public class PackageUtils { * @return Returns the {@code targetSdkVersion}. */ public static int getTargetSDKForPackage(@NonNull final Context context) { - return context.getApplicationInfo().targetSdkVersion; + return getTargetSDKForPackage(context.getApplicationInfo()); } + /** + * Get the {@code targetSdkVersion} for the package associated with the {@code applicationInfo}. + * + * @param applicationInfo The {@link ApplicationInfo} for the package. + * @return Returns the {@code targetSdkVersion}. + */ + public static int getTargetSDKForPackage(@NonNull final ApplicationInfo applicationInfo) { + return applicationInfo.targetSdkVersion; + } + + + /** * Check if the app associated with the {@code context} has {@link ApplicationInfo#FLAG_DEBUGGABLE} * set. @@ -132,9 +234,22 @@ public class PackageUtils { * @return Returns the {@code versionName}. This will be {@code null} if an exception is raised. */ public static Boolean isAppForPackageADebuggableBuild(@NonNull final Context context) { - return ( 0 != ( context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE ) ); + return isAppForPackageADebuggableBuild(context.getApplicationInfo()); } + /** + * Check if the app associated with the {@code applicationInfo} has {@link ApplicationInfo#FLAG_DEBUGGABLE} + * set. + * + * @param applicationInfo The {@link ApplicationInfo} for the package. + * @return Returns the {@code versionName}. This will be {@code null} if an exception is raised. + */ + public static Boolean isAppForPackageADebuggableBuild(@NonNull final ApplicationInfo applicationInfo) { + return ( 0 != ( applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE ) ); + } + + + /** * Check if the app associated with the {@code context} has {@link ApplicationInfo#FLAG_EXTERNAL_STORAGE} * set. @@ -143,9 +258,22 @@ public class PackageUtils { * @return Returns the {@code versionName}. This will be {@code null} if an exception is raised. */ public static Boolean isAppInstalledOnExternalStorage(@NonNull final Context context) { - return ( 0 != ( context.getApplicationInfo().flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE ) ); + return isAppInstalledOnExternalStorage(context.getApplicationInfo()); } + /** + * Check if the app associated with the {@code applicationInfo} has {@link ApplicationInfo#FLAG_EXTERNAL_STORAGE} + * set. + * + * @param applicationInfo The {@link ApplicationInfo} for the package. + * @return Returns the {@code versionName}. This will be {@code null} if an exception is raised. + */ + public static Boolean isAppInstalledOnExternalStorage(@NonNull final ApplicationInfo applicationInfo) { + return ( 0 != ( applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE ) ); + } + + + /** * Get the {@code versionCode} for the package associated with the {@code context}. * @@ -154,13 +282,27 @@ public class PackageUtils { */ @Nullable public static Integer getVersionCodeForPackage(@NonNull final Context context) { + return getVersionCodeForPackage(context, context.getPackageName()); + } + + /** + * Get the {@code versionCode} for the {@code packageName}. + * + * @param context The {@link Context} for operations. + * @param packageName The package name of the package. + * @return Returns the {@code versionCode}. This will be {@code null} if an exception is raised. + */ + @Nullable + public static Integer getVersionCodeForPackage(@NonNull final Context context, @NonNull final String packageName) { try { - return getPackageInfoForPackage(context).versionCode; + return getPackageInfoForPackage(context, packageName).versionCode; } catch (final Exception e) { return null; } } + + /** * Get the {@code versionName} for the package associated with the {@code context}. * @@ -169,13 +311,26 @@ public class PackageUtils { */ @Nullable public static String getVersionNameForPackage(@NonNull final Context context) { + return getVersionNameForPackage(context, context.getPackageName()); + } + /** + * Get the {@code versionName} for the {@code packageName}. + * + * @param context The {@link Context} for operations. + * @param packageName The package name of the package. + * @return Returns the {@code versionName}. This will be {@code null} if an exception is raised. + */ + @Nullable + public static String getVersionNameForPackage(@NonNull final Context context, @NonNull final String packageName) { try { - return getPackageInfoForPackage(context).versionName; + return getPackageInfoForPackage(context, packageName).versionName; } catch (final Exception e) { return null; } } + + /** * Get the {@code SHA-256 digest} of signing certificate for the package associated with the {@code context}. * @@ -184,16 +339,28 @@ public class PackageUtils { */ @Nullable public static String getSigningCertificateSHA256DigestForPackage(@NonNull final Context context) { + return getSigningCertificateSHA256DigestForPackage(context, context.getPackageName()); + } + + /** + * Get the {@code SHA-256 digest} of signing certificate for the {@code packageName}. + * + * @param context The {@link Context} for operations. + * @param packageName The package name of the package. + * @return Returns the {@code SHA-256 digest}. This will be {@code null} if an exception is raised. + */ + @Nullable + public static String getSigningCertificateSHA256DigestForPackage(@NonNull final Context context, @NonNull final String packageName) { try { /* - * Todo: We may need AndroidManifest queries entries if package is installed but with a different signature on android 11 - * https://developer.android.com/training/package-visibility - * Need a device that allows (manual) installation of apk with mismatched signature of - * sharedUserId apps to test. Currently, if its done, PackageManager just doesn't load - * the package and removes its apk automatically if its installed as a user app instead of system app - * W/PackageManager: Failed to parse /path/to/com.termux.tasker.apk: Signature mismatch for shared user: SharedUserSetting{xxxxxxx com.termux/10xxx} - */ - PackageInfo packageInfo = getPackageInfoForPackage(context, PackageManager.GET_SIGNATURES); + * Todo: We may need AndroidManifest queries entries if package is installed but with a different signature on android 11 + * https://developer.android.com/training/package-visibility + * Need a device that allows (manual) installation of apk with mismatched signature of + * sharedUserId apps to test. Currently, if its done, PackageManager just doesn't load + * the package and removes its apk automatically if its installed as a user app instead of system app + * W/PackageManager: Failed to parse /path/to/com.termux.tasker.apk: Signature mismatch for shared user: SharedUserSetting{xxxxxxx com.termux/10xxx} + */ + PackageInfo packageInfo = getPackageInfoForPackage(context, packageName, PackageManager.GET_SIGNATURES); if (packageInfo == null) return null; return DataUtils.bytesToHex(MessageDigest.getInstance("SHA-256").digest(packageInfo.signatures[0].toByteArray())); } catch (final Exception e) { @@ -287,7 +454,8 @@ public class PackageUtils { * If your third-party app is targeting sdk `30` (android `11`), then it needs to add package * name to the `queries` element or request `QUERY_ALL_PACKAGES` permission in its * `AndroidManifest.xml`. Otherwise it will get `PackageSetting{...... package_name/......} BLOCKED` - * errors in `logcat` and `RUN_COMMAND` won't work. + * errors in `logcat` and {@link PackageManager.NameNotFoundException} may be thrown. + * `RUN_COMMAND` intent won't work either. * Check [package-visibility](https://developer.android.com/training/basics/intents/package-visibility#package-name), * `QUERY_ALL_PACKAGES` [googleplay policy](https://support.google.com/googleplay/android-developer/answer/10158779 * and this [article](https://medium.com/androiddevelopers/working-with-package-visibility-dc252829de2d) for more info. @@ -295,25 +463,24 @@ public class PackageUtils { * {@code * - * + * * + * + * * * } * * @param context The context for operations. + * @param appName The name of the app. + * @param packageName The package name of the package. * @return Returns {@code errmsg} if {@code packageName} is not installed or disabled, otherwise {@code null}. */ public static String isAppInstalled(@NonNull final Context context, String appName, String packageName) { String errmsg = null; - PackageManager packageManager = context.getPackageManager(); - - ApplicationInfo applicationInfo; - try { - applicationInfo = packageManager.getApplicationInfo(packageName, 0); - } catch (final PackageManager.NameNotFoundException e) { - applicationInfo = null; - } + ApplicationInfo applicationInfo = getApplicationInfoForPackage(context, packageName); boolean isAppEnabled = (applicationInfo != null && applicationInfo.enabled); // If app is not installed or is disabled diff --git a/termux-shared/src/main/java/com/termux/shared/termux/AndroidUtils.java b/termux-shared/src/main/java/com/termux/shared/termux/AndroidUtils.java index 2b1ac942..80e4f130 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/AndroidUtils.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/AndroidUtils.java @@ -2,6 +2,7 @@ package com.termux.shared.termux; import android.annotation.SuppressLint; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.os.Build; import androidx.annotation.NonNull; @@ -26,6 +27,9 @@ public class AndroidUtils { /** * Get a markdown {@link String} for the app info for the package associated with the {@code context}. + * This will contain additional info about the app in addition to the one returned by + * {@link #getAppInfoMarkdownString(Context, String)}, which will be got via the {@code context} + * object. * * @param context The context for operations for the package. * @return Returns the markdown {@link String}. @@ -33,21 +37,16 @@ public class AndroidUtils { public static String getAppInfoMarkdownString(@NonNull final Context context) { StringBuilder markdownString = new StringBuilder(); - AndroidUtils.appendPropertyToMarkdown(markdownString,"APP_NAME", PackageUtils.getAppNameForPackage(context)); - AndroidUtils.appendPropertyToMarkdown(markdownString,"PACKAGE_NAME", PackageUtils.getPackageNameForPackage(context)); - AndroidUtils.appendPropertyToMarkdown(markdownString,"VERSION_NAME", PackageUtils.getVersionNameForPackage(context)); - AndroidUtils.appendPropertyToMarkdown(markdownString,"VERSION_CODE", PackageUtils.getVersionCodeForPackage(context)); - AndroidUtils.appendPropertyToMarkdown(markdownString,"TARGET_SDK", PackageUtils.getTargetSDKForPackage(context)); - AndroidUtils.appendPropertyToMarkdown(markdownString,"IS_DEBUGGABLE_BUILD", PackageUtils.isAppForPackageADebuggableBuild(context)); - - if (PackageUtils.isAppInstalledOnExternalStorage(context)) { - AndroidUtils.appendPropertyToMarkdown(markdownString,"IS_INSTALLED_ON_EXTERNAL_STORAGE", true); - } + String appInfo = getAppInfoMarkdownString(context, context.getPackageName()); + if (appInfo == null) + return markdownString.toString(); + else + markdownString.append(appInfo); String filesDir = context.getFilesDir().getAbsolutePath(); if (!filesDir.equals("/data/user/0/" + context.getPackageName() + "/files") && !filesDir.equals("/data/data/" + context.getPackageName() + "/files")) - AndroidUtils.appendPropertyToMarkdown(markdownString,"FILES_DIR", filesDir); + AndroidUtils.appendPropertyToMarkdown(markdownString,"FILES_DIR", filesDir); Long userId = PackageUtils.getSerialNumberForCurrentUser(context); if (userId == null || userId != 0) @@ -58,6 +57,33 @@ public class AndroidUtils { return markdownString.toString(); } + /** + * Get a markdown {@link String} for the app info for the {@code packageName}. + * + * @param context The {@link Context} for operations. + * @param packageName The package name of the package. + * @return Returns the markdown {@link String}. + */ + public static String getAppInfoMarkdownString(@NonNull final Context context, @NonNull final String packageName) { + ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoForPackage(context, packageName); + if (applicationInfo == null) return null; + + StringBuilder markdownString = new StringBuilder(); + + AndroidUtils.appendPropertyToMarkdown(markdownString,"APP_NAME", PackageUtils.getAppNameForPackage(context, applicationInfo)); + AndroidUtils.appendPropertyToMarkdown(markdownString,"PACKAGE_NAME", PackageUtils.getPackageNameForPackage(applicationInfo)); + AndroidUtils.appendPropertyToMarkdown(markdownString,"VERSION_NAME", PackageUtils.getVersionNameForPackage(context, packageName)); + AndroidUtils.appendPropertyToMarkdown(markdownString,"VERSION_CODE", PackageUtils.getVersionCodeForPackage(context, packageName)); + AndroidUtils.appendPropertyToMarkdown(markdownString,"TARGET_SDK", PackageUtils.getTargetSDKForPackage(applicationInfo)); + AndroidUtils.appendPropertyToMarkdown(markdownString,"IS_DEBUGGABLE_BUILD", PackageUtils.isAppForPackageADebuggableBuild(applicationInfo)); + + if (PackageUtils.isAppInstalledOnExternalStorage(applicationInfo)) { + AndroidUtils.appendPropertyToMarkdown(markdownString,"IS_INSTALLED_ON_EXTERNAL_STORAGE", true); + } + + return markdownString.toString(); + } + /** * Get a markdown {@link String} for the device info. *