blob: 2cf17229a5bf5a169c34180a44058d362458a128 [file] [log] [blame]
Gustav Sennton79fea482016-04-07 14:22:56 +01001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.server.webkit;
17
18import android.content.Context;
19import android.content.pm.ApplicationInfo;
20import android.content.pm.PackageInfo;
21import android.content.pm.PackageManager.NameNotFoundException;
22import android.content.pm.Signature;
Gustav Sennton0df2c552016-06-14 15:32:19 +010023import android.os.UserHandle;
Gustav Sennton79fea482016-04-07 14:22:56 +010024import android.util.Base64;
25import android.util.Slog;
26import android.webkit.WebViewFactory;
27import android.webkit.WebViewProviderInfo;
28import android.webkit.WebViewProviderResponse;
29
30import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.List;
33
34/**
35 * Implementation of the WebViewUpdateService.
36 * This class doesn't depend on the android system like the actual Service does and can be used
37 * directly by tests (as long as they implement a SystemInterface).
38 * @hide
39 */
40public class WebViewUpdateServiceImpl {
41 private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName();
42
43 private SystemInterface mSystemInterface;
44 private WebViewUpdater mWebViewUpdater;
45 private Context mContext;
46
47 public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
48 mContext = context;
49 mSystemInterface = systemInterface;
50 mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface);
51 }
52
Gustav Sennton0df2c552016-06-14 15:32:19 +010053 void packageStateChanged(String packageName, int changedState, int userId) {
54 // We don't early out here in different cases where we could potentially early-out (e.g. if
55 // we receive PACKAGE_CHANGED for another user than the system user) since that would
56 // complicate this logic further and open up for more edge cases.
Gustav Sennton79fea482016-04-07 14:22:56 +010057 updateFallbackStateOnPackageChange(packageName, changedState);
58 mWebViewUpdater.packageStateChanged(packageName, changedState);
59 }
60
61 void prepareWebViewInSystemServer() {
62 updateFallbackStateOnBoot();
63 mWebViewUpdater.prepareWebViewInSystemServer();
64 }
65
66 private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
67 for (WebViewProviderInfo provider : providers) {
68 if (provider.availableByDefault && !provider.isFallback) {
69 try {
70 PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(provider);
Gustav Sennton0df2c552016-06-14 15:32:19 +010071 if (isInstalledPackage(packageInfo) && isEnabledPackage(packageInfo)
Gustav Sennton79fea482016-04-07 14:22:56 +010072 && mWebViewUpdater.isValidProvider(provider, packageInfo)) {
73 return true;
74 }
75 } catch (NameNotFoundException e) {
76 // A non-existent provider is neither valid nor enabled
77 }
78 }
79 }
80 return false;
81 }
82
83 /**
84 * Called when a new user has been added to update the state of its fallback package.
85 */
86 void handleNewUser(int userId) {
87 if (!mSystemInterface.isFallbackLogicEnabled()) return;
88
89 WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
90 WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
91 if (fallbackProvider == null) return;
92
93 mSystemInterface.enablePackageForUser(fallbackProvider.packageName,
94 !existsValidNonFallbackProvider(webviewProviders), userId);
95 }
96
97 void notifyRelroCreationCompleted() {
98 mWebViewUpdater.notifyRelroCreationCompleted();
99 }
100
101 WebViewProviderResponse waitForAndGetProvider() {
102 return mWebViewUpdater.waitForAndGetProvider();
103 }
104
105 String changeProviderAndSetting(String newProvider) {
106 return mWebViewUpdater.changeProviderAndSetting(newProvider);
107 }
108
109 WebViewProviderInfo[] getValidWebViewPackages() {
Gustav Sennton0df2c552016-06-14 15:32:19 +0100110 return mWebViewUpdater.getValidAndInstalledWebViewPackages();
Gustav Sennton79fea482016-04-07 14:22:56 +0100111 }
112
113 WebViewProviderInfo[] getWebViewPackages() {
114 return mSystemInterface.getWebViewPackages();
115 }
116
117 String getCurrentWebViewPackageName() {
118 return mWebViewUpdater.getCurrentWebViewPackageName();
119 }
120
121 void enableFallbackLogic(boolean enable) {
122 mSystemInterface.enableFallbackLogic(enable);
123 }
124
125 private void updateFallbackStateOnBoot() {
Gustav Sennton53b78242016-04-07 15:56:10 +0100126 if (!mSystemInterface.isFallbackLogicEnabled()) return;
127
Gustav Sennton79fea482016-04-07 14:22:56 +0100128 WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
129 updateFallbackState(webviewProviders, true);
130 }
131
132 /**
133 * Handle the enabled-state of our fallback package, i.e. if there exists some non-fallback
134 * package that is valid (and available by default) then disable the fallback package,
135 * otherwise, enable the fallback package.
136 */
137 private void updateFallbackStateOnPackageChange(String changedPackage, int changedState) {
138 if (!mSystemInterface.isFallbackLogicEnabled()) return;
139
140 WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
141
142 // A package was changed / updated / downgraded, early out if it is not one of the
143 // webview packages that are available by default.
144 boolean changedPackageAvailableByDefault = false;
145 for (WebViewProviderInfo provider : webviewProviders) {
146 if (provider.packageName.equals(changedPackage)) {
147 if (provider.availableByDefault) {
148 changedPackageAvailableByDefault = true;
149 }
150 break;
151 }
152 }
153 if (!changedPackageAvailableByDefault) return;
154 updateFallbackState(webviewProviders, false);
155 }
156
157 private void updateFallbackState(WebViewProviderInfo[] webviewProviders, boolean isBoot) {
158 // If there exists a valid and enabled non-fallback package - disable the fallback
159 // package, otherwise, enable it.
160 WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
161 if (fallbackProvider == null) return;
162 boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
163
164 boolean isFallbackEnabled = false;
165 try {
166 isFallbackEnabled = isEnabledPackage(
167 mSystemInterface.getPackageInfoForProvider(fallbackProvider));
168 } catch (NameNotFoundException e) {
Gustav Senntonff396f22016-04-08 18:33:30 +0100169 // No fallback package installed -> early out.
170 return;
Gustav Sennton79fea482016-04-07 14:22:56 +0100171 }
172
173 if (existsValidNonFallbackProvider
174 // During an OTA the primary user's WebView state might differ from other users', so
175 // ignore the state of that user during boot.
176 && (isFallbackEnabled || isBoot)) {
177 mSystemInterface.uninstallAndDisablePackageForAllUsers(mContext,
178 fallbackProvider.packageName);
179 } else if (!existsValidNonFallbackProvider
180 // During an OTA the primary user's WebView state might differ from other users', so
181 // ignore the state of that user during boot.
182 && (!isFallbackEnabled || isBoot)) {
183 // Enable the fallback package for all users.
184 mSystemInterface.enablePackageForAllUsers(mContext,
185 fallbackProvider.packageName, true);
186 }
187 }
188
189 /**
190 * Returns the only fallback provider in the set of given packages, or null if there is none.
191 */
192 private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
193 for (WebViewProviderInfo provider : webviewPackages) {
194 if (provider.isFallback) {
195 return provider;
196 }
197 }
198 return null;
199 }
200
201 boolean isFallbackPackage(String packageName) {
202 if (packageName == null || !mSystemInterface.isFallbackLogicEnabled()) return false;
203
204 WebViewProviderInfo[] webviewPackages = mSystemInterface.getWebViewPackages();
205 WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages);
206 return (fallbackProvider != null
207 && packageName.equals(fallbackProvider.packageName));
208 }
209
210 /**
211 * Class that decides what WebView implementation to use and prepares that implementation for
212 * use.
213 */
214 private static class WebViewUpdater {
215 private Context mContext;
216 private SystemInterface mSystemInterface;
217 private int mMinimumVersionCode = -1;
218
219 public WebViewUpdater(Context context, SystemInterface systemInterface) {
220 mContext = context;
221 mSystemInterface = systemInterface;
222 }
223
Gustav Senntonfd07efa2016-05-23 13:09:03 +0100224 private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
Gustav Sennton79fea482016-04-07 14:22:56 +0100225
226 // Keeps track of the number of running relro creations
227 private int mNumRelroCreationsStarted = 0;
228 private int mNumRelroCreationsFinished = 0;
229 // Implies that we need to rerun relro creation because we are using an out-of-date package
230 private boolean mWebViewPackageDirty = false;
231 private boolean mAnyWebViewInstalled = false;
232
233 private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
234
235 // The WebView package currently in use (or the one we are preparing).
236 private PackageInfo mCurrentWebViewPackage = null;
237
238 private Object mLock = new Object();
239
240 public void packageStateChanged(String packageName, int changedState) {
241 for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
242 String webviewPackage = provider.packageName;
243
244 if (webviewPackage.equals(packageName)) {
245 boolean updateWebView = false;
246 boolean removedOrChangedOldPackage = false;
247 String oldProviderName = null;
248 PackageInfo newPackage = null;
249 synchronized(mLock) {
250 try {
251 newPackage = findPreferredWebViewPackage();
Gustav Sennton2198a532016-04-12 16:38:28 +0100252 if (mCurrentWebViewPackage != null) {
Gustav Sennton79fea482016-04-07 14:22:56 +0100253 oldProviderName = mCurrentWebViewPackage.packageName;
Gustav Sennton2198a532016-04-12 16:38:28 +0100254 if (changedState == WebViewUpdateService.PACKAGE_CHANGED
255 && newPackage.packageName.equals(oldProviderName)) {
256 // If we don't change package name we should only rerun the
257 // preparation phase if the current package has been replaced
258 // (not if it has been enabled/disabled).
259 return;
260 }
Gustav Sennton0df2c552016-06-14 15:32:19 +0100261 if (newPackage.packageName.equals(oldProviderName)
262 && (newPackage.lastUpdateTime
263 == mCurrentWebViewPackage.lastUpdateTime)) {
264 // If the chosen package hasn't been updated, then early-out
265 return;
266 }
Gustav Sennton2198a532016-04-12 16:38:28 +0100267 }
Gustav Sennton79fea482016-04-07 14:22:56 +0100268 // Only trigger update actions if the updated package is the one
269 // that will be used, or the one that was in use before the
270 // update, or if we haven't seen a valid WebView package before.
271 updateWebView =
272 provider.packageName.equals(newPackage.packageName)
273 || provider.packageName.equals(oldProviderName)
274 || mCurrentWebViewPackage == null;
275 // We removed the old package if we received an intent to remove
276 // or replace the old package.
277 removedOrChangedOldPackage =
278 provider.packageName.equals(oldProviderName);
279 if (updateWebView) {
280 onWebViewProviderChanged(newPackage);
281 }
282 } catch (WebViewFactory.MissingWebViewPackageException e) {
283 Slog.e(TAG, "Could not find valid WebView package to create " +
284 "relro with " + e);
285 }
286 }
287 if(updateWebView && !removedOrChangedOldPackage
288 && oldProviderName != null) {
289 // If the provider change is the result of adding or replacing a
290 // package that was not the previous provider then we must kill
291 // packages dependent on the old package ourselves. The framework
292 // only kills dependents of packages that are being removed.
293 mSystemInterface.killPackageDependents(oldProviderName);
294 }
295 return;
296 }
297 }
298 }
299
300 public void prepareWebViewInSystemServer() {
301 try {
302 synchronized(mLock) {
303 mCurrentWebViewPackage = findPreferredWebViewPackage();
Gustav Senntona9159042016-04-11 16:32:52 +0100304 // Don't persist the user-chosen setting across boots if the package being
305 // chosen is not used (could be disabled or uninstalled) so that the user won't
306 // be surprised by the device switching to using a certain webview package,
307 // that was uninstalled/disabled a long time ago, if it is installed/enabled
308 // again.
309 mSystemInterface.updateUserSetting(mContext,
310 mCurrentWebViewPackage.packageName);
Gustav Sennton79fea482016-04-07 14:22:56 +0100311 onWebViewProviderChanged(mCurrentWebViewPackage);
312 }
313 } catch (Throwable t) {
314 // Log and discard errors at this stage as we must not crash the system server.
315 Slog.e(TAG, "error preparing webview provider from system server", t);
316 }
317 }
318
319 /**
320 * Change WebView provider and provider setting and kill packages using the old provider.
Gustav Sennton95f7e8e2016-04-14 15:07:09 +0100321 * Return the new provider (in case we are in the middle of creating relro files, or
322 * replacing that provider it will not be in use directly, but will be used when the relros
323 * or the replacement are done).
Gustav Sennton79fea482016-04-07 14:22:56 +0100324 */
325 public String changeProviderAndSetting(String newProviderName) {
326 PackageInfo oldPackage = null;
327 PackageInfo newPackage = null;
Gustav Sennton95f7e8e2016-04-14 15:07:09 +0100328 boolean providerChanged = false;
Gustav Sennton79fea482016-04-07 14:22:56 +0100329 synchronized(mLock) {
330 oldPackage = mCurrentWebViewPackage;
331 mSystemInterface.updateUserSetting(mContext, newProviderName);
332
333 try {
334 newPackage = findPreferredWebViewPackage();
Gustav Sennton95f7e8e2016-04-14 15:07:09 +0100335 providerChanged = (oldPackage == null)
336 || !newPackage.packageName.equals(oldPackage.packageName);
Gustav Sennton79fea482016-04-07 14:22:56 +0100337 } catch (WebViewFactory.MissingWebViewPackageException e) {
338 Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView " +
339 "package " + e);
340 // If we don't perform the user change but don't have an installed WebView
341 // package, we will have changed the setting and it will be used when a package
342 // is available.
Gustav Sennton95f7e8e2016-04-14 15:07:09 +0100343 return "";
Gustav Sennton79fea482016-04-07 14:22:56 +0100344 }
Gustav Sennton95f7e8e2016-04-14 15:07:09 +0100345 // Perform the provider change if we chose a new provider
346 if (providerChanged) {
347 onWebViewProviderChanged(newPackage);
348 }
Gustav Sennton79fea482016-04-07 14:22:56 +0100349 }
Gustav Sennton95f7e8e2016-04-14 15:07:09 +0100350 // Kill apps using the old provider only if we changed provider
351 if (providerChanged && oldPackage != null) {
Gustav Sennton79fea482016-04-07 14:22:56 +0100352 mSystemInterface.killPackageDependents(oldPackage.packageName);
353 }
Gustav Sennton95f7e8e2016-04-14 15:07:09 +0100354 // Return the new provider, this is not necessarily the one we were asked to switch to
355 // But the persistent setting will now be pointing to the provider we were asked to
356 // switch to anyway
Gustav Sennton79fea482016-04-07 14:22:56 +0100357 return newPackage.packageName;
358 }
359
360 /**
361 * This is called when we change WebView provider, either when the current provider is
362 * updated or a new provider is chosen / takes precedence.
363 */
364 private void onWebViewProviderChanged(PackageInfo newPackage) {
365 synchronized(mLock) {
366 mAnyWebViewInstalled = true;
367 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
368 mCurrentWebViewPackage = newPackage;
Gustav Sennton79fea482016-04-07 14:22:56 +0100369
370 // The relro creations might 'finish' (not start at all) before
371 // WebViewFactory.onWebViewProviderChanged which means we might not know the
372 // number of started creations before they finish.
373 mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
374 mNumRelroCreationsFinished = 0;
375 mNumRelroCreationsStarted =
376 mSystemInterface.onWebViewProviderChanged(newPackage);
377 // If the relro creations finish before we know the number of started creations
378 // we will have to do any cleanup/notifying here.
379 checkIfRelrosDoneLocked();
380 } else {
381 mWebViewPackageDirty = true;
382 }
383 }
384 }
385
Gustav Sennton0df2c552016-06-14 15:32:19 +0100386 private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos(boolean onlyInstalled) {
Gustav Sennton79fea482016-04-07 14:22:56 +0100387 WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
388 List<ProviderAndPackageInfo> providers = new ArrayList<>();
389 for(int n = 0; n < allProviders.length; n++) {
390 try {
391 PackageInfo packageInfo =
392 mSystemInterface.getPackageInfoForProvider(allProviders[n]);
Gustav Sennton0df2c552016-06-14 15:32:19 +0100393 if ((!onlyInstalled || isInstalledPackage(packageInfo))
394 && isValidProvider(allProviders[n], packageInfo)) {
Gustav Sennton79fea482016-04-07 14:22:56 +0100395 providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
396 }
397 } catch (NameNotFoundException e) {
398 // Don't add non-existent packages
399 }
400 }
401 return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
402 }
403
404 /**
405 * Fetch only the currently valid WebView packages.
406 **/
Gustav Sennton0df2c552016-06-14 15:32:19 +0100407 public WebViewProviderInfo[] getValidAndInstalledWebViewPackages() {
408 ProviderAndPackageInfo[] providersAndPackageInfos =
409 getValidWebViewPackagesAndInfos(true /* only fetch installed packages */);
Gustav Sennton79fea482016-04-07 14:22:56 +0100410 WebViewProviderInfo[] providers =
411 new WebViewProviderInfo[providersAndPackageInfos.length];
412 for(int n = 0; n < providersAndPackageInfos.length; n++) {
413 providers[n] = providersAndPackageInfos[n].provider;
414 }
415 return providers;
416 }
417
418
Hui Shu9455bd02016-04-08 13:25:26 -0700419 private static class ProviderAndPackageInfo {
Gustav Sennton79fea482016-04-07 14:22:56 +0100420 public final WebViewProviderInfo provider;
421 public final PackageInfo packageInfo;
422
423 public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
424 this.provider = provider;
425 this.packageInfo = packageInfo;
426 }
427 }
428
429 /**
430 * Returns either the package info of the WebView provider determined in the following way:
431 * If the user has chosen a provider then use that if it is valid,
432 * otherwise use the first package in the webview priority list that is valid.
433 *
434 */
435 private PackageInfo findPreferredWebViewPackage() {
Gustav Sennton0df2c552016-06-14 15:32:19 +0100436 ProviderAndPackageInfo[] providers =
437 getValidWebViewPackagesAndInfos(false /* onlyInstalled */);
Gustav Sennton79fea482016-04-07 14:22:56 +0100438
439 String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
440
441 // If the user has chosen provider, use that
442 for (ProviderAndPackageInfo providerAndPackage : providers) {
443 if (providerAndPackage.provider.packageName.equals(userChosenProvider)
Gustav Sennton0df2c552016-06-14 15:32:19 +0100444 && isInstalledPackage(providerAndPackage.packageInfo)
Gustav Sennton79fea482016-04-07 14:22:56 +0100445 && isEnabledPackage(providerAndPackage.packageInfo)) {
446 return providerAndPackage.packageInfo;
447 }
448 }
449
450 // User did not choose, or the choice failed; use the most stable provider that is
Gustav Sennton0df2c552016-06-14 15:32:19 +0100451 // installed and enabled for the device owner, and available by default (not through
452 // user choice).
Gustav Sennton79fea482016-04-07 14:22:56 +0100453 for (ProviderAndPackageInfo providerAndPackage : providers) {
454 if (providerAndPackage.provider.availableByDefault
Gustav Sennton0df2c552016-06-14 15:32:19 +0100455 && isInstalledPackage(providerAndPackage.packageInfo)
Gustav Sennton79fea482016-04-07 14:22:56 +0100456 && isEnabledPackage(providerAndPackage.packageInfo)) {
457 return providerAndPackage.packageInfo;
458 }
459 }
460
Gustav Sennton0df2c552016-06-14 15:32:19 +0100461 // Could not find any installed and enabled package either, use the most stable and
462 // default-available provider.
Gustav Sennton79fea482016-04-07 14:22:56 +0100463 for (ProviderAndPackageInfo providerAndPackage : providers) {
Gustav Senntona9159042016-04-11 16:32:52 +0100464 if (providerAndPackage.provider.availableByDefault) {
465 return providerAndPackage.packageInfo;
466 }
Gustav Sennton79fea482016-04-07 14:22:56 +0100467 }
468
469 mAnyWebViewInstalled = false;
470 throw new WebViewFactory.MissingWebViewPackageException(
471 "Could not find a loadable WebView package");
472 }
473
474 public void notifyRelroCreationCompleted() {
475 synchronized (mLock) {
476 mNumRelroCreationsFinished++;
477 checkIfRelrosDoneLocked();
478 }
479 }
480
481 public WebViewProviderResponse waitForAndGetProvider() {
482 PackageInfo webViewPackage = null;
483 final long NS_PER_MS = 1000000;
484 final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
485 boolean webViewReady = false;
486 int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
487 synchronized (mLock) {
488 webViewReady = webViewIsReadyLocked();
489 while (!webViewReady) {
490 final long timeNowMs = System.nanoTime() / NS_PER_MS;
491 if (timeNowMs >= timeoutTimeMs) break;
492 try {
493 mLock.wait(timeoutTimeMs - timeNowMs);
494 } catch (InterruptedException e) {}
495 webViewReady = webViewIsReadyLocked();
496 }
497 // Make sure we return the provider that was used to create the relro file
498 webViewPackage = mCurrentWebViewPackage;
499 if (webViewReady) {
500 } else if (!mAnyWebViewInstalled) {
501 webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
502 } else {
503 // Either the current relro creation isn't done yet, or the new relro creatioin
504 // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
505 webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
Gustav Senntonfd07efa2016-05-23 13:09:03 +0100506 Slog.e(TAG, "Timed out waiting for relro creation, relros started "
507 + mNumRelroCreationsStarted
508 + " relros finished " + mNumRelroCreationsFinished
509 + " package dirty? " + mWebViewPackageDirty);
Gustav Sennton79fea482016-04-07 14:22:56 +0100510 }
511 }
512 if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
513 return new WebViewProviderResponse(webViewPackage, webViewStatus);
514 }
515
516 public String getCurrentWebViewPackageName() {
517 synchronized(mLock) {
518 if (mCurrentWebViewPackage == null)
519 return null;
520 return mCurrentWebViewPackage.packageName;
521 }
522 }
523
524 /**
525 * Returns whether WebView is ready and is not going to go through its preparation phase
526 * again directly.
527 */
528 private boolean webViewIsReadyLocked() {
529 return !mWebViewPackageDirty
530 && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
531 // The current package might be replaced though we haven't received an intent
532 // declaring this yet, the following flag makes anyone loading WebView to wait in
533 // this case.
534 && mAnyWebViewInstalled;
535 }
536
537 private void checkIfRelrosDoneLocked() {
538 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
539 if (mWebViewPackageDirty) {
540 mWebViewPackageDirty = false;
541 // If we have changed provider since we started the relro creation we need to
542 // redo the whole process using the new package instead.
Gustav Sennton53b78242016-04-07 15:56:10 +0100543 try {
544 PackageInfo newPackage = findPreferredWebViewPackage();
545 onWebViewProviderChanged(newPackage);
546 } catch (WebViewFactory.MissingWebViewPackageException e) {
547 // If we can't find any valid WebView package we are now in a state where
548 // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
549 // should simply wait until we receive an intent declaring a new package was
550 // installed.
551 }
Gustav Sennton79fea482016-04-07 14:22:56 +0100552 } else {
553 mLock.notifyAll();
554 }
555 }
556 }
557
558 /**
Hui Shub5f554a2016-04-20 17:17:44 -0700559 * Both versionCodes should be from a WebView provider package implemented by Chromium.
560 * VersionCodes from other kinds of packages won't make any sense in this method.
561 *
562 * An introduction to Chromium versionCode scheme:
563 * "BBBBPPPAX"
564 * BBBB: 4 digit branch number. It monotonically increases over time.
565 * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits may
566 * change their meaning in the future.
567 * A: architecture digit.
568 * X: A digit to differentiate APKs for other reasons.
569 *
570 * This method takes the "BBBB" of versionCodes and compare them.
571 *
572 * @return true if versionCode1 is higher than or equal to versionCode2.
573 */
574 private static boolean versionCodeGE(int versionCode1, int versionCode2) {
575 int v1 = versionCode1 / 100000;
576 int v2 = versionCode2 / 100000;
577
578 return v1 >= v2;
579 }
580
581 /**
Gustav Sennton79fea482016-04-07 14:22:56 +0100582 * Returns whether this provider is valid for use as a WebView provider.
583 */
584 public boolean isValidProvider(WebViewProviderInfo configInfo,
585 PackageInfo packageInfo) {
Hui Shub5f554a2016-04-20 17:17:44 -0700586 if (!versionCodeGE(packageInfo.versionCode, getMinimumVersionCode())
Gustav Sennton79fea482016-04-07 14:22:56 +0100587 && !mSystemInterface.systemIsDebuggable()) {
Hui Shub5f554a2016-04-20 17:17:44 -0700588 // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
589 // minimum version code. This check is only enforced for user builds.
Gustav Sennton79fea482016-04-07 14:22:56 +0100590 return false;
591 }
592 if (providerHasValidSignature(configInfo, packageInfo, mSystemInterface) &&
593 WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) {
594 return true;
595 }
596 return false;
597 }
598
599 /**
600 * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
601 * of all available-by-default and non-fallback WebView provider packages. If there is no
602 * such WebView provider package on the system, then return -1, which means all positive
603 * versionCode WebView packages are accepted.
604 */
605 private int getMinimumVersionCode() {
606 if (mMinimumVersionCode > 0) {
607 return mMinimumVersionCode;
608 }
609
610 for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
611 if (provider.availableByDefault && !provider.isFallback) {
612 try {
613 int versionCode =
614 mSystemInterface.getFactoryPackageVersion(provider.packageName);
615 if (mMinimumVersionCode < 0 || versionCode < mMinimumVersionCode) {
616 mMinimumVersionCode = versionCode;
617 }
618 } catch (NameNotFoundException e) {
619 // Safe to ignore.
620 }
621 }
622 }
623
624 return mMinimumVersionCode;
625 }
626 }
627
628 private static boolean providerHasValidSignature(WebViewProviderInfo provider,
629 PackageInfo packageInfo, SystemInterface systemInterface) {
630 if (systemInterface.systemIsDebuggable()) {
631 return true;
632 }
633 Signature[] packageSignatures;
634 // If no signature is declared, instead check whether the package is included in the
635 // system.
636 if (provider.signatures == null || provider.signatures.length == 0) {
637 return packageInfo.applicationInfo.isSystemApp();
638 }
639 packageSignatures = packageInfo.signatures;
640 if (packageSignatures.length != 1)
641 return false;
642
643 final byte[] packageSignature = packageSignatures[0].toByteArray();
644 // Return whether the package signature matches any of the valid signatures
645 for (String signature : provider.signatures) {
646 final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
647 if (Arrays.equals(packageSignature, validSignature))
648 return true;
649 }
650 return false;
651 }
652
653 /**
654 * Returns whether the given package is enabled.
655 * This state can be changed by the user from Settings->Apps
656 */
657 private static boolean isEnabledPackage(PackageInfo packageInfo) {
658 return packageInfo.applicationInfo.enabled;
659 }
660
Gustav Sennton0df2c552016-06-14 15:32:19 +0100661 /**
662 * Return true if the package is installed and not hidden
663 */
664 private static boolean isInstalledPackage(PackageInfo packageInfo) {
665 return (((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0)
666 && ((packageInfo.applicationInfo.privateFlags
667 & ApplicationInfo.PRIVATE_FLAG_HIDDEN) == 0));
668 }
669
Gustav Sennton79fea482016-04-07 14:22:56 +0100670}