// Copyright 2019 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.base; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.TransactionTooLargeException; import java.util.Collections; import java.util.List; /** This class provides Android PackageManager related utility methods. */ public class PackageManagerUtils { private static final String TAG = "PackageManagerUtils"; // This is the intent Android uses internally to detect browser apps. // See // https://cs.android.com/android/_/android/platform/packages/modules/Permission/+/android12-release:PermissionController/src/com/android/permissioncontroller/role/model/BrowserRoleBehavior.java;drc=86fa7d5dfa43f66b170f93ade4f59b9a770be32f;l=50 public static final Intent BROWSER_INTENT = new Intent() .setAction(Intent.ACTION_VIEW) .addCategory(Intent.CATEGORY_BROWSABLE) .setData(Uri.fromParts("http", "", null)); /** * Retrieve information about the Activity that will handle the given Intent. * * Note: This function is expensive on KK and below and should not be called from main thread * when avoidable. * * @param intent Intent to resolve. * @param flags The PackageManager flags to pass to resolveActivity(). * @return ResolveInfo of the Activity that will handle the Intent, or null if it failed. */ public static ResolveInfo resolveActivity(Intent intent, int flags) { // On KitKat, calling PackageManager#resolveActivity() causes disk reads and // writes. Temporarily allow this while resolving the intent. try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) { PackageManager pm = ContextUtils.getApplicationContext().getPackageManager(); return pm.resolveActivity(intent, flags); } catch (RuntimeException e) { handleExpectedExceptionsOrRethrow(e, intent); } return null; } /** * Get the list of component name of activities which can resolve |intent|. If the request * fails, an empty list will be returned. * * See {@link PackageManager#queryIntentActivities(Intent, int)} */ public static List queryIntentActivities(Intent intent, int flags) { // Allowlist for Samsung. See http://crbug.com/613977 and https://crbug.com/894160 for more // context. try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { PackageManager pm = ContextUtils.getApplicationContext().getPackageManager(); return pm.queryIntentActivities(intent, flags); } catch (RuntimeException e) { handleExpectedExceptionsOrRethrow(e, intent); } return Collections.emptyList(); } /** * Check if the given Intent can be resolved by any Activities on the system. * * See {@link PackageManagerUtils#queryIntentActivities(Intent, int)} */ public static boolean canResolveActivity(Intent intent, int flags) { return !queryIntentActivities(intent, flags).isEmpty(); } /** * Check if the given Intent can be resolved by any Activities on the system. * * See {@link PackageManagerUtils#canResolveActivity(Intent, int)} */ public static boolean canResolveActivity(Intent intent) { return canResolveActivity(intent, 0); } /** * @return Intent to query a list of installed home launchers. */ public static Intent getQueryInstalledHomeLaunchersIntent() { return new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME); } /** * @return Default ResolveInfo to handle a VIEW intent for a url. */ public static ResolveInfo resolveDefaultWebBrowserActivity() { return resolveActivity(BROWSER_INTENT, PackageManager.MATCH_DEFAULT_ONLY); } /** * @return The list of names of web browser applications available in the system. A browser * may appear twice if it has multiple intent handlers. */ public static List queryAllWebBrowsersInfo() { // Copying these flags from Android source for detecting the list of installed browsers. // Apparently MATCH_ALL doesn't include MATCH_DIRECT_BOOT_*. // See // https://cs.android.com/android/_/android/platform/packages/modules/Permission/+/android12-release:PermissionController/src/com/android/permissioncontroller/role/model/BrowserRoleBehavior.java;drc=86fa7d5dfa43f66b170f93ade4f59b9a770be32f;l=114 int flags = PackageManager.MATCH_ALL | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DEFAULT_ONLY; return queryIntentActivities(BROWSER_INTENT, flags); } /** * @return The list of names of system launcher applications available in the system. */ public static List queryAllLaunchersInfo() { return queryIntentActivities( getQueryInstalledHomeLaunchersIntent(), PackageManager.MATCH_ALL); } // See https://crbug.com/700505 and https://crbug.com/369574. private static void handleExpectedExceptionsOrRethrow(RuntimeException e, Intent intent) { if (e instanceof NullPointerException || e.getCause() instanceof TransactionTooLargeException) { Log.e(TAG, "Could not resolve Activity for intent " + intent.toString(), e); } else { throw e; } } }