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.app.Service; | |
8 import android.content.Context; | 7 import android.content.Context; |
9 import android.content.Intent; | |
10 import android.content.SharedPreferences; | 8 import android.content.SharedPreferences; |
11 import android.content.pm.PackageInfo; | 9 import android.content.pm.PackageInfo; |
12 import android.content.pm.PackageManager; | 10 import android.content.pm.PackageManager; |
13 import android.os.Bundle; | |
14 import android.os.IBinder; | |
15 import android.util.Log; | 11 import android.util.Log; |
16 | 12 |
17 import org.chromium.webapk.lib.common.WebApkUtils; | 13 import org.chromium.webapk.lib.common.WebApkUtils; |
18 | 14 |
19 import java.io.File; | 15 import java.io.File; |
20 import java.lang.reflect.Constructor; | |
21 import java.util.Scanner; | 16 import java.util.Scanner; |
22 | 17 |
23 /** | 18 /** |
24 * Shell class for services provided by WebAPK to Chrome. Extracts code with imp
lementation of | 19 * Creates ClassLoader for WebAPK-specific dex file in Chrome APK's assets. |
25 * services from .dex file in Chrome APK. | |
26 */ | 20 */ |
27 public class WebApkServiceFactory extends Service { | 21 public class HostBrowserClassLoader { |
28 private static final String TAG = "cr_WebApkServiceFactory"; | 22 private static final String TAG = "cr_HostBrowserClassLoader"; |
29 | |
30 /** | |
31 * Name of the class with IBinder API implementation. | |
32 */ | |
33 private static final String WEBAPK_SERVICE_IMPL_CLASS_NAME = | |
34 "org.chromium.webapk.lib.runtime_library.WebApkServiceImpl"; | |
35 | 23 |
36 /** | 24 /** |
37 * Name of the shared preferences file. | 25 * Name of the shared preferences file. |
38 */ | 26 */ |
39 private static final String PREF_PACKAGE = "org.chromium.webapk.shell_apk"; | 27 private static final String PREF_PACKAGE = "org.chromium.webapk.shell_apk"; |
40 | 28 |
41 /** | 29 /** |
42 * Name of the shared preference for Chrome's version code. | 30 * Name of the shared preference for Chrome's version code. |
43 */ | 31 */ |
44 private static final String REMOTE_VERSION_CODE_PREF = | 32 private static final String REMOTE_VERSION_CODE_PREF = |
45 "org.chromium.webapk.shell_apk.version_code"; | 33 "org.chromium.webapk.shell_apk.version_code"; |
46 | 34 |
47 /** | 35 /** |
48 * Name of the shared preference for the version number of the dynamically l
oaded dex. | 36 * Name of the shared preference for the version number of the dynamically l
oaded dex. |
49 */ | 37 */ |
50 private static final String RUNTIME_DEX_VERSION_PREF = | 38 private static final String RUNTIME_DEX_VERSION_PREF = |
51 "org.chromium.webapk.shell_apk.dex_version"; | 39 "org.chromium.webapk.shell_apk.dex_version"; |
52 | 40 |
53 /** | |
54 * Key for passing id of icon to represent WebAPK notifications in status ba
r. | |
55 */ | |
56 private static final String KEY_SMALL_ICON_ID = "small_icon_id"; | |
57 | |
58 /** | |
59 * Key for passing package name of only process allowed to call the service'
s methods. | |
60 */ | |
61 private static final String KEY_HOST_BROWSER_PACKAGE = "host_browser_package
"; | |
62 | |
63 /* | 41 /* |
64 * ClassLoader for loading {@link WEBAPK_SERVICE_IMPL_CLASS_NAME}. Static so
that all | 42 * ClassLoader for WebAPK dex. Static so that the same ClassLoader is used f
or app's lifetime. |
65 * {@link WebApkServiceFactory} service instatiations use the same ClassLoad
er during the app's | |
66 * lifetime. | |
67 */ | 43 */ |
68 private static ClassLoader sClassLoader; | 44 private static ClassLoader sClassLoader; |
69 | 45 |
70 @Override | 46 /** |
71 public IBinder onBind(Intent intent) { | 47 * Guards all access to {@link sClassLoader}. |
72 ClassLoader webApkClassLoader = getClassLoaderInstance(this); | 48 */ |
73 if (webApkClassLoader == null) { | 49 private static final Object sLock = new Object(); |
74 Log.w(TAG, "Unable to create ClassLoader."); | |
75 return null; | |
76 } | |
77 | |
78 try { | |
79 Class<?> webApkServiceImplClass = | |
80 webApkClassLoader.loadClass(WEBAPK_SERVICE_IMPL_CLASS_NAME); | |
81 Constructor<?> webApkServiceImplConstructor = | |
82 webApkServiceImplClass.getConstructor(Context.class, Bundle.
class); | |
83 String hostPackageName = WebApkUtils.getHostBrowserPackageName(this)
; | |
84 Bundle bundle = new Bundle(); | |
85 bundle.putInt(KEY_SMALL_ICON_ID, R.drawable.app_icon); | |
86 bundle.putString(KEY_HOST_BROWSER_PACKAGE, hostPackageName); | |
87 return (IBinder) webApkServiceImplConstructor.newInstance(new Object
[] {this, bundle}); | |
88 } catch (Exception e) { | |
89 Log.w(TAG, "Unable to create WebApkServiceImpl."); | |
90 e.printStackTrace(); | |
91 return null; | |
92 } | |
93 } | |
94 | 50 |
95 /** | 51 /** |
96 * Gets / creates ClassLoader for loading {@link WEBAPK_SERVICE_IMPL_CLASS_N
AME}. | 52 * Gets / creates ClassLoader for WebAPK dex. |
97 * @param context WebAPK's context. | 53 * @param context WebAPK's context. |
| 54 * @param canaryClassname Class to load to check that ClassLoader is valid. |
98 * @return The ClassLoader. | 55 * @return The ClassLoader. |
99 */ | 56 */ |
100 private static ClassLoader getClassLoaderInstance(Context context) { | 57 public static ClassLoader getClassLoaderInstance(Context context, String can
aryClassName) { |
101 if (sClassLoader == null) { | 58 synchronized (sLock) { |
102 sClassLoader = createClassLoader(context); | 59 if (sClassLoader == null) { |
| 60 sClassLoader = createClassLoader(context, canaryClassName); |
| 61 } |
103 } | 62 } |
104 return sClassLoader; | 63 return sClassLoader; |
105 } | 64 } |
106 | 65 |
107 /** | 66 /** |
108 * Creates ClassLoader for loading {@link WEBAPK_SERVICE_IMPL_CLASS_NAME}. | 67 * Creates ClassLoader for WebAPK dex. |
109 * @param context WebAPK's context. | 68 * @param context WebAPK's context. |
| 69 * @param canaryClassName Class to load to check that ClassLoader is valid. |
110 * @return The ClassLoader. | 70 * @return The ClassLoader. |
111 */ | 71 */ |
112 private static ClassLoader createClassLoader(Context context) { | 72 private static ClassLoader createClassLoader(Context context, String canaryC
lassName) { |
113 Context remoteContext = WebApkUtils.getHostBrowserContext(context); | 73 Context remoteContext = WebApkUtils.getHostBrowserContext(context); |
114 if (remoteContext == null) { | 74 if (remoteContext == null) { |
115 Log.w(TAG, "Failed to get remote context."); | 75 Log.w(TAG, "Failed to get remote context."); |
116 return null; | 76 return null; |
117 } | 77 } |
118 | 78 |
119 SharedPreferences preferences = context.getSharedPreferences(PREF_PACKAG
E, MODE_PRIVATE); | 79 SharedPreferences preferences = |
| 80 context.getSharedPreferences(PREF_PACKAGE, Context.MODE_PRIVATE)
; |
120 | 81 |
121 int runtimeDexVersion = preferences.getInt(RUNTIME_DEX_VERSION_PREF, -1)
; | 82 int runtimeDexVersion = preferences.getInt(RUNTIME_DEX_VERSION_PREF, -1)
; |
122 int newRuntimeDexVersion = checkForNewRuntimeDexVersion(preferences, rem
oteContext); | 83 int newRuntimeDexVersion = checkForNewRuntimeDexVersion(preferences, rem
oteContext); |
123 if (newRuntimeDexVersion == -1) { | 84 if (newRuntimeDexVersion == -1) { |
124 newRuntimeDexVersion = runtimeDexVersion; | 85 newRuntimeDexVersion = runtimeDexVersion; |
125 } | 86 } |
126 File localDexDir = context.getDir("dex", Context.MODE_PRIVATE); | 87 File localDexDir = context.getDir("dex", Context.MODE_PRIVATE); |
127 if (newRuntimeDexVersion != runtimeDexVersion) { | 88 if (newRuntimeDexVersion != runtimeDexVersion) { |
128 Log.w(TAG, "Delete cached dex files."); | 89 Log.w(TAG, "Delete cached dex files."); |
129 DexLoader.deleteCachedDexes(localDexDir); | 90 DexLoader.deleteCachedDexes(localDexDir); |
130 } | 91 } |
131 | 92 |
132 String dexAssetName = WebApkUtils.getRuntimeDexName(newRuntimeDexVersion
); | 93 String dexAssetName = WebApkUtils.getRuntimeDexName(newRuntimeDexVersion
); |
133 File remoteDexFile = | 94 File remoteDexFile = |
134 new File(remoteContext.getDir("dex", Context.MODE_PRIVATE), dexA
ssetName); | 95 new File(remoteContext.getDir("dex", Context.MODE_PRIVATE), dexA
ssetName); |
135 return DexLoader.load(remoteContext, dexAssetName, WEBAPK_SERVICE_IMPL_C
LASS_NAME, | 96 return DexLoader.load(remoteContext, dexAssetName, canaryClassName, |
136 remoteDexFile, localDexDir); | 97 remoteDexFile, localDexDir); |
137 } | 98 } |
138 | 99 |
139 /** | 100 /** |
140 * Checks if there is a new "runtime dex" version number. If there is a new
version number, | 101 * Checks if there is a new "runtime dex" version number. If there is a new
version number, |
141 * updates SharedPreferences. | 102 * updates SharedPreferences. |
142 * @param preferences WebAPK's SharedPreferences. | 103 * @param preferences WebAPK's SharedPreferences. |
143 * @param remoteContext | 104 * @param remoteContext |
144 * @return The new "runtime dex" version number. -1 if there is no new versi
on number. | 105 * @return The new "runtime dex" version number. -1 if there is no new versi
on number. |
145 */ | 106 */ |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
188 if (scanner != null) { | 149 if (scanner != null) { |
189 try { | 150 try { |
190 scanner.close(); | 151 scanner.close(); |
191 } catch (Exception e) { | 152 } catch (Exception e) { |
192 } | 153 } |
193 } | 154 } |
194 } | 155 } |
195 return value; | 156 return value; |
196 } | 157 } |
197 } | 158 } |
OLD | NEW |