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 |