• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base;
6 
7 import android.content.Intent;
8 import android.content.pm.PackageManager;
9 import android.content.pm.ResolveInfo;
10 import android.net.Uri;
11 import android.os.TransactionTooLargeException;
12 
13 import java.util.Collections;
14 import java.util.List;
15 
16 /** This class provides Android PackageManager related utility methods. */
17 public class PackageManagerUtils {
18     private static final String TAG = "PackageManagerUtils";
19 
20     // This is the intent Android uses internally to detect browser apps.
21     // See
22     // 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
23     public static final Intent BROWSER_INTENT =
24             new Intent()
25                     .setAction(Intent.ACTION_VIEW)
26                     .addCategory(Intent.CATEGORY_BROWSABLE)
27                     .setData(Uri.fromParts("http", "", null));
28 
29     /**
30      * Retrieve information about the Activity that will handle the given Intent.
31      *
32      * Note: This function is expensive on KK and below and should not be called from main thread
33      * when avoidable.
34      *
35      * @param intent Intent to resolve.
36      * @param flags The PackageManager flags to pass to resolveActivity().
37      * @return       ResolveInfo of the Activity that will handle the Intent, or null if it failed.
38      */
resolveActivity(Intent intent, int flags)39     public static ResolveInfo resolveActivity(Intent intent, int flags) {
40         // On KitKat, calling PackageManager#resolveActivity() causes disk reads and
41         // writes. Temporarily allow this while resolving the intent.
42         try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {
43             PackageManager pm = ContextUtils.getApplicationContext().getPackageManager();
44             return pm.resolveActivity(intent, flags);
45         } catch (RuntimeException e) {
46             handleExpectedExceptionsOrRethrow(e, intent);
47         }
48         return null;
49     }
50 
51     /**
52      * Get the list of component name of activities which can resolve |intent|.  If the request
53      * fails, an empty list will be returned.
54      *
55      * See {@link PackageManager#queryIntentActivities(Intent, int)}
56      */
queryIntentActivities(Intent intent, int flags)57     public static List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
58         // Allowlist for Samsung. See http://crbug.com/613977 and https://crbug.com/894160 for more
59         // context.
60         try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
61             PackageManager pm = ContextUtils.getApplicationContext().getPackageManager();
62             return pm.queryIntentActivities(intent, flags);
63         } catch (RuntimeException e) {
64             handleExpectedExceptionsOrRethrow(e, intent);
65         }
66         return Collections.emptyList();
67     }
68 
69     /**
70      * Check if the given Intent can be resolved by any Activities on the system.
71      *
72      * See {@link PackageManagerUtils#queryIntentActivities(Intent, int)}
73      */
canResolveActivity(Intent intent, int flags)74     public static boolean canResolveActivity(Intent intent, int flags) {
75         return !queryIntentActivities(intent, flags).isEmpty();
76     }
77 
78     /**
79      * Check if the given Intent can be resolved by any Activities on the system.
80      *
81      * See {@link PackageManagerUtils#canResolveActivity(Intent, int)}
82      */
canResolveActivity(Intent intent)83     public static boolean canResolveActivity(Intent intent) {
84         return canResolveActivity(intent, 0);
85     }
86 
87     /**
88      * @return Intent to query a list of installed home launchers.
89      */
getQueryInstalledHomeLaunchersIntent()90     public static Intent getQueryInstalledHomeLaunchersIntent() {
91         return new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME);
92     }
93 
94     /**
95      * @return Default ResolveInfo to handle a VIEW intent for a url.
96      */
resolveDefaultWebBrowserActivity()97     public static ResolveInfo resolveDefaultWebBrowserActivity() {
98         return resolveActivity(BROWSER_INTENT, PackageManager.MATCH_DEFAULT_ONLY);
99     }
100 
101     /**
102      * @return The list of names of web browser applications available in the system. A browser
103      *         may appear twice if it has multiple intent handlers.
104      */
queryAllWebBrowsersInfo()105     public static List<ResolveInfo> queryAllWebBrowsersInfo() {
106         // Copying these flags from Android source for detecting the list of installed browsers.
107         // Apparently MATCH_ALL doesn't include MATCH_DIRECT_BOOT_*.
108         // See
109         // 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
110         int flags =
111                 PackageManager.MATCH_ALL
112                         | PackageManager.MATCH_DIRECT_BOOT_AWARE
113                         | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
114                         | PackageManager.MATCH_DEFAULT_ONLY;
115         return queryIntentActivities(BROWSER_INTENT, flags);
116     }
117 
118     /**
119      * @return The list of names of system launcher applications available in the system.
120      */
queryAllLaunchersInfo()121     public static List<ResolveInfo> queryAllLaunchersInfo() {
122         return queryIntentActivities(
123                 getQueryInstalledHomeLaunchersIntent(), PackageManager.MATCH_ALL);
124     }
125 
126     // See https://crbug.com/700505 and https://crbug.com/369574.
handleExpectedExceptionsOrRethrow(RuntimeException e, Intent intent)127     private static void handleExpectedExceptionsOrRethrow(RuntimeException e, Intent intent) {
128         if (e instanceof NullPointerException
129                 || e.getCause() instanceof TransactionTooLargeException) {
130             Log.e(TAG, "Could not resolve Activity for intent " + intent.toString(), e);
131         } else {
132             throw e;
133         }
134     }
135 }
136