Chromium Code Reviews| 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.chrome.browser.webapps; | 5 package org.chromium.chrome.browser.webapps; |
| 6 | 6 |
| 7 import android.content.pm.PackageInfo; | 7 import android.content.pm.PackageInfo; |
| 8 import android.content.pm.PackageManager; | 8 import android.content.pm.PackageManager; |
| 9 import android.graphics.Bitmap; | 9 import android.graphics.Bitmap; |
| 10 import android.os.Bundle; | |
| 11 | 10 |
| 12 import org.chromium.base.CommandLine; | 11 import org.chromium.base.CommandLine; |
| 13 import org.chromium.base.ContextUtils; | 12 import org.chromium.base.ContextUtils; |
| 14 import org.chromium.base.Log; | 13 import org.chromium.base.Log; |
| 15 import org.chromium.base.annotations.CalledByNative; | 14 import org.chromium.base.annotations.CalledByNative; |
| 16 import org.chromium.chrome.browser.ChromeSwitches; | 15 import org.chromium.chrome.browser.ChromeSwitches; |
| 17 import org.chromium.chrome.browser.tab.Tab; | 16 import org.chromium.chrome.browser.tab.Tab; |
| 18 import org.chromium.chrome.browser.util.IntentUtils; | |
| 19 import org.chromium.webapk.lib.client.WebApkVersion; | 17 import org.chromium.webapk.lib.client.WebApkVersion; |
| 20 import org.chromium.webapk.lib.common.WebApkMetaDataKeys; | |
| 21 | 18 |
| 22 import java.util.concurrent.TimeUnit; | 19 import java.util.concurrent.TimeUnit; |
| 23 | 20 |
| 24 /** | 21 /** |
| 25 * WebApkUpdateManager manages when to check for updates to the WebAPK's Web Man ifest, and sends | 22 * WebApkUpdateManager manages when to check for updates to the WebAPK's Web Man ifest, and sends |
| 26 * an update request to the WebAPK Server when an update is needed. | 23 * an update request to the WebAPK Server when an update is needed. |
| 27 */ | 24 */ |
| 28 public class WebApkUpdateManager implements ManifestUpgradeDetector.Callback { | 25 public class WebApkUpdateManager implements ManifestUpgradeDetector.Callback { |
| 29 private static final String TAG = "WebApkUpdateManager"; | 26 private static final String TAG = "WebApkUpdateManager"; |
| 30 | 27 |
| 31 /** Number of milliseconds between checks for whether the WebAPK's Web Manif est has changed. */ | 28 /** Number of milliseconds between checks for whether the WebAPK's Web Manif est has changed. */ |
| 32 public static final long FULL_CHECK_UPDATE_INTERVAL = TimeUnit.DAYS.toMillis (3L); | 29 public static final long FULL_CHECK_UPDATE_INTERVAL = TimeUnit.DAYS.toMillis (3L); |
| 33 | 30 |
| 34 /** | 31 /** |
| 35 * Number of milliseconds to wait before re-requesting an updated WebAPK fro m the WebAPK | 32 * Number of milliseconds to wait before re-requesting an updated WebAPK fro m the WebAPK |
| 36 * server if the previous update attempt failed. | 33 * server if the previous update attempt failed. |
| 37 */ | 34 */ |
| 38 public static final long RETRY_UPDATE_DURATION = TimeUnit.HOURS.toMillis(12L ); | 35 public static final long RETRY_UPDATE_DURATION = TimeUnit.HOURS.toMillis(12L ); |
| 39 | 36 |
| 40 /** | 37 /** Id of WebAPK data in WebappDataStorage */ |
| 41 * Id of WebAPK data in WebappDataStorage | |
| 42 */ | |
| 43 private String mId; | 38 private String mId; |
| 44 | 39 |
| 45 /** Android version code of WebAPK. */ | 40 /** WebAPK package name. */ |
| 46 private int mVersionCode; | 41 private String mWebApkPackageName; |
| 42 | |
| 43 /** Meta data from the WebAPK's Android Manifest */ | |
| 44 private WebApkMetaData mMetaData; | |
| 45 | |
| 46 /** WebAPK's icon. */ | |
| 47 private Bitmap mIcon; | |
| 47 | 48 |
| 48 /** | 49 /** |
| 49 * Whether the previous WebAPK update succeeded. True if there has not been any update attempts. | 50 * Whether the previous WebAPK update succeeded. True if there has not been any update attempts. |
| 50 */ | 51 */ |
| 51 private boolean mPreviousUpdateSucceeded; | 52 private boolean mPreviousUpdateSucceeded; |
| 52 | 53 |
| 53 private ManifestUpgradeDetector mUpgradeDetector; | 54 private ManifestUpgradeDetector mUpgradeDetector; |
| 54 | 55 |
| 55 /** | 56 /** |
| 56 * Checks whether the WebAPK's Web Manifest has changed. Requests an updated WebAPK if the | 57 * Checks whether the WebAPK's Web Manifest has changed. Requests an updated WebAPK if the |
| 57 * Web Manifest has changed. Skips the check if the check was done recently. | 58 * Web Manifest has changed. Skips the check if the check was done recently. |
| 58 * @param tab The tab of the WebAPK. | 59 * @param tab The tab of the WebAPK. |
| 59 * @param info The WebappInfo of the WebAPK. | 60 * @param info The WebappInfo of the WebAPK. |
| 60 */ | 61 */ |
| 61 public void updateIfNeeded(Tab tab, WebappInfo info) { | 62 public void updateIfNeeded(Tab tab, WebappInfo info) { |
| 62 PackageInfo packageInfo = readPackageInfoFromAndroidManifest(info.webApk PackageName()); | 63 mMetaData = WebApkMetaDataUtils.extractMetaDataFromWebApk(info.webApkPac kageName()); |
| 63 if (packageInfo == null) { | 64 if (mMetaData == null) { |
|
dominickn
2016/10/24 11:34:59
Nit: inline the return.
| |
| 64 return; | 65 return; |
| 65 } | 66 } |
| 66 | 67 |
| 68 mWebApkPackageName = info.webApkPackageName(); | |
| 67 mId = info.id(); | 69 mId = info.id(); |
| 68 mVersionCode = packageInfo.versionCode; | 70 mIcon = info.icon(); |
| 69 final Bundle metadata = packageInfo.applicationInfo.metaData; | |
| 70 int shellApkVersion = | |
| 71 IntentUtils.safeGetInt(metadata, WebApkMetaDataKeys.SHELL_APK_VE RSION, 0); | |
| 72 | 71 |
| 73 WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataSt orage(mId); | 72 WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataSt orage(mId); |
| 74 mPreviousUpdateSucceeded = didPreviousUpdateSucceed(storage); | 73 mPreviousUpdateSucceeded = didPreviousUpdateSucceed(storage); |
| 75 | 74 |
| 76 // TODO(pkotwicz|hanxi): Request upgrade if ShellAPK version changes if | 75 if (!shouldCheckIfWebManifestUpdated(storage, mMetaData, mPreviousUpdate Succeeded)) return; |
| 77 // ManifestUpgradeDetector cannot fetch the Web Manifest. For instance, the web developer | |
| 78 // may have removed the Web Manifest from their site. (http://crbug.com/ 639536) | |
| 79 | 76 |
| 80 if (!shouldCheckIfWebManifestUpdated(storage, shellApkVersion, mPrevious UpdateSucceeded)) { | 77 mUpgradeDetector = buildManifestUpgradeDetector(tab, mMetaData); |
| 81 return; | |
| 82 } | |
| 83 | |
| 84 mUpgradeDetector = buildManifestUpgradeDetector(tab, info, metadata); | |
| 85 if (!mUpgradeDetector.start()) return; | 78 if (!mUpgradeDetector.start()) return; |
| 86 | 79 |
| 87 // crbug.com/636525. The timestamp of the last manifest update check sho uld be updated after | 80 // crbug.com/636525. The timestamp of the last manifest update check sho uld be updated after |
| 88 // the detector finds the manifest, not when the detector is started. | 81 // the detector finds the manifest, not when the detector is started. |
| 89 storage.updateTimeOfLastCheckForUpdatedWebManifest(); | 82 storage.updateTimeOfLastCheckForUpdatedWebManifest(); |
| 90 } | 83 } |
| 91 | 84 |
| 85 public void destroy() { | |
|
dominickn
2016/10/24 11:34:59
Can you just override this method in tests, and ca
pkotwicz
2016/10/24 16:03:43
I made destroyUpgradeDetector() its own function t
dominickn
2016/10/25 07:39:16
The semantic difference doesn't really make sense
pkotwicz
2016/10/26 01:53:29
I think that the semantic difference makes sense i
| |
| 86 destroyUpgradeDetector(); | |
| 87 } | |
| 88 | |
| 92 @Override | 89 @Override |
| 93 public void onUpgradeNeededCheckFinished(boolean needsUpgrade, | 90 public void onFinishedFetchingWebManifestForInitialUrl( |
| 94 ManifestUpgradeDetector.FetchedManifestData data) { | 91 boolean needsUpgrade, ManifestUpgradeDetector.FetchedManifestData da ta) { |
| 95 if (mUpgradeDetector != null) { | 92 onGotManifestData(needsUpgrade, data); |
| 96 mUpgradeDetector.destroy(); | 93 } |
| 94 | |
| 95 @Override | |
| 96 public void onGotManifestData( | |
| 97 boolean needsUpgrade, ManifestUpgradeDetector.FetchedManifestData da ta) { | |
| 98 boolean gotManifest = (data != null); | |
| 99 needsUpgrade |= isShellApkVersionOutOfDate(mMetaData); | |
| 100 Log.v(TAG, "Got Manifest: " + gotManifest); | |
| 101 Log.v(TAG, "WebAPK upgrade needed: " + needsUpgrade); | |
| 102 | |
| 103 // If the Web Manifest was not found and an upgrade is requested, stop f etching Web | |
| 104 // Manifests as the user navigates to avoid sending multiple WebAPK upda te requests. In | |
| 105 // particular: | |
| 106 // - A WebAPK update request on the initial load because the Shell APK v ersion is out of | |
| 107 // date. | |
| 108 // - A second WebAPK update request once the user navigates to a page wh ich points to the | |
| 109 // correct Web Manifest URL because the Web Manifest has been updated by the Web | |
| 110 // developer. | |
| 111 // | |
| 112 // If the Web Manifest was not found and an upgrade is not requested, ke ep on fetching | |
| 113 // Web Manifests as the user navigates. For instance, the WebAPK's start _url might not | |
| 114 // point to a Web Manifest because start_url redirects to the WebAPK's m ain page. | |
| 115 if (gotManifest || needsUpgrade) { | |
| 116 destroyUpgradeDetector(); | |
| 97 } | 117 } |
| 98 mUpgradeDetector = null; | |
| 99 | |
| 100 Log.v(TAG, "WebAPK upgrade needed: " + needsUpgrade); | |
| 101 | 118 |
| 102 if (!needsUpgrade) { | 119 if (!needsUpgrade) { |
| 103 if (!mPreviousUpdateSucceeded) { | 120 if (!mPreviousUpdateSucceeded) { |
| 104 recordUpdateInWebappDataStorage(mId, true); | 121 recordUpdateInWebappDataStorage(mId, true); |
| 105 } | 122 } |
| 106 return; | 123 return; |
| 107 } | 124 } |
| 108 | 125 |
| 109 // Set WebAPK update as having failed in case that Chrome is killed prio r to | 126 // Set WebAPK update as having failed in case that Chrome is killed prio r to |
| 110 // {@link onBuiltWebApk} being called. | 127 // {@link onBuiltWebApk} being called. |
| 111 recordUpdateInWebappDataStorage(mId, false); | 128 recordUpdateInWebappDataStorage(mId, false); |
| 112 updateAsync(data); | 129 |
| 130 if (data != null) { | |
| 131 updateAsync(data.startUrl, data.scopeUrl, data.name, data.shortName, data.iconUrl, | |
| 132 data.iconMurmur2Hash, data.icon, data.displayMode, data.orie ntation, | |
| 133 data.themeColor, data.backgroundColor); | |
| 134 return; | |
| 135 } | |
| 136 | |
| 137 updateAsyncUsingAndroidManifestMetaData(); | |
| 113 } | 138 } |
| 114 | 139 |
| 115 /** | 140 /** |
| 116 * Builds {@link ManifestUpgradeDetector}. In a separate function for the sa ke of tests. | 141 * Builds {@link ManifestUpgradeDetector}. In a separate function for the sa ke of tests. |
| 117 */ | 142 */ |
| 118 protected ManifestUpgradeDetector buildManifestUpgradeDetector( | 143 protected ManifestUpgradeDetector buildManifestUpgradeDetector( |
| 119 Tab tab, WebappInfo info, Bundle metaData) { | 144 Tab tab, WebApkMetaData metaData) { |
| 120 return new ManifestUpgradeDetector(tab, info, metaData, this); | 145 return new ManifestUpgradeDetector(tab, metaData, this); |
| 146 } | |
| 147 | |
| 148 /** | |
| 149 * Sends a request to WebAPK Server to update WebAPK using the meta data fro m the WebAPK's | |
| 150 * Android Manifest. | |
| 151 */ | |
| 152 private void updateAsyncUsingAndroidManifestMetaData() { | |
| 153 updateAsync(mMetaData.startUrl, mMetaData.scope, mMetaData.name, mMetaDa ta.shortName, | |
| 154 mMetaData.iconUrl, mMetaData.iconMurmur2Hash, mIcon, mMetaData.d isplayMode, | |
| 155 mMetaData.orientation, mMetaData.themeColor, mMetaData.backgroun dColor); | |
| 121 } | 156 } |
| 122 | 157 |
| 123 /** | 158 /** |
| 124 * Sends request to WebAPK Server to update WebAPK. | 159 * Sends request to WebAPK Server to update WebAPK. |
| 125 */ | 160 */ |
| 126 public void updateAsync(ManifestUpgradeDetector.FetchedManifestData data) { | 161 protected void updateAsync(String startUrl, String scopeUrl, String name, St ring shortName, |
| 127 String packageName = mUpgradeDetector.getWebApkPackageName(); | 162 String iconUrl, String iconMurmur2Hash, Bitmap icon, int displayMode , int orientation, |
| 128 nativeUpdateAsync(mId, data.startUrl, data.scopeUrl, data.name, data.sho rtName, | 163 long themeColor, long backgroundColor) { |
| 129 data.iconUrl, data.iconMurmur2Hash, data.icon, data.displayMode, data.orientation, | 164 int versionCode = readVersionCodeFromAndroidManifest(mWebApkPackageName) ; |
| 130 data.themeColor, data.backgroundColor, mUpgradeDetector.getManif estUrl(), | 165 nativeUpdateAsync(mId, startUrl, scopeUrl, name, shortName, iconUrl, ico nMurmur2Hash, icon, |
| 131 packageName, mVersionCode); | 166 displayMode, orientation, themeColor, backgroundColor, mMetaData .manifestUrl, |
| 167 mWebApkPackageName, versionCode); | |
| 132 } | 168 } |
| 133 | 169 |
| 134 public void destroy() { | 170 /** |
| 135 if (mUpgradeDetector != null) { | 171 * Destroys {@link mUpgradeDetector}. In a separate function for the sake of tests. |
| 136 mUpgradeDetector.destroy(); | 172 */ |
| 137 } | 173 protected void destroyUpgradeDetector() { |
| 174 if (mUpgradeDetector == null) return; | |
| 175 | |
| 176 mUpgradeDetector.destroy(); | |
| 138 mUpgradeDetector = null; | 177 mUpgradeDetector = null; |
| 139 } | 178 } |
| 140 | 179 |
| 141 /** Returns the current time. In a separate function for the sake of testing . */ | 180 /** Returns the current time. In a separate function for the sake of testing . */ |
| 142 protected long currentTimeMillis() { | 181 protected long currentTimeMillis() { |
| 143 return System.currentTimeMillis(); | 182 return System.currentTimeMillis(); |
| 144 } | 183 } |
| 145 | 184 |
| 146 /** | 185 /** |
| 147 * Reads the WebAPK's PackageInfo from the Android Manifest. | 186 * Reads the WebAPK's version code. Returns 0 on failure. |
| 148 */ | 187 */ |
| 149 private PackageInfo readPackageInfoFromAndroidManifest(String webApkPackage) { | 188 private int readVersionCodeFromAndroidManifest(String webApkPackage) { |
| 150 try { | 189 try { |
| 151 PackageManager packageManager = | 190 PackageManager packageManager = |
| 152 ContextUtils.getApplicationContext().getPackageManager(); | 191 ContextUtils.getApplicationContext().getPackageManager(); |
| 153 return packageManager.getPackageInfo(webApkPackage, PackageManager.G ET_META_DATA); | 192 PackageInfo packageInfo = packageManager.getPackageInfo(webApkPackag e, 0); |
| 193 return packageInfo.versionCode; | |
| 154 } catch (PackageManager.NameNotFoundException e) { | 194 } catch (PackageManager.NameNotFoundException e) { |
| 155 e.printStackTrace(); | 195 e.printStackTrace(); |
| 156 } | 196 } |
| 157 return null; | 197 return 0; |
| 158 } | 198 } |
| 159 | 199 |
| 160 /** | 200 /** |
| 161 * Returns whether the previous WebAPK update attempt succeeded. Returns tru e if there has not | 201 * Returns whether the previous WebAPK update attempt succeeded. Returns tru e if there has not |
| 162 * been any update attempts. | 202 * been any update attempts. |
| 163 */ | 203 */ |
| 164 private static boolean didPreviousUpdateSucceed(WebappDataStorage storage) { | 204 private static boolean didPreviousUpdateSucceed(WebappDataStorage storage) { |
| 165 long lastUpdateCompletionTime = storage.getLastWebApkUpdateRequestComple tionTime(); | 205 long lastUpdateCompletionTime = storage.getLastWebApkUpdateRequestComple tionTime(); |
| 166 if (lastUpdateCompletionTime == WebappDataStorage.LAST_USED_INVALID | 206 if (lastUpdateCompletionTime == WebappDataStorage.LAST_USED_INVALID |
| 167 || lastUpdateCompletionTime == WebappDataStorage.LAST_USED_UNSET ) { | 207 || lastUpdateCompletionTime == WebappDataStorage.LAST_USED_UNSET ) { |
| 168 return true; | 208 return true; |
| 169 } | 209 } |
| 170 return storage.getDidLastWebApkUpdateRequestSucceed(); | 210 return storage.getDidLastWebApkUpdateRequestSucceed(); |
| 171 } | 211 } |
| 172 | 212 |
| 173 /** | 213 /** |
| 214 * Whether there is a new version of the //chrome/android/webapk/shell_apk c ode. | |
| 215 */ | |
| 216 private static boolean isShellApkVersionOutOfDate(WebApkMetaData metaData) { | |
| 217 return metaData.shellApkVersion < WebApkVersion.CURRENT_SHELL_APK_VERSIO N; | |
| 218 } | |
| 219 | |
| 220 /** | |
| 174 * Returns whether the Web Manifest should be refetched to check whether it has been updated. | 221 * Returns whether the Web Manifest should be refetched to check whether it has been updated. |
| 175 * TODO: Make this method static once there is a static global clock class. | 222 * TODO: Make this method static once there is a static global clock class. |
| 176 * @param storage WebappDataStorage with the WebAPK's cached data. | 223 * @param storage WebappDataStorage with the WebAPK's cached data. |
| 177 * @param shellApkVersion Version number of //chrome/android/webapk/shell_ap k code. | 224 * @param metaData Meta data from WebAPK's Android Manifest. |
| 178 * @param previousUpdateSucceeded Whether the previous update attempt succee ded. | 225 * @param previousUpdateSucceeded Whether the previous update attempt succee ded. |
| 179 * True if there has not been any update attempts. | 226 * True if there has not been any update attempts. |
| 180 */ | 227 */ |
| 181 private boolean shouldCheckIfWebManifestUpdated( | 228 private boolean shouldCheckIfWebManifestUpdated( |
| 182 WebappDataStorage storage, int shellApkVersion, boolean previousUpda teSucceeded) { | 229 WebappDataStorage storage, WebApkMetaData metaData, boolean previous UpdateSucceeded) { |
| 183 if (CommandLine.getInstance().hasSwitch( | 230 if (CommandLine.getInstance().hasSwitch( |
| 184 ChromeSwitches.CHECK_FOR_WEB_MANIFEST_UPDATE_ON_STARTUP)) { | 231 ChromeSwitches.CHECK_FOR_WEB_MANIFEST_UPDATE_ON_STARTUP)) { |
| 185 return true; | 232 return true; |
| 186 } | 233 } |
| 187 | 234 |
| 188 if (shellApkVersion < WebApkVersion.CURRENT_SHELL_APK_VERSION) return tr ue; | 235 if (isShellApkVersionOutOfDate(metaData)) return true; |
| 189 | 236 |
| 190 long now = currentTimeMillis(); | 237 long now = currentTimeMillis(); |
| 191 long sinceLastCheckDurationMs = now - storage.getLastCheckForWebManifest UpdateTime(); | 238 long sinceLastCheckDurationMs = now - storage.getLastCheckForWebManifest UpdateTime(); |
| 192 if (sinceLastCheckDurationMs >= FULL_CHECK_UPDATE_INTERVAL) return true; | 239 if (sinceLastCheckDurationMs >= FULL_CHECK_UPDATE_INTERVAL) return true; |
| 193 | 240 |
| 194 long sinceLastUpdateRequestDurationMs = | 241 long sinceLastUpdateRequestDurationMs = |
| 195 now - storage.getLastWebApkUpdateRequestCompletionTime(); | 242 now - storage.getLastWebApkUpdateRequestCompletionTime(); |
| 196 return sinceLastUpdateRequestDurationMs >= RETRY_UPDATE_DURATION | 243 return sinceLastUpdateRequestDurationMs >= RETRY_UPDATE_DURATION |
| 197 && !previousUpdateSucceeded; | 244 && !previousUpdateSucceeded; |
| 198 } | 245 } |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 216 @CalledByNative | 263 @CalledByNative |
| 217 private static void onBuiltWebApk(String id, boolean success) { | 264 private static void onBuiltWebApk(String id, boolean success) { |
| 218 recordUpdateInWebappDataStorage(id, success); | 265 recordUpdateInWebappDataStorage(id, success); |
| 219 } | 266 } |
| 220 | 267 |
| 221 private static native void nativeUpdateAsync(String id, String startUrl, Str ing scope, | 268 private static native void nativeUpdateAsync(String id, String startUrl, Str ing scope, |
| 222 String name, String shortName, String iconUrl, String iconMurmur2Has h, Bitmap icon, | 269 String name, String shortName, String iconUrl, String iconMurmur2Has h, Bitmap icon, |
| 223 int displayMode, int orientation, long themeColor, long backgroundCo lor, | 270 int displayMode, int orientation, long themeColor, long backgroundCo lor, |
| 224 String manifestUrl, String webApkPackage, int webApkVersion); | 271 String manifestUrl, String webApkPackage, int webApkVersion); |
| 225 } | 272 } |
| OLD | NEW |