Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(198)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java

Issue 2641973003: Implement server-suggested update check backoff (Closed)
Patch Set: Nits. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698