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

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

Issue 2460253002: Update WebAPKs even if the WebAPK start URL has no Web Manifest part 2/3 (Closed)
Patch Set: Merge branch 'master' into update_fail_refactor0 Created 4 years 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.ContentResolver; 7 import android.content.ContentResolver;
8 import android.content.pm.PackageInfo; 8 import android.content.pm.PackageInfo;
9 import android.content.pm.PackageManager; 9 import android.content.pm.PackageManager;
10 import android.graphics.Bitmap; 10 import android.graphics.Bitmap;
11 import android.provider.Settings; 11 import android.provider.Settings;
12 import android.text.TextUtils;
12 13
13 import org.chromium.base.CommandLine; 14 import org.chromium.base.CommandLine;
14 import org.chromium.base.ContextUtils; 15 import org.chromium.base.ContextUtils;
15 import org.chromium.base.Log; 16 import org.chromium.base.Log;
16 import org.chromium.base.annotations.CalledByNative; 17 import org.chromium.base.annotations.CalledByNative;
17 import org.chromium.chrome.browser.ChromeSwitches; 18 import org.chromium.chrome.browser.ChromeSwitches;
18 import org.chromium.chrome.browser.tab.Tab; 19 import org.chromium.chrome.browser.tab.Tab;
20 import org.chromium.chrome.browser.util.UrlUtilities;
19 import org.chromium.webapk.lib.client.WebApkVersion; 21 import org.chromium.webapk.lib.client.WebApkVersion;
20 22
23 import java.util.Map;
21 import java.util.concurrent.TimeUnit; 24 import java.util.concurrent.TimeUnit;
22 25
23 /** 26 /**
24 * 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
25 * 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.
26 */ 29 */
27 public class WebApkUpdateManager implements ManifestUpgradeDetector.Callback { 30 public class WebApkUpdateManager implements WebApkUpdateDataFetcher.Observer {
28 private static final String TAG = "WebApkUpdateManager"; 31 private static final String TAG = "WebApkUpdateManager";
29 32
30 /** Number of milliseconds between checks for whether the WebAPK's Web Manif est has changed. */ 33 /** Number of milliseconds between checks for whether the WebAPK's Web Manif est has changed. */
31 public static final long FULL_CHECK_UPDATE_INTERVAL = TimeUnit.DAYS.toMillis (3L); 34 public static final long FULL_CHECK_UPDATE_INTERVAL = TimeUnit.DAYS.toMillis (3L);
32 35
33 /** 36 /**
34 * Number of milliseconds to wait before re-requesting an updated WebAPK fro m the WebAPK 37 * Number of milliseconds to wait before re-requesting an updated WebAPK fro m the WebAPK
35 * server if the previous update attempt failed. 38 * server if the previous update attempt failed.
36 */ 39 */
37 public static final long RETRY_UPDATE_DURATION = TimeUnit.HOURS.toMillis(12L ); 40 public static final long RETRY_UPDATE_DURATION = TimeUnit.HOURS.toMillis(12L );
38 41
39 /** 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. */
40 private WebApkInfo mInfo; 43 private WebApkInfo mInfo;
41 44
42 /** 45 /**
43 * Whether the previous WebAPK update succeeded. True if there has not been any update attempts. 46 * Whether the previous WebAPK update succeeded. True if there has not been any update attempts.
44 */ 47 */
45 private boolean mPreviousUpdateSucceeded; 48 private boolean mPreviousUpdateSucceeded;
46 49
47 private ManifestUpgradeDetector mUpgradeDetector; 50 private WebApkUpdateDataFetcher mFetcher;
48 51
49 /** 52 /**
50 * Checks whether the WebAPK's Web Manifest has changed. Requests an updated WebAPK if the 53 * Checks whether the WebAPK's Web Manifest has changed. Requests an updated WebAPK if the
51 * Web Manifest has changed. Skips the check if the check was done recently. 54 * Web Manifest has changed. Skips the check if the check was done recently.
52 * @param tab The tab of the WebAPK. 55 * @param tab The tab of the WebAPK.
53 * @param info The WebApkInfo of the WebAPK. 56 * @param info The WebApkInfo of the WebAPK.
54 */ 57 */
55 public void updateIfNeeded(Tab tab, WebApkInfo info) { 58 public void updateIfNeeded(Tab tab, WebApkInfo info) {
56 mInfo = info; 59 mInfo = info;
57 60
58 WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataSt orage(mInfo.id()); 61 WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataSt orage(mInfo.id());
59 mPreviousUpdateSucceeded = didPreviousUpdateSucceed(storage); 62 mPreviousUpdateSucceeded = didPreviousUpdateSucceed(storage);
60 63
61 if (!shouldCheckIfWebManifestUpdated(storage, mInfo, mPreviousUpdateSucc eeded)) return; 64 if (!shouldCheckIfWebManifestUpdated(storage, mInfo, mPreviousUpdateSucc eeded)) return;
62 65
63 mUpgradeDetector = buildManifestUpgradeDetector(tab, mInfo); 66 mFetcher = buildFetcher();
64 mUpgradeDetector.start(); 67 mFetcher.start(tab, mInfo, this);
65 } 68 }
66 69
67 public void destroy() { 70 public void destroy() {
68 destroyUpgradeDetector(); 71 destroyFetcher();
69 } 72 }
70 73
71 @Override 74 @Override
72 public void onFinishedFetchingWebManifestForInitialUrl( 75 public void onFinishedFetchingWebManifestForInitialUrl(
73 boolean needsUpgrade, WebApkInfo info, String bestIconUrl) { 76 WebApkInfo fetchedInfo, String bestIconUrl) {
74 onGotManifestData(needsUpgrade, info, bestIconUrl); 77 onGotManifestData(fetchedInfo, bestIconUrl);
75 } 78 }
76 79
77 @Override 80 @Override
78 public void onGotManifestData(boolean needsUpgrade, WebApkInfo info, String bestIconUrl) { 81 public void onGotManifestData(WebApkInfo fetchedInfo, String bestIconUrl) {
79 WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataSt orage(mInfo.id()); 82 WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataSt orage(mInfo.id());
80 storage.updateTimeOfLastCheckForUpdatedWebManifest(); 83 storage.updateTimeOfLastCheckForUpdatedWebManifest();
81 84
82 boolean gotManifest = (info != null); 85 boolean gotManifest = (fetchedInfo != null);
83 needsUpgrade |= isShellApkVersionOutOfDate(mInfo); 86 boolean needsUpgrade = isShellApkVersionOutOfDate(mInfo)
87 || (gotManifest && needsUpdate(mInfo, fetchedInfo, bestIconUrl)) ;
84 Log.v(TAG, "Got Manifest: " + gotManifest); 88 Log.v(TAG, "Got Manifest: " + gotManifest);
85 Log.v(TAG, "WebAPK upgrade needed: " + needsUpgrade); 89 Log.v(TAG, "WebAPK upgrade needed: " + needsUpgrade);
86 90
87 // If the Web Manifest was not found and an upgrade is requested, stop f etching Web 91 // If the Web Manifest was not found and an upgrade is requested, stop f etching Web
88 // Manifests as the user navigates to avoid sending multiple WebAPK upda te requests. In 92 // Manifests as the user navigates to avoid sending multiple WebAPK upda te requests. In
89 // particular: 93 // particular:
90 // - A WebAPK update request on the initial load because the Shell APK v ersion is out of 94 // - A WebAPK update request on the initial load because the Shell APK v ersion is out of
91 // date. 95 // date.
92 // - A second WebAPK update request once the user navigates to a page wh ich points to the 96 // - A second WebAPK update request once the user navigates to a page wh ich points to the
93 // correct Web Manifest URL because the Web Manifest has been updated by the Web 97 // correct Web Manifest URL because the Web Manifest has been updated by the Web
94 // developer. 98 // developer.
95 // 99 //
96 // If the Web Manifest was not found and an upgrade is not requested, ke ep on fetching 100 // If the Web Manifest was not found and an upgrade is not requested, ke ep on fetching
97 // Web Manifests as the user navigates. For instance, the WebAPK's start _url might not 101 // Web Manifests as the user navigates. For instance, the WebAPK's start _url might not
98 // point to a Web Manifest because start_url redirects to the WebAPK's m ain page. 102 // point to a Web Manifest because start_url redirects to the WebAPK's m ain page.
99 if (gotManifest || needsUpgrade) { 103 if (gotManifest || needsUpgrade) {
100 destroyUpgradeDetector(); 104 destroyFetcher();
101 } 105 }
102 106
103 if (!needsUpgrade) { 107 if (!needsUpgrade) {
104 if (!mPreviousUpdateSucceeded) { 108 if (!mPreviousUpdateSucceeded) {
105 recordUpdate(storage, true); 109 recordUpdate(storage, true);
106 } 110 }
107 return; 111 return;
108 } 112 }
109 113
110 // Set WebAPK update as having failed in case that Chrome is killed prio r to 114 // Set WebAPK update as having failed in case that Chrome is killed prio r to
111 // {@link onBuiltWebApk} being called. 115 // {@link onBuiltWebApk} being called.
112 recordUpdate(storage, false); 116 recordUpdate(storage, false);
113 117
114 if (info != null) { 118 if (fetchedInfo != null) {
115 updateAsync(info, bestIconUrl); 119 updateAsync(fetchedInfo, bestIconUrl);
116 return; 120 return;
117 } 121 }
118 122
119 // Since we could not fetch the Web Manifest, we do not know what the be st icon URL is. Pass 123 // Since we could not fetch the Web Manifest, we do not know what the be st icon URL is. Pass
120 // an empty "best icon URL" to tell the server that there is no best ico n URL. 124 // an empty "best icon URL" to tell the server that there is no best ico n URL.
121 updateAsync(mInfo, ""); 125 updateAsync(mInfo, "");
122 } 126 }
123 127
124 /** 128 /**
125 * Builds {@link ManifestUpgradeDetector}. In a separate function for the sa ke of tests. 129 * Builds {@link WebApkUpdateDataFetcher}. In a separate function for the sa ke of tests.
126 */ 130 */
127 protected ManifestUpgradeDetector buildManifestUpgradeDetector(Tab tab, WebA pkInfo info) { 131 protected WebApkUpdateDataFetcher buildFetcher() {
128 return new ManifestUpgradeDetector(tab, info, this); 132 return new WebApkUpdateDataFetcher();
129 } 133 }
130 134
131 /** 135 /**
132 * Sends request to WebAPK Server to update WebAPK. 136 * Sends request to WebAPK Server to update WebAPK.
133 */ 137 */
134 protected void updateAsync(WebApkInfo info, String bestIconUrl) { 138 protected void updateAsync(WebApkInfo info, String bestIconUrl) {
135 int versionCode = readVersionCodeFromAndroidManifest(info.webApkPackageN ame()); 139 int versionCode = readVersionCodeFromAndroidManifest(info.webApkPackageN ame());
136 String bestIconMurmur2Hash = info.iconUrlToMurmur2HashMap().get(bestIcon Url); 140 String bestIconMurmur2Hash = info.iconUrlToMurmur2HashMap().get(bestIcon Url);
137 nativeUpdateAsync(info.id(), info.manifestStartUrl(), info.scopeUri().to String(), 141 nativeUpdateAsync(info.id(), info.manifestStartUrl(), info.scopeUri().to String(),
138 info.name(), info.shortName(), bestIconUrl, bestIconMurmur2Hash, info.icon(), 142 info.name(), info.shortName(), bestIconUrl, bestIconMurmur2Hash, info.icon(),
139 info.iconUrlToMurmur2HashMap().keySet().toArray(new String[0]), info.displayMode(), 143 info.iconUrlToMurmur2HashMap().keySet().toArray(new String[0]), info.displayMode(),
140 info.orientation(), info.themeColor(), info.backgroundColor(), i nfo.manifestUrl(), 144 info.orientation(), info.themeColor(), info.backgroundColor(), i nfo.manifestUrl(),
141 info.webApkPackageName(), versionCode); 145 info.webApkPackageName(), versionCode);
142 } 146 }
143 147
144 /** 148 /**
145 * Destroys {@link mUpgradeDetector}. In a separate function for the sake of tests. 149 * Destroys {@link mFetcher}. In a separate function for the sake of tests.
146 */ 150 */
147 protected void destroyUpgradeDetector() { 151 protected void destroyFetcher() {
148 if (mUpgradeDetector == null) return; 152 if (mFetcher == null) return;
149 153
150 mUpgradeDetector.destroy(); 154 mFetcher.destroy();
151 mUpgradeDetector = null; 155 mFetcher = null;
152 } 156 }
153 157
154 /** Returns the current time. In a separate function for the sake of testing . */ 158 /** Returns the current time. In a separate function for the sake of testing . */
155 protected long currentTimeMillis() { 159 protected long currentTimeMillis() {
156 return System.currentTimeMillis(); 160 return System.currentTimeMillis();
157 } 161 }
158 162
159 /** 163 /**
160 * Reads the WebAPK's version code. Returns 0 on failure. 164 * Reads the WebAPK's version code. Returns 0 on failure.
161 */ 165 */
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
194 /** 198 /**
195 * Returns whether the Web Manifest should be refetched to check whether it has been updated. 199 * Returns whether the Web Manifest should be refetched to check whether it has been updated.
196 * TODO: Make this method static once there is a static global clock class. 200 * TODO: Make this method static once there is a static global clock class.
197 * @param storage WebappDataStorage with the WebAPK's cached data. 201 * @param storage WebappDataStorage with the WebAPK's cached data.
198 * @param info Meta data from WebAPK's Android Manifest. 202 * @param info Meta data from WebAPK's Android Manifest.
199 * @param previousUpdateSucceeded Whether the previous update attempt succee ded. 203 * @param previousUpdateSucceeded Whether the previous update attempt succee ded.
200 * True if there has not been any update attempts. 204 * True if there has not been any update attempts.
201 */ 205 */
202 private boolean shouldCheckIfWebManifestUpdated( 206 private boolean shouldCheckIfWebManifestUpdated(
203 WebappDataStorage storage, WebApkInfo info, boolean previousUpdateSu cceeded) { 207 WebappDataStorage storage, WebApkInfo info, boolean previousUpdateSu cceeded) {
208 if (CommandLine.getInstance().hasSwitch(
209 ChromeSwitches.CHECK_FOR_WEB_MANIFEST_UPDATE_ON_STARTUP)) {
210 return true;
211 }
212
204 // Updating WebAPKs requires "installation from unknown sources" to be e nabled. It is 213 // Updating WebAPKs requires "installation from unknown sources" to be e nabled. It is
205 // confusing for a user to see a dialog asking them to enable "installat ion from unknown 214 // confusing for a user to see a dialog asking them to enable "installat ion from unknown
206 // sources" when they are in the middle of using the WebAPK (as opposed to after requesting 215 // sources" when they are in the middle of using the WebAPK (as opposed to after requesting
207 // to add a WebAPK to the homescreen). 216 // to add a WebAPK to the homescreen).
208 if (!installingFromUnknownSourcesAllowed()) { 217 if (!installingFromUnknownSourcesAllowed()) {
209 return false; 218 return false;
210 } 219 }
211 220
212 if (CommandLine.getInstance().hasSwitch(
213 ChromeSwitches.CHECK_FOR_WEB_MANIFEST_UPDATE_ON_STARTUP)) {
214 return true;
215 }
216
217 if (isShellApkVersionOutOfDate(info)) return true; 221 if (isShellApkVersionOutOfDate(info)) return true;
218 222
219 long now = currentTimeMillis(); 223 long now = currentTimeMillis();
220 long sinceLastCheckDurationMs = now - storage.getLastCheckForWebManifest UpdateTime(); 224 long sinceLastCheckDurationMs = now - storage.getLastCheckForWebManifest UpdateTime();
221 if (sinceLastCheckDurationMs >= FULL_CHECK_UPDATE_INTERVAL) return true; 225 if (sinceLastCheckDurationMs >= FULL_CHECK_UPDATE_INTERVAL) return true;
222 226
223 long sinceLastUpdateRequestDurationMs = 227 long sinceLastUpdateRequestDurationMs =
224 now - storage.getLastWebApkUpdateRequestCompletionTime(); 228 now - storage.getLastWebApkUpdateRequestCompletionTime();
225 return sinceLastUpdateRequestDurationMs >= RETRY_UPDATE_DURATION 229 return sinceLastUpdateRequestDurationMs >= RETRY_UPDATE_DURATION
226 && !previousUpdateSucceeded; 230 && !previousUpdateSucceeded;
(...skipping 19 matching lines...) Expand all
246 try { 250 try {
247 int setting = Settings.Secure.getInt( 251 int setting = Settings.Secure.getInt(
248 contentResolver, Settings.Secure.INSTALL_NON_MARKET_APPS); 252 contentResolver, Settings.Secure.INSTALL_NON_MARKET_APPS);
249 return setting == 1; 253 return setting == 1;
250 } catch (Settings.SettingNotFoundException e) { 254 } catch (Settings.SettingNotFoundException e) {
251 return false; 255 return false;
252 } 256 }
253 } 257 }
254 258
255 /** 259 /**
260 * Checks whether the WebAPK needs to be updated.
261 * @param info Meta data from WebAPK's Android Manifest.
262 * @param fetchedInfo Fetched data for Web Manifest.
263 * @param bestFetchedIconUrl The icon URL in {@link fetchedInfo#iconUrlToMur mur2HashMap()} best
264 * suited for use as the launcher icon on this dev ice.
265 */
266 private boolean needsUpdate(WebApkInfo info, WebApkInfo fetchedInfo, String bestIconUrl) {
267 // We should have computed the Murmur2 hash for the bitmap at the best i con URL for
268 // {@link fetchedInfo} (but not the other icon URLs.)
269 String fetchedBestIconMurmur2Hash = fetchedInfo.iconUrlToMurmur2HashMap( ).get(bestIconUrl);
270 String bestIconMurmur2Hash =
271 findMurmur2HashForUrlIgnoringFragment(mInfo.iconUrlToMurmur2Hash Map(), bestIconUrl);
272
273 return !TextUtils.equals(bestIconMurmur2Hash, fetchedBestIconMurmur2Hash )
274 || !urlsMatchIgnoringFragments(
275 mInfo.scopeUri().toString(), fetchedInfo.scopeUri().t oString())
276 || !urlsMatchIgnoringFragments(
277 mInfo.manifestStartUrl(), fetchedInfo.manifestStartUr l())
278 || !TextUtils.equals(mInfo.shortName(), fetchedInfo.shortName())
279 || !TextUtils.equals(mInfo.name(), fetchedInfo.name())
280 || mInfo.backgroundColor() != fetchedInfo.backgroundColor()
281 || mInfo.themeColor() != fetchedInfo.themeColor()
282 || mInfo.orientation() != fetchedInfo.orientation()
283 || mInfo.displayMode() != fetchedInfo.displayMode();
284 }
285
286 /**
287 * Returns the Murmur2 hash for entry in {@link iconUrlToMurmur2HashMap} who se canonical
288 * representation, ignoring fragments, matches {@link iconUrlToMatch}.
289 */
290 private String findMurmur2HashForUrlIgnoringFragment(
291 Map<String, String> iconUrlToMurmur2HashMap, String iconUrlToMatch) {
292 for (Map.Entry<String, String> entry : iconUrlToMurmur2HashMap.entrySet( )) {
293 if (urlsMatchIgnoringFragments(entry.getKey(), iconUrlToMatch)) {
294 return entry.getValue();
295 }
296 }
297 return null;
298 }
299
300 /**
301 * Returns whether the urls match ignoring fragments. Canonicalizes the URLs prior to doing the
302 * comparison.
303 */
304 protected boolean urlsMatchIgnoringFragments(String url1, String url2) {
305 return UrlUtilities.urlsMatchIgnoringFragments(url1, url2);
306 }
307
308 /**
256 * Called after either a request to update the WebAPK has been sent or the u pdate process 309 * Called after either a request to update the WebAPK has been sent or the u pdate process
257 * fails. 310 * fails.
258 */ 311 */
259 @CalledByNative 312 @CalledByNative
260 private static void onBuiltWebApk(String id, boolean success) { 313 private static void onBuiltWebApk(String id, boolean success) {
261 WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataSt orage(id); 314 WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataSt orage(id);
262 recordUpdate(storage, success); 315 recordUpdate(storage, success);
263 } 316 }
264 317
265 private static native void nativeUpdateAsync(String id, String startUrl, Str ing scope, 318 private static native void nativeUpdateAsync(String id, String startUrl, Str ing scope,
266 String name, String shortName, String bestIconUrl, String bestIconMu rmur2Hash, 319 String name, String shortName, String bestIconUrl, String bestIconMu rmur2Hash,
267 Bitmap bestIcon, String[] iconUrls, int displayMode, int orientation , long themeColor, 320 Bitmap bestIcon, String[] iconUrls, int displayMode, int orientation , long themeColor,
268 long backgroundColor, String manifestUrl, String webApkPackage, int webApkVersion); 321 long backgroundColor, String manifestUrl, String webApkPackage, int webApkVersion);
269 } 322 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698