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; | 7 import android.app.Service; |
8 import android.content.Context; | 8 import android.content.Context; |
9 import android.content.Intent; | 9 import android.content.Intent; |
| 10 import android.content.SharedPreferences; |
| 11 import android.content.pm.PackageInfo; |
| 12 import android.content.pm.PackageManager; |
10 import android.os.Bundle; | 13 import android.os.Bundle; |
11 import android.os.IBinder; | 14 import android.os.IBinder; |
12 import android.util.Log; | 15 import android.util.Log; |
13 | 16 |
14 import org.chromium.webapk.lib.common.WebApkUtils; | 17 import org.chromium.webapk.lib.common.WebApkUtils; |
15 | 18 |
16 import java.io.File; | 19 import java.io.File; |
17 import java.lang.reflect.Constructor; | 20 import java.lang.reflect.Constructor; |
| 21 import java.util.Scanner; |
18 | 22 |
19 /** | 23 /** |
20 * Shell class for services provided by WebAPK to Chrome. Extracts code with imp
lementation of | 24 * Shell class for services provided by WebAPK to Chrome. Extracts code with imp
lementation of |
21 * services from .dex file in Chrome APK. | 25 * services from .dex file in Chrome APK. |
22 */ | 26 */ |
23 public class WebApkServiceFactory extends Service { | 27 public class WebApkServiceFactory extends Service { |
24 private static final String TAG = "cr_WebApkServiceFactory"; | 28 private static final String TAG = "cr_WebApkServiceFactory"; |
25 | 29 |
26 /** | 30 /** |
27 * Name of the class with IBinder API implementation. | 31 * Name of the class with IBinder API implementation. |
28 */ | 32 */ |
29 private static final String WEBAPK_SERVICE_IMPL_CLASS_NAME = | 33 private static final String WEBAPK_SERVICE_IMPL_CLASS_NAME = |
30 "org.chromium.webapk.lib.runtime_library.WebApkServiceImpl"; | 34 "org.chromium.webapk.lib.runtime_library.WebApkServiceImpl"; |
31 | 35 |
| 36 /** |
| 37 * Name of the shared preferences file. |
| 38 */ |
| 39 private static final String PREF_PACKAGE = "org.chromium.webapk.shell_apk"; |
| 40 |
| 41 /** |
| 42 * Name of the shared preference for Chrome's version code. |
| 43 */ |
| 44 private static final String REMOTE_VERSION_CODE_PREF = |
| 45 "org.chromium.webapk.shell_apk.version_code"; |
| 46 |
| 47 /** |
| 48 * Name of the shared preference for the version number of the dynamically l
oaded dex. |
| 49 */ |
| 50 private static final String RUNTIME_DEX_VERSION_PREF = |
| 51 "org.chromium.webapk.shell_apk.dex_version"; |
| 52 |
32 private static final String KEY_SMALL_ICON_ID = "small_icon_id"; | 53 private static final String KEY_SMALL_ICON_ID = "small_icon_id"; |
33 | 54 |
34 /* | 55 /* |
35 * ClassLoader for loading {@link WEBAPK_SERVICE_IMPL_CLASS_NAME}. Static so
that all | 56 * ClassLoader for loading {@link WEBAPK_SERVICE_IMPL_CLASS_NAME}. Static so
that all |
36 * {@link WebApkServiceFactory} service instatiations use the same ClassLoad
er during the app's | 57 * {@link WebApkServiceFactory} service instatiations use the same ClassLoad
er during the app's |
37 * lifetime. | 58 * lifetime. |
38 */ | 59 */ |
39 private static ClassLoader sClassLoader; | 60 private static ClassLoader sClassLoader; |
40 | 61 |
41 @Override | 62 @Override |
(...skipping 13 matching lines...) Expand all Loading... |
55 bundle.putInt(KEY_SMALL_ICON_ID, R.drawable.app_icon); | 76 bundle.putInt(KEY_SMALL_ICON_ID, R.drawable.app_icon); |
56 return (IBinder) webApkServiceImplConstructor.newInstance(new Object
[] {this, bundle}); | 77 return (IBinder) webApkServiceImplConstructor.newInstance(new Object
[] {this, bundle}); |
57 } catch (Exception e) { | 78 } catch (Exception e) { |
58 Log.w(TAG, "Unable to create WebApkServiceImpl."); | 79 Log.w(TAG, "Unable to create WebApkServiceImpl."); |
59 e.printStackTrace(); | 80 e.printStackTrace(); |
60 return null; | 81 return null; |
61 } | 82 } |
62 } | 83 } |
63 | 84 |
64 /** | 85 /** |
65 * Gets / creates ClassLaoder for loading {@link WEBAPK_SERVICE_IMPL_CLASS_N
AME}. | 86 * Gets / creates ClassLoader for loading {@link WEBAPK_SERVICE_IMPL_CLASS_N
AME}. |
66 * @param context WebAPK's context. | 87 * @param context WebAPK's context. |
67 * @return The ClassLoader. | 88 * @return The ClassLoader. |
68 */ | 89 */ |
69 private static ClassLoader getClassLoaderInstance(Context context) { | 90 private static ClassLoader getClassLoaderInstance(Context context) { |
70 if (sClassLoader == null) { | 91 if (sClassLoader == null) { |
71 sClassLoader = createClassLoader(context); | 92 sClassLoader = createClassLoader(context); |
72 } | 93 } |
73 return sClassLoader; | 94 return sClassLoader; |
74 } | 95 } |
75 | 96 |
76 /** | 97 /** |
77 * Creates ClassLoader for loading {@link WEBAPK_SERVICE_IMPL_CLASS_NAME}. | 98 * Creates ClassLoader for loading {@link WEBAPK_SERVICE_IMPL_CLASS_NAME}. |
78 * @param context WebAPK's context. | 99 * @param context WebAPK's context. |
79 * @return The ClassLoader. | 100 * @return The ClassLoader. |
80 */ | 101 */ |
81 private static ClassLoader createClassLoader(Context context) { | 102 private static ClassLoader createClassLoader(Context context) { |
82 Context remoteContext = WebApkUtils.getHostBrowserContext(context); | 103 Context remoteContext = WebApkUtils.getHostBrowserContext(context); |
83 if (remoteContext == null) { | 104 if (remoteContext == null) { |
84 Log.w(TAG, "Failed to get remote context."); | 105 Log.w(TAG, "Failed to get remote context."); |
85 return null; | 106 return null; |
86 } | 107 } |
87 | 108 |
| 109 SharedPreferences preferences = context.getSharedPreferences(PREF_PACKAG
E, MODE_PRIVATE); |
| 110 |
| 111 int runtimeDexVersion = preferences.getInt(RUNTIME_DEX_VERSION_PREF, -1)
; |
| 112 int newRuntimeDexVersion = checkForNewRuntimeDexVersion(preferences, rem
oteContext); |
| 113 if (newRuntimeDexVersion == -1) { |
| 114 newRuntimeDexVersion = runtimeDexVersion; |
| 115 } |
88 File localDexDir = context.getDir("dex", Context.MODE_PRIVATE); | 116 File localDexDir = context.getDir("dex", Context.MODE_PRIVATE); |
| 117 if (newRuntimeDexVersion != runtimeDexVersion) { |
| 118 Log.w(TAG, "Delete cached dex files."); |
| 119 DexLoader.deleteCachedDexes(localDexDir); |
| 120 } |
| 121 |
| 122 String dexAssetName = WebApkUtils.getRuntimeDexName(newRuntimeDexVersion
); |
89 File remoteDexFile = | 123 File remoteDexFile = |
90 new File(remoteContext.getDir("dex", Context.MODE_PRIVATE), "web
_apk.dex"); | 124 new File(remoteContext.getDir("dex", Context.MODE_PRIVATE), dexA
ssetName); |
91 return DexLoader.load(remoteContext, "web_apk.dex", WEBAPK_SERVICE_IMPL_
CLASS_NAME, | 125 return DexLoader.load(remoteContext, dexAssetName, WEBAPK_SERVICE_IMPL_C
LASS_NAME, |
92 remoteDexFile, localDexDir); | 126 remoteDexFile, localDexDir); |
93 } | 127 } |
| 128 |
| 129 /** |
| 130 * Checks if there is a new "runtime dex" version number. If there is a new
version number, |
| 131 * updates SharedPreferences. |
| 132 * @param preferences WebAPK's SharedPreferences. |
| 133 * @param remoteContext |
| 134 * @return The new "runtime dex" version number. -1 if there is no new versi
on number. |
| 135 */ |
| 136 private static int checkForNewRuntimeDexVersion( |
| 137 SharedPreferences preferences, Context remoteContext) { |
| 138 // The "runtime dex" version only changes when {@link remoteContext}'s A
PK version code |
| 139 // changes. Checking the APK's version code is less expensive than readi
ng from the APK's |
| 140 // assets. |
| 141 PackageInfo remotePackageInfo = null; |
| 142 try { |
| 143 remotePackageInfo = remoteContext.getPackageManager().getPackageInfo
( |
| 144 remoteContext.getPackageName(), 0); |
| 145 } catch (PackageManager.NameNotFoundException e) { |
| 146 Log.e(TAG, "Failed to get remote package info."); |
| 147 return -1; |
| 148 } |
| 149 |
| 150 int cachedRemoteVersionCode = preferences.getInt(REMOTE_VERSION_CODE_PRE
F, -1); |
| 151 if (cachedRemoteVersionCode == remotePackageInfo.versionCode) { |
| 152 return -1; |
| 153 } |
| 154 |
| 155 int runtimeDexVersion = readAssetContentsToInt(remoteContext, "webapk_de
x_version.txt"); |
| 156 SharedPreferences.Editor editor = preferences.edit(); |
| 157 editor.putInt(REMOTE_VERSION_CODE_PREF, remotePackageInfo.versionCode); |
| 158 editor.putInt(RUNTIME_DEX_VERSION_PREF, runtimeDexVersion); |
| 159 editor.apply(); |
| 160 return runtimeDexVersion; |
| 161 } |
| 162 |
| 163 /** |
| 164 * Returns the first integer in an asset file's contents. |
| 165 * @param context |
| 166 * @param assetName The name of the asset. |
| 167 * @return The first integer. |
| 168 */ |
| 169 private static int readAssetContentsToInt(Context context, String assetName)
{ |
| 170 Scanner scanner = null; |
| 171 int value = -1; |
| 172 try { |
| 173 scanner = new Scanner(context.getAssets().open(assetName)); |
| 174 value = scanner.nextInt(); |
| 175 scanner.close(); |
| 176 } catch (Exception e) { |
| 177 } finally { |
| 178 if (scanner != null) { |
| 179 try { |
| 180 scanner.close(); |
| 181 } catch (Exception e) { |
| 182 } |
| 183 } |
| 184 } |
| 185 return value; |
| 186 } |
94 } | 187 } |
OLD | NEW |