OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.webapk.shell_apk; | 5 package org.chromium.webapk.shell_apk; |
6 | 6 |
7 import android.content.Context; | 7 import android.content.Context; |
| 8 import android.content.Intent; |
| 9 import android.content.SharedPreferences; |
8 import android.content.pm.ApplicationInfo; | 10 import android.content.pm.ApplicationInfo; |
9 import android.content.pm.PackageManager; | 11 import android.content.pm.PackageManager; |
10 import android.content.pm.PackageManager.NameNotFoundException; | 12 import android.content.pm.PackageManager.NameNotFoundException; |
| 13 import android.content.pm.ResolveInfo; |
| 14 import android.graphics.drawable.Drawable; |
| 15 import android.net.Uri; |
| 16 import android.text.TextUtils; |
11 | 17 |
| 18 import org.chromium.webapk.lib.common.WebApkConstants; |
12 import org.chromium.webapk.lib.common.WebApkMetaDataKeys; | 19 import org.chromium.webapk.lib.common.WebApkMetaDataKeys; |
13 | 20 |
| 21 import java.util.ArrayList; |
| 22 import java.util.Arrays; |
| 23 import java.util.Collections; |
| 24 import java.util.Comparator; |
| 25 import java.util.HashSet; |
| 26 import java.util.List; |
| 27 import java.util.Set; |
| 28 |
14 /** | 29 /** |
15 * Contains utility methods for interacting with WebAPKs. | 30 * Contains utility methods for interacting with WebAPKs. |
16 */ | 31 */ |
17 public class WebApkUtils { | 32 public class WebApkUtils { |
| 33 public static final String SHARED_PREF_RUNTIME_HOST = "runtime_host"; |
18 | 34 |
19 /** | 35 /** |
20 * Caches the value read from Application Metadata which specifies the host
browser's package | 36 * The package names of the channels of Chrome that support WebAPKs. The mos
t preferred one |
21 * name. | 37 * comes first. |
| 38 */ |
| 39 private static List<String> sBrowsersSupportingWebApk = new ArrayList<String
>( |
| 40 Arrays.asList("com.google.android.apps.chrome", "com.android.chrome"
, "com.chrome.beta", |
| 41 "com.chrome.dev", "com.chrome.canary")); |
| 42 |
| 43 /** Stores information about a potential host browser for the WebAPK. */ |
| 44 public static class BrowserItem { |
| 45 private String mPackageName; |
| 46 private CharSequence mApplicationLabel; |
| 47 private Drawable mIcon; |
| 48 private boolean mSupportsWebApks; |
| 49 |
| 50 public BrowserItem(String packageName, CharSequence applicationLabel, Dr
awable icon, |
| 51 boolean supportsWebApks) { |
| 52 mPackageName = packageName; |
| 53 mApplicationLabel = applicationLabel; |
| 54 mIcon = icon; |
| 55 mSupportsWebApks = supportsWebApks; |
| 56 } |
| 57 |
| 58 /** Returns the package name of a browser. */ |
| 59 public String getPackageName() { |
| 60 return mPackageName; |
| 61 } |
| 62 |
| 63 /** Returns the application name of a browser. */ |
| 64 public CharSequence getApplicationName() { |
| 65 return mApplicationLabel; |
| 66 } |
| 67 |
| 68 /** Returns a drawable of the browser icon. */ |
| 69 public Drawable getApplicationIcon() { |
| 70 return mIcon; |
| 71 } |
| 72 |
| 73 /** Returns whether the browser supports WebAPKs. */ |
| 74 public boolean supportsWebApks() { |
| 75 return mSupportsWebApks; |
| 76 } |
| 77 } |
| 78 |
| 79 /** |
| 80 * Caches the package name of the host browser. {@link sHostPackage} might r
efer to a browser |
| 81 * which has been uninstalled. A notification can keep the WebAPK process al
ive after the host |
| 82 * browser has been uninstalled. |
22 */ | 83 */ |
23 private static String sHostPackage; | 84 private static String sHostPackage; |
24 | 85 |
| 86 /** For testing only. */ |
| 87 public static void resetCachedHostPackageForTesting() { |
| 88 sHostPackage = null; |
| 89 } |
| 90 |
25 /** | 91 /** |
26 * Returns a Context for the host browser that was specified when building t
he WebAPK. | 92 * Returns a Context for the host browser that was specified when building t
he WebAPK. |
27 * @param context A context. | 93 * @param context A context. |
28 * @return The remote context. Returns null on an error. | 94 * @return The remote context. Returns null on an error. |
29 */ | 95 */ |
30 public static Context getHostBrowserContext(Context context) { | 96 public static Context getHostBrowserContext(Context context) { |
31 try { | 97 try { |
32 String hostPackage = getHostBrowserPackageName(context); | 98 String hostPackage = getHostBrowserPackageName(context); |
33 return context.getApplicationContext().createPackageContext( | 99 return context.getApplicationContext().createPackageContext( |
34 hostPackage, | 100 hostPackage, Context.CONTEXT_IGNORE_SECURITY | Context.CONTE
XT_INCLUDE_CODE); |
35 Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_IN
CLUDE_CODE); | |
36 } catch (NameNotFoundException e) { | 101 } catch (NameNotFoundException e) { |
37 e.printStackTrace(); | 102 e.printStackTrace(); |
38 } | 103 } |
39 return null; | 104 return null; |
40 } | 105 } |
41 | 106 |
42 /** | 107 /** |
43 * Returns the package name for the host browser that was specified when bui
lding the WebAPK. | 108 * Returns the package name of the host browser to launch the WebAPK. Also c
aches the package |
| 109 * name in the SharedPreference if it is not null. |
44 * @param context A context. | 110 * @param context A context. |
45 * @return The package name. Returns null on an error. | 111 * @return The package name. Returns null on an error. |
46 */ | 112 */ |
47 public static String getHostBrowserPackageName(Context context) { | 113 public static String getHostBrowserPackageName(Context context) { |
48 if (sHostPackage != null) return sHostPackage; | 114 if (sHostPackage == null) { |
49 String hostPackage = readMetaDataFromManifest(context, WebApkMetaDataKey
s.RUNTIME_HOST); | 115 sHostPackage = getHostBrowserPackageNameInternal(context); |
50 // Set {@link sHostPackage} to a non-null value so that the value is com
puted only once. | 116 if (sHostPackage != null) { |
51 sHostPackage = hostPackage != null ? hostPackage : ""; | 117 writeHostBrowserToSharedPref(context, sHostPackage); |
| 118 } |
| 119 } |
| 120 |
52 return sHostPackage; | 121 return sHostPackage; |
53 } | 122 } |
54 | 123 |
55 /** Returns the <meta-data> value in the Android Manifest for {@link key}. *
/ | 124 /** Returns the <meta-data> value in the Android Manifest for {@link key}. *
/ |
56 public static String readMetaDataFromManifest(Context context, String key) { | 125 public static String readMetaDataFromManifest(Context context, String key) { |
57 ApplicationInfo ai = null; | 126 ApplicationInfo ai = null; |
58 try { | 127 try { |
59 ai = context.getPackageManager().getApplicationInfo( | 128 ai = context.getPackageManager().getApplicationInfo( |
60 context.getPackageName(), PackageManager.GET_META_DATA); | 129 context.getPackageName(), PackageManager.GET_META_DATA); |
61 } catch (NameNotFoundException e) { | 130 } catch (NameNotFoundException e) { |
62 return null; | 131 return null; |
63 } | 132 } |
64 return ai.metaData.getString(key); | 133 return ai.metaData.getString(key); |
65 } | 134 } |
66 | 135 |
67 /** | 136 /** |
| 137 * Returns the package name of the host browser to launch the WebAPK, or nul
l if we did not find |
| 138 * one. |
| 139 */ |
| 140 private static String getHostBrowserPackageNameInternal(Context context) { |
| 141 Set<String> installedBrowsers = getInstalledBrowsers(context.getPackageM
anager()); |
| 142 if (installedBrowsers.isEmpty()) { |
| 143 return null; |
| 144 } |
| 145 |
| 146 // Gets the package name of the host browser if it is stored in the Shar
edPreference. |
| 147 String cachedHostBrowser = getHostBrowserFromSharedPreference(context); |
| 148 if (!TextUtils.isEmpty(cachedHostBrowser) |
| 149 && installedBrowsers.contains(cachedHostBrowser)) { |
| 150 return cachedHostBrowser; |
| 151 } |
| 152 |
| 153 // Gets the package name of the host browser if it is specified in Andro
idManifest.xml. |
| 154 String hostBrowserFromManifest = |
| 155 readMetaDataFromManifest(context, WebApkMetaDataKeys.RUNTIME_HOS
T); |
| 156 if (!TextUtils.isEmpty(hostBrowserFromManifest)) { |
| 157 if (installedBrowsers.contains(hostBrowserFromManifest)) { |
| 158 return hostBrowserFromManifest; |
| 159 } |
| 160 return null; |
| 161 } |
| 162 |
| 163 // Gets the package name of the default browser on the Android device. |
| 164 // TODO(hanxi): Investigate the best way to know which browser supports
WebAPKs. |
| 165 String defaultBrowser = getDefaultBrowserPackageName(context.getPackageM
anager()); |
| 166 if (!TextUtils.isEmpty(defaultBrowser) && installedBrowsers.contains(def
aultBrowser) |
| 167 && sBrowsersSupportingWebApk.contains(defaultBrowser)) { |
| 168 return defaultBrowser; |
| 169 } |
| 170 |
| 171 return null; |
| 172 } |
| 173 |
| 174 /** |
| 175 * Returns a list of browsers to choose host browser from. The list includes
all the installed |
| 176 * browsers, and if none of the installed browser supports WebAPKs, Chrome w
ill be added to the |
| 177 * list as well. |
| 178 */ |
| 179 public static List<BrowserItem> getBrowserInfosForHostBrowserSelection( |
| 180 PackageManager packageManager) { |
| 181 Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://
")); |
| 182 List<ResolveInfo> resolvedActivityList = packageManager.queryIntentActiv
ities( |
| 183 browserIntent, PackageManager.MATCH_DEFAULT_ONLY); |
| 184 |
| 185 boolean hasBrowserSupportingWebApks = false; |
| 186 List<BrowserItem> browsers = new ArrayList<>(); |
| 187 for (ResolveInfo info : resolvedActivityList) { |
| 188 boolean supportsWebApk = false; |
| 189 if (sBrowsersSupportingWebApk.contains(info.activityInfo.packageName
)) { |
| 190 supportsWebApk = true; |
| 191 hasBrowserSupportingWebApks = true; |
| 192 } |
| 193 browsers.add(new BrowserItem(info.activityInfo.packageName, |
| 194 info.loadLabel(packageManager), info.loadIcon(packageManager
), supportsWebApk)); |
| 195 } |
| 196 |
| 197 Collections.sort(browsers, new Comparator<BrowserItem>() { |
| 198 @Override |
| 199 public int compare(BrowserItem a, BrowserItem b) { |
| 200 if (a.mSupportsWebApks == b.mSupportsWebApks) { |
| 201 return a.getPackageName().compareTo(b.getPackageName()); |
| 202 } |
| 203 return a.mSupportsWebApks ? -1 : 1; |
| 204 } |
| 205 }); |
| 206 |
| 207 if (hasBrowserSupportingWebApks) return browsers; |
| 208 |
| 209 // TODO(hanxi): add Chrome's icon to WebAPKs. |
| 210 browsers.add(new BrowserItem("com.android.chrome", "Chrome", null, true)
); |
| 211 return browsers; |
| 212 } |
| 213 |
| 214 /** |
68 * Returns the uid for the host browser that was specified when building the
WebAPK. | 215 * Returns the uid for the host browser that was specified when building the
WebAPK. |
69 * @param context A context. | 216 * @param context A context. |
70 * @return The application uid. Returns -1 on an error. | 217 * @return The application uid. Returns -1 on an error. |
71 */ | 218 */ |
72 public static int getHostBrowserUid(Context context) { | 219 public static int getHostBrowserUid(Context context) { |
73 String hostPackageName = getHostBrowserPackageName(context); | 220 String hostPackageName = getHostBrowserPackageName(context); |
74 if (hostPackageName == null) { | 221 if (hostPackageName == null) { |
75 return -1; | 222 return -1; |
76 } | 223 } |
77 try { | 224 try { |
78 PackageManager packageManager = context.getPackageManager(); | 225 PackageManager packageManager = context.getPackageManager(); |
79 ApplicationInfo appInfo = packageManager.getApplicationInfo( | 226 ApplicationInfo appInfo = packageManager.getApplicationInfo( |
80 hostPackageName, PackageManager.GET_META_DATA); | 227 hostPackageName, PackageManager.GET_META_DATA); |
81 return appInfo.uid; | 228 return appInfo.uid; |
82 } catch (NameNotFoundException e) { | 229 } catch (NameNotFoundException e) { |
83 e.printStackTrace(); | 230 e.printStackTrace(); |
84 } | 231 } |
85 return -1; | 232 return -1; |
86 } | 233 } |
| 234 |
| 235 /** Returns the package name of the host browser cached in the SharedPrefere
nces. */ |
| 236 public static String getHostBrowserFromSharedPreference(Context context) { |
| 237 SharedPreferences sharedPref = |
| 238 context.getSharedPreferences(WebApkConstants.PREF_PACKAGE, Conte
xt.MODE_PRIVATE); |
| 239 return sharedPref.getString(SHARED_PREF_RUNTIME_HOST, null); |
| 240 } |
| 241 |
| 242 /** Returns a set of package names of all the installed browsers on the devi
ce. */ |
| 243 public static Set<String> getInstalledBrowsers(PackageManager packageManager
) { |
| 244 Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://
")); |
| 245 List<ResolveInfo> resolvedActivityList = packageManager.queryIntentActiv
ities( |
| 246 browserIntent, PackageManager.MATCH_DEFAULT_ONLY); |
| 247 |
| 248 Set<String> packagesSupportingWebApks = new HashSet<String>(); |
| 249 for (ResolveInfo info : resolvedActivityList) { |
| 250 packagesSupportingWebApks.add(info.activityInfo.packageName); |
| 251 } |
| 252 return packagesSupportingWebApks; |
| 253 } |
| 254 |
| 255 /** Returns the package name of the default browser on the Android device. *
/ |
| 256 private static String getDefaultBrowserPackageName(PackageManager packageMan
ager) { |
| 257 Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://
")); |
| 258 ResolveInfo resolveInfo = |
| 259 packageManager.resolveActivity(browserIntent, PackageManager.MAT
CH_DEFAULT_ONLY); |
| 260 if (resolveInfo == null || resolveInfo.activityInfo == null) return null
; |
| 261 |
| 262 return resolveInfo.activityInfo.packageName; |
| 263 } |
| 264 |
| 265 /** |
| 266 * Writes the package name of the host browser to the SharedPreferences. If
the host browser is |
| 267 * different than the previous one stored, delete the SharedPreference befor
e storing the new |
| 268 * host browser. |
| 269 */ |
| 270 public static void writeHostBrowserToSharedPref(Context context, String host
Package) { |
| 271 if (TextUtils.isEmpty(hostPackage)) return; |
| 272 |
| 273 SharedPreferences sharedPref = |
| 274 context.getSharedPreferences(WebApkConstants.PREF_PACKAGE, Conte
xt.MODE_PRIVATE); |
| 275 SharedPreferences.Editor editor = sharedPref.edit(); |
| 276 editor.putString(SHARED_PREF_RUNTIME_HOST, hostPackage); |
| 277 editor.apply(); |
| 278 } |
| 279 |
| 280 /** Deletes the SharedPreferences. */ |
| 281 public static void deleteSharedPref(Context context) { |
| 282 SharedPreferences sharedPref = |
| 283 context.getSharedPreferences(WebApkConstants.PREF_PACKAGE, Conte
xt.MODE_PRIVATE); |
| 284 SharedPreferences.Editor editor = sharedPref.edit(); |
| 285 editor.clear(); |
| 286 editor.apply(); |
| 287 } |
87 } | 288 } |
OLD | NEW |