| 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.text.TextUtils; | 10 import android.text.TextUtils; |
| 11 | 11 |
| 12 import org.chromium.base.ActivityState; | 12 import org.chromium.base.ActivityState; |
| 13 import org.chromium.base.ApplicationStatus; | 13 import org.chromium.base.ApplicationStatus; |
| 14 import org.chromium.base.CommandLine; | 14 import org.chromium.base.CommandLine; |
| 15 import org.chromium.base.ContextUtils; | 15 import org.chromium.base.ContextUtils; |
| 16 import org.chromium.base.Log; | 16 import org.chromium.base.Log; |
| 17 import org.chromium.base.annotations.CalledByNative; | 17 import org.chromium.base.annotations.CalledByNative; |
| 18 import org.chromium.chrome.browser.ChromeSwitches; | 18 import org.chromium.chrome.browser.ChromeSwitches; |
| 19 import org.chromium.chrome.browser.metrics.WebApkUma; | 19 import org.chromium.chrome.browser.metrics.WebApkUma; |
| 20 import org.chromium.chrome.browser.tab.Tab; | 20 import org.chromium.chrome.browser.tab.Tab; |
| 21 import org.chromium.chrome.browser.util.UrlUtilities; | 21 import org.chromium.chrome.browser.util.UrlUtilities; |
| 22 import org.chromium.webapk.lib.client.WebApkVersion; | 22 import org.chromium.webapk.lib.client.WebApkVersion; |
| 23 | 23 |
| 24 import java.util.Map; | 24 import java.util.Map; |
| 25 import java.util.concurrent.TimeUnit; | |
| 26 | 25 |
| 27 /** | 26 /** |
| 28 * WebApkUpdateManager manages when to check for updates to the WebAPK's Web Man
ifest, and sends | 27 * WebApkUpdateManager manages when to check for updates to the WebAPK's Web Man
ifest, and sends |
| 29 * an update request to the WebAPK Server when an update is needed. | 28 * an update request to the WebAPK Server when an update is needed. |
| 30 */ | 29 */ |
| 31 public class WebApkUpdateManager implements WebApkUpdateDataFetcher.Observer { | 30 public class WebApkUpdateManager implements WebApkUpdateDataFetcher.Observer { |
| 32 private static final String TAG = "WebApkUpdateManager"; | 31 private static final String TAG = "WebApkUpdateManager"; |
| 33 | 32 |
| 34 /** Number of milliseconds between checks for whether the WebAPK's Web Manif
est has changed. */ | |
| 35 public static final long FULL_CHECK_UPDATE_INTERVAL = TimeUnit.DAYS.toMillis
(3L); | |
| 36 | |
| 37 /** | |
| 38 * Number of milliseconds to wait before re-requesting an updated WebAPK fro
m the WebAPK | |
| 39 * server if the previous update attempt failed. | |
| 40 */ | |
| 41 public static final long RETRY_UPDATE_DURATION = TimeUnit.HOURS.toMillis(12L
); | |
| 42 | |
| 43 /** | 33 /** |
| 44 * Number of times to wait for updating the WebAPK after it is moved to the
background prior | 34 * Number of times to wait for updating the WebAPK after it is moved to the
background prior |
| 45 * to doing the update while the WebAPK is in the foreground. | 35 * to doing the update while the WebAPK is in the foreground. |
| 46 */ | 36 */ |
| 47 private static final int MAX_UPDATE_ATTEMPTS = 3; | 37 private static final int MAX_UPDATE_ATTEMPTS = 3; |
| 48 | 38 |
| 49 /** Whether updates are enabled. Some tests disable updates. */ | 39 /** Whether updates are enabled. Some tests disable updates. */ |
| 50 private static boolean sUpdatesEnabled = true; | 40 private static boolean sUpdatesEnabled = true; |
| 51 | 41 |
| 52 /** Data extracted from the WebAPK's launch intent and from the WebAPK's And
roid Manifest. */ | 42 /** Data extracted from the WebAPK's launch intent and from the WebAPK's And
roid Manifest. */ |
| 53 private WebApkInfo mInfo; | 43 private WebApkInfo mInfo; |
| 54 | 44 |
| 55 /** | 45 /** |
| 56 * The cached data for a pending update request which needs to be sent after
the WebAPK isn't | 46 * The cached data for a pending update request which needs to be sent after
the WebAPK isn't |
| 57 * running in the foreground. | 47 * running in the foreground. |
| 58 */ | 48 */ |
| 59 private PendingUpdate mPendingUpdate; | 49 private PendingUpdate mPendingUpdate; |
| 60 | 50 |
| 61 /** The WebApkActivity which owns the WebApkUpdateManager. */ | 51 /** The WebApkActivity which owns the WebApkUpdateManager. */ |
| 62 private final WebApkActivity mActivity; | 52 private final WebApkActivity mActivity; |
| 63 | 53 |
| 64 /** The WebappDataStorage with cached data about prior update requests. */ | 54 /** The WebappDataStorage with cached data about prior update requests. */ |
| 65 private WebappDataStorage mStorage; | 55 private WebappDataStorage mStorage; |
| 66 | 56 |
| 67 /** | |
| 68 * Whether the previous WebAPK update succeeded. True if there has not been
any update attempts. | |
| 69 */ | |
| 70 private boolean mPreviousUpdateSucceeded; | |
| 71 | |
| 72 private WebApkUpdateDataFetcher mFetcher; | 57 private WebApkUpdateDataFetcher mFetcher; |
| 73 | 58 |
| 74 /** | 59 /** |
| 75 * Contains all the data which is cached for a pending update request once t
he WebAPK is no | 60 * Contains all the data which is cached for a pending update request once t
he WebAPK is no |
| 76 * longer running foreground. | 61 * longer running foreground. |
| 77 */ | 62 */ |
| 78 private static class PendingUpdate { | 63 private static class PendingUpdate { |
| 79 public WebApkInfo mUpdateInfo; | 64 public WebApkInfo mUpdateInfo; |
| 80 public String mBestIconUrl; | 65 public String mBestIconUrl; |
| 81 public boolean mIsManifestStale; | 66 public boolean mIsManifestStale; |
| 82 | 67 |
| 83 public PendingUpdate(WebApkInfo info, String bestIconUrl, boolean isMani
festStale) { | 68 public PendingUpdate(WebApkInfo info, String bestIconUrl, boolean isMani
festStale) { |
| 84 mUpdateInfo = info; | 69 mUpdateInfo = info; |
| 85 mBestIconUrl = bestIconUrl; | 70 mBestIconUrl = bestIconUrl; |
| 86 mIsManifestStale = isManifestStale; | 71 mIsManifestStale = isManifestStale; |
| 87 } | 72 } |
| 88 } | 73 } |
| 89 | 74 |
| 90 public WebApkUpdateManager(WebApkActivity activity, WebappDataStorage storag
e) { | 75 public WebApkUpdateManager(WebApkActivity activity, WebappDataStorage storag
e) { |
| 91 mActivity = activity; | 76 mActivity = activity; |
| 92 mStorage = storage; | 77 mStorage = storage; |
| 93 } | 78 } |
| 94 | 79 |
| 95 /** | 80 /** |
| 96 * Checks whether the WebAPK's Web Manifest has changed. Requests an updated
WebAPK if the | 81 * Checks whether the WebAPK's Web Manifest has changed. Requests an updated
WebAPK if the Web |
| 97 * Web Manifest has changed. Skips the check if the check was done recently. | 82 * Manifest has changed. Skips the check if the check was done recently. |
| 98 * @param tab The tab of the WebAPK. | 83 * @param tab The tab of the WebAPK. |
| 99 * @param info The WebApkInfo of the WebAPK. | 84 * @param info The WebApkInfo of the WebAPK. |
| 100 */ | 85 */ |
| 101 public void updateIfNeeded(Tab tab, WebApkInfo info) { | 86 public void updateIfNeeded(Tab tab, WebApkInfo info) { |
| 102 mInfo = info; | 87 mInfo = info; |
| 103 mPreviousUpdateSucceeded = didPreviousUpdateSucceed(); | |
| 104 | 88 |
| 105 if (!shouldCheckIfWebManifestUpdated(mInfo, mPreviousUpdateSucceeded)) r
eturn; | 89 if (!shouldCheckIfWebManifestUpdated(mInfo)) return; |
| 106 | 90 |
| 107 mFetcher = buildFetcher(); | 91 mFetcher = buildFetcher(); |
| 108 mFetcher.start(tab, mInfo, this); | 92 mFetcher.start(tab, mInfo, this); |
| 109 } | 93 } |
| 110 | 94 |
| 111 /** | 95 /** |
| 112 * It sends the pending update request to the WebAPK server if exits. | 96 * It sends the pending update request to the WebAPK server if exits. |
| 113 * @return Whether a pending update request is sent to the WebAPK server. | 97 * @return Whether a pending update request is sent to the WebAPK server. |
| 114 */ | 98 */ |
| 115 public boolean requestPendingUpdate() { | 99 public boolean requestPendingUpdate() { |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 158 // developer. | 142 // developer. |
| 159 // | 143 // |
| 160 // If the Web Manifest was not found and an upgrade is not requested, ke
ep on fetching | 144 // If the Web Manifest was not found and an upgrade is not requested, ke
ep on fetching |
| 161 // Web Manifests as the user navigates. For instance, the WebAPK's start
_url might not | 145 // Web Manifests as the user navigates. For instance, the WebAPK's start
_url might not |
| 162 // point to a Web Manifest because start_url redirects to the WebAPK's m
ain page. | 146 // point to a Web Manifest because start_url redirects to the WebAPK's m
ain page. |
| 163 if (gotManifest || needsUpgrade) { | 147 if (gotManifest || needsUpgrade) { |
| 164 destroyFetcher(); | 148 destroyFetcher(); |
| 165 } | 149 } |
| 166 | 150 |
| 167 if (!needsUpgrade) { | 151 if (!needsUpgrade) { |
| 168 if (!mPreviousUpdateSucceeded) { | 152 if (!mStorage.didPreviousUpdateSucceed()) { |
| 169 recordUpdate(mStorage, true); | 153 recordUpdate(mStorage, true /* success */, false /* relaxUpdates
*/); |
| 170 } | 154 } |
| 171 return; | 155 return; |
| 172 } | 156 } |
| 173 | 157 |
| 174 // Set WebAPK update as having failed in case that Chrome is killed prio
r to | 158 // Set WebAPK update as having failed in case that Chrome is killed prio
r to |
| 175 // {@link onBuiltWebApk} being called. | 159 // {@link onBuiltWebApk} being called. |
| 176 recordUpdate(mStorage, false); | 160 recordUpdate(mStorage, false /* success */, false /* relaxUpdates*/); |
| 177 | 161 |
| 178 if (fetchedInfo != null) { | 162 if (fetchedInfo != null) { |
| 179 scheduleUpdate(fetchedInfo, bestIconUrl, false /* isManifestStale */
); | 163 scheduleUpdate(fetchedInfo, bestIconUrl, false /* isManifestStale */
); |
| 180 return; | 164 return; |
| 181 } | 165 } |
| 182 | 166 |
| 183 // Tell the server that the our version of the Web Manifest might be sta
le and to ignore | 167 // Tell the server that the our version of the Web Manifest might be sta
le and to ignore |
| 184 // our Web Manifest data if the server's Web Manifest data is newer. Thi
s scenario can | 168 // our Web Manifest data if the server's Web Manifest data is newer. Thi
s scenario can |
| 185 // occur if the Web Manifest is temporarily unreachable. | 169 // occur if the Web Manifest is temporarily unreachable. |
| 186 scheduleUpdate(mInfo, "", true /* isManifestStale */); | 170 scheduleUpdate(mInfo, "" /* bestIconUrl */, true /* isManifestStale */); |
| 187 } | 171 } |
| 188 | 172 |
| 189 /** | 173 /** |
| 190 * Builds {@link WebApkUpdateDataFetcher}. In a separate function for the sa
ke of tests. | 174 * Builds {@link WebApkUpdateDataFetcher}. In a separate function for the sa
ke of tests. |
| 191 */ | 175 */ |
| 192 protected WebApkUpdateDataFetcher buildFetcher() { | 176 protected WebApkUpdateDataFetcher buildFetcher() { |
| 193 return new WebApkUpdateDataFetcher(); | 177 return new WebApkUpdateDataFetcher(); |
| 194 } | 178 } |
| 195 | 179 |
| 196 /** | 180 /** |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 256 /** | 240 /** |
| 257 * Destroys {@link mFetcher}. In a separate function for the sake of tests. | 241 * Destroys {@link mFetcher}. In a separate function for the sake of tests. |
| 258 */ | 242 */ |
| 259 protected void destroyFetcher() { | 243 protected void destroyFetcher() { |
| 260 if (mFetcher == null) return; | 244 if (mFetcher == null) return; |
| 261 | 245 |
| 262 mFetcher.destroy(); | 246 mFetcher.destroy(); |
| 263 mFetcher = null; | 247 mFetcher = null; |
| 264 } | 248 } |
| 265 | 249 |
| 266 /** Returns the current time. In a separate function for the sake of testing
. */ | |
| 267 protected long currentTimeMillis() { | |
| 268 return System.currentTimeMillis(); | |
| 269 } | |
| 270 | |
| 271 /** | 250 /** |
| 272 * Reads the WebAPK's version code. Returns 0 on failure. | 251 * Reads the WebAPK's version code. Returns 0 on failure. |
| 273 */ | 252 */ |
| 274 private int readVersionCodeFromAndroidManifest(String webApkPackage) { | 253 private int readVersionCodeFromAndroidManifest(String webApkPackage) { |
| 275 try { | 254 try { |
| 276 PackageManager packageManager = | 255 PackageManager packageManager = |
| 277 ContextUtils.getApplicationContext().getPackageManager(); | 256 ContextUtils.getApplicationContext().getPackageManager(); |
| 278 PackageInfo packageInfo = packageManager.getPackageInfo(webApkPackag
e, 0); | 257 PackageInfo packageInfo = packageManager.getPackageInfo(webApkPackag
e, 0); |
| 279 return packageInfo.versionCode; | 258 return packageInfo.versionCode; |
| 280 } catch (PackageManager.NameNotFoundException e) { | 259 } catch (PackageManager.NameNotFoundException e) { |
| 281 e.printStackTrace(); | 260 e.printStackTrace(); |
| 282 } | 261 } |
| 283 return 0; | 262 return 0; |
| 284 } | 263 } |
| 285 | 264 |
| 286 /** | 265 /** |
| 287 * Returns whether the previous WebAPK update attempt succeeded. Returns tru
e if there has not | |
| 288 * been any update attempts. | |
| 289 */ | |
| 290 private boolean didPreviousUpdateSucceed() { | |
| 291 long lastUpdateCompletionTime = mStorage.getLastWebApkUpdateRequestCompl
etionTime(); | |
| 292 if (lastUpdateCompletionTime == WebappDataStorage.LAST_USED_INVALID | |
| 293 || lastUpdateCompletionTime == WebappDataStorage.LAST_USED_UNSET
) { | |
| 294 return true; | |
| 295 } | |
| 296 return mStorage.getDidLastWebApkUpdateRequestSucceed(); | |
| 297 } | |
| 298 | |
| 299 /** | |
| 300 * Whether there is a new version of the //chrome/android/webapk/shell_apk c
ode. | 266 * Whether there is a new version of the //chrome/android/webapk/shell_apk c
ode. |
| 301 */ | 267 */ |
| 302 private static boolean isShellApkVersionOutOfDate(WebApkInfo info) { | 268 private static boolean isShellApkVersionOutOfDate(WebApkInfo info) { |
| 303 return info.shellApkVersion() < WebApkVersion.CURRENT_SHELL_APK_VERSION; | 269 return info.shellApkVersion() < WebApkVersion.CURRENT_SHELL_APK_VERSION; |
| 304 } | 270 } |
| 305 | 271 |
| 306 /** | 272 /** |
| 307 * Returns whether the Web Manifest should be refetched to check whether it
has been updated. | 273 * Returns whether the Web Manifest should be refetched to check whether it
has been updated. |
| 308 * TODO: Make this method static once there is a static global clock class. | 274 * TODO: Make this method static once there is a static global clock class. |
| 309 * @param info Meta data from WebAPK's Android Manifest. | 275 * @param info Meta data from WebAPK's Android Manifest. |
| 310 * @param previousUpdateSucceeded Whether the previous update attempt succee
ded. | |
| 311 * True if there has not been any update attempts. | 276 * True if there has not been any update attempts. |
| 312 */ | 277 */ |
| 313 private boolean shouldCheckIfWebManifestUpdated(WebApkInfo info, | 278 private boolean shouldCheckIfWebManifestUpdated(WebApkInfo info) { |
| 314 boolean previousUpdateSucceeded) { | |
| 315 if (!sUpdatesEnabled) { | 279 if (!sUpdatesEnabled) { |
| 316 return false; | 280 return false; |
| 317 } | 281 } |
| 318 | 282 |
| 319 if (CommandLine.getInstance().hasSwitch( | 283 if (CommandLine.getInstance().hasSwitch( |
| 320 ChromeSwitches.CHECK_FOR_WEB_MANIFEST_UPDATE_ON_STARTUP)) { | 284 ChromeSwitches.CHECK_FOR_WEB_MANIFEST_UPDATE_ON_STARTUP)) { |
| 321 return true; | 285 return true; |
| 322 } | 286 } |
| 323 | 287 |
| 324 if (!ChromeWebApkHost.areUpdatesEnabled()) return false; | 288 if (!ChromeWebApkHost.areUpdatesEnabled()) return false; |
| 325 | 289 |
| 326 if (isShellApkVersionOutOfDate(info)) return true; | 290 if (isShellApkVersionOutOfDate(info)) return true; |
| 327 | 291 |
| 328 long now = currentTimeMillis(); | 292 return mStorage.shouldCheckForUpdate(); |
| 329 long sinceLastCheckDurationMs = now - mStorage.getLastCheckForWebManifes
tUpdateTime(); | |
| 330 if (sinceLastCheckDurationMs >= FULL_CHECK_UPDATE_INTERVAL) return true; | |
| 331 | |
| 332 long sinceLastUpdateRequestDurationMs = | |
| 333 now - mStorage.getLastWebApkUpdateRequestCompletionTime(); | |
| 334 return sinceLastUpdateRequestDurationMs >= RETRY_UPDATE_DURATION | |
| 335 && !previousUpdateSucceeded; | |
| 336 } | 293 } |
| 337 | 294 |
| 338 /** | 295 /** |
| 339 * Updates {@link WebappDataStorage} with the time of the latest WebAPK upda
te and whether the | 296 * Updates {@link WebappDataStorage} with the time of the latest WebAPK upda
te and whether the |
| 340 * WebAPK update succeeded. | 297 * WebAPK update succeeded. |
| 341 */ | 298 */ |
| 342 private static void recordUpdate(WebappDataStorage storage, boolean success)
{ | 299 private static void recordUpdate( |
| 300 WebappDataStorage storage, boolean success, boolean relaxUpdates) { |
| 343 // Update the request time and result together. It prevents getting a co
rrect request time | 301 // Update the request time and result together. It prevents getting a co
rrect request time |
| 344 // but a result from the previous request. | 302 // but a result from the previous request. |
| 345 storage.updateTimeOfLastWebApkUpdateRequestCompletion(); | 303 storage.updateTimeOfLastWebApkUpdateRequestCompletion(); |
| 346 storage.updateDidLastWebApkUpdateRequestSucceed(success); | 304 storage.updateDidLastWebApkUpdateRequestSucceed(success); |
| 305 storage.setRelaxedUpdates(relaxUpdates); |
| 347 } | 306 } |
| 348 | 307 |
| 349 /** | 308 /** |
| 350 * Checks whether the WebAPK needs to be updated. | 309 * Checks whether the WebAPK needs to be updated. |
| 351 * @param info Meta data from WebAPK's Android Manifest. | 310 * @param info Meta data from WebAPK's Android Manifest. |
| 352 * @param fetchedInfo Fetched data for Web Manifest. | 311 * @param fetchedInfo Fetched data for Web Manifest. |
| 353 * @param bestFetchedIconUrl The icon URL in {@link fetchedInfo#iconUrlToMur
mur2HashMap()} best | 312 * @param bestFetchedIconUrl The icon URL in {@link fetchedInfo#iconUrlToMur
mur2HashMap()} best |
| 354 * suited for use as the launcher icon on this dev
ice. | 313 * suited for use as the launcher icon on this dev
ice. |
| 355 */ | 314 */ |
| 356 private boolean needsUpdate(WebApkInfo info, WebApkInfo fetchedInfo, String
bestIconUrl) { | 315 private boolean needsUpdate(WebApkInfo info, WebApkInfo fetchedInfo, String
bestIconUrl) { |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 393 */ | 352 */ |
| 394 protected boolean urlsMatchIgnoringFragments(String url1, String url2) { | 353 protected boolean urlsMatchIgnoringFragments(String url1, String url2) { |
| 395 return UrlUtilities.urlsMatchIgnoringFragments(url1, url2); | 354 return UrlUtilities.urlsMatchIgnoringFragments(url1, url2); |
| 396 } | 355 } |
| 397 | 356 |
| 398 /** | 357 /** |
| 399 * Called after either a request to update the WebAPK has been sent or the u
pdate process | 358 * Called after either a request to update the WebAPK has been sent or the u
pdate process |
| 400 * fails. | 359 * fails. |
| 401 */ | 360 */ |
| 402 @CalledByNative | 361 @CalledByNative |
| 403 private static void onBuiltWebApk(String id, boolean success) { | 362 private static void onBuiltWebApk(String id, boolean success, boolean relaxU
pdates) { |
| 404 WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataSt
orage(id); | 363 WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataSt
orage(id); |
| 405 if (storage == null) return; | 364 if (storage == null) return; |
| 406 | 365 |
| 407 recordUpdate(storage, success); | 366 recordUpdate(storage, success, relaxUpdates); |
| 408 } | 367 } |
| 409 | 368 |
| 410 private static native void nativeUpdateAsync(String id, String startUrl, Str
ing scope, | 369 private static native void nativeUpdateAsync(String id, String startUrl, Str
ing scope, |
| 411 String name, String shortName, String bestIconUrl, Bitmap bestIcon,
String[] iconUrls, | 370 String name, String shortName, String bestIconUrl, Bitmap bestIcon,
String[] iconUrls, |
| 412 String[] iconHashes, int displayMode, int orientation, long themeCol
or, | 371 String[] iconHashes, int displayMode, int orientation, long themeCol
or, |
| 413 long backgroundColor, String manifestUrl, String webApkPackage, int
webApkVersion, | 372 long backgroundColor, String manifestUrl, String webApkPackage, int
webApkVersion, |
| 414 boolean isManifestStale); | 373 boolean isManifestStale); |
| 415 } | 374 } |
| OLD | NEW |