• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
16 package com.android.server.webkit;
17 
18 import android.annotation.Nullable;
19 import android.content.pm.PackageInfo;
20 import android.content.pm.PackageManager.NameNotFoundException;
21 import android.content.pm.Signature;
22 import android.os.AsyncTask;
23 import android.os.Trace;
24 import android.os.UserHandle;
25 import android.text.TextUtils;
26 import android.util.AndroidRuntimeException;
27 import android.util.Slog;
28 import android.webkit.UserPackage;
29 import android.webkit.WebViewFactory;
30 import android.webkit.WebViewFactoryProvider;
31 import android.webkit.WebViewProviderInfo;
32 import android.webkit.WebViewProviderResponse;
33 
34 import com.android.modules.expresslog.Counter;
35 
36 import java.io.PrintWriter;
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 /**
41  * Implementation of the WebViewUpdateService.
42  * This class doesn't depend on the android system like the actual Service does and can be used
43  * directly by tests (as long as they implement a SystemInterface).
44  *
45  * This class keeps track of and prepares the current WebView implementation, and needs to keep
46  * track of a couple of different things such as what package is used as WebView implementation.
47  *
48  * The package-visible methods in this class are accessed from WebViewUpdateService either on the UI
49  * thread or on one of multiple Binder threads. The WebView preparation code shares state between
50  * threads meaning that code that chooses a new WebView implementation or checks which
51  * implementation is being used needs to hold a lock.
52  *
53  * The WebViewUpdateService can be accessed in a couple of different ways.
54  * 1. It is started from the SystemServer at boot - at that point we just initiate some state such
55  * as the WebView preparation class.
56  * 2. The SystemServer calls WebViewUpdateService.prepareWebViewInSystemServer. This happens at boot
57  * and the WebViewUpdateService should not have been accessed before this call. In this call we
58  * choose WebView implementation for the first time.
59  * 3. The update service listens for Intents related to package installs and removals. These intents
60  * are received and processed on the UI thread. Each intent can result in changing WebView
61  * implementation.
62  * 4. The update service can be reached through Binder calls which are handled on specific binder
63  * threads. These calls can be made from any process. Generally they are used for changing WebView
64  * implementation (from Settings), getting information about the current WebView implementation (for
65  * loading WebView into an app process), or notifying the service about Relro creation being
66  * completed.
67  *
68  * @hide
69  */
70 class WebViewUpdateServiceImpl2 {
71     private static final String TAG = WebViewUpdateServiceImpl2.class.getSimpleName();
72 
73     private static class WebViewPackageMissingException extends Exception {
WebViewPackageMissingException(String message)74         WebViewPackageMissingException(String message) {
75             super(message);
76         }
77     }
78 
79     private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
80     private static final long NS_PER_MS = 1000000;
81 
82     private static final int VALIDITY_OK = 0;
83     private static final int VALIDITY_OS_INCOMPATIBLE = 1;
84     private static final int VALIDITY_INCORRECT_VERSION_CODE = 2;
85     private static final int VALIDITY_INCORRECT_SIGNATURE = 3;
86     private static final int VALIDITY_NO_LIBRARY_FLAG = 4;
87 
88     private final SystemInterface mSystemInterface;
89     private final WebViewProviderInfo mDefaultProvider;
90 
91     private long mMinimumVersionCode = -1;
92 
93     // Keeps track of the number of running relro creations
94     private int mNumRelroCreationsStarted = 0;
95     private int mNumRelroCreationsFinished = 0;
96     // Implies that we need to rerun relro creation because we are using an out-of-date package
97     private boolean mWebViewPackageDirty = false;
98     private boolean mAnyWebViewInstalled = false;
99 
100     // Keeps track of whether we attempted to repair WebView before.
101     private boolean mAttemptedToRepairBefore = false;
102 
103     private static final int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
104 
105     // The WebView package currently in use (or the one we are preparing).
106     private PackageInfo mCurrentWebViewPackage = null;
107 
108     private final Object mLock = new Object();
109 
WebViewUpdateServiceImpl2(SystemInterface systemInterface)110     WebViewUpdateServiceImpl2(SystemInterface systemInterface) {
111         mSystemInterface = systemInterface;
112         WebViewProviderInfo[] webviewProviders = getWebViewPackages();
113 
114         WebViewProviderInfo defaultProvider = null;
115         for (WebViewProviderInfo provider : webviewProviders) {
116             if (provider.availableByDefault) {
117                 defaultProvider = provider;
118                 break;
119             }
120         }
121         if (defaultProvider == null) {
122             // This should be unreachable because the config parser enforces that there is at least
123             // one availableByDefault provider.
124             throw new AndroidRuntimeException("No available by default WebView Provider.");
125         }
126         mDefaultProvider = defaultProvider;
127     }
128 
packageStateChanged(String packageName, int changedState, int userId)129     public void packageStateChanged(String packageName, int changedState, int userId) {
130         // We don't early out here in different cases where we could potentially early-out (e.g. if
131         // we receive PACKAGE_CHANGED for another user than the system user) since that would
132         // complicate this logic further and open up for more edge cases.
133         for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
134             String webviewPackage = provider.packageName;
135 
136             if (webviewPackage.equals(packageName)) {
137                 boolean updateWebView = false;
138                 boolean removedOrChangedOldPackage = false;
139                 String oldProviderName = null;
140                 PackageInfo newPackage = null;
141                 boolean repairNeeded = false;
142                 synchronized (mLock) {
143                     try {
144                         newPackage = findPreferredWebViewPackage();
145                         if (mCurrentWebViewPackage != null) {
146                             oldProviderName = mCurrentWebViewPackage.packageName;
147                         }
148                         // Only trigger update actions if the updated package is the one
149                         // that will be used, or the one that was in use before the
150                         // update, or if we haven't seen a valid WebView package before.
151                         updateWebView =
152                             provider.packageName.equals(newPackage.packageName)
153                             || provider.packageName.equals(oldProviderName)
154                             || mCurrentWebViewPackage == null;
155                         // We removed the old package if we received an intent to remove
156                         // or replace the old package.
157                         removedOrChangedOldPackage =
158                             provider.packageName.equals(oldProviderName);
159                         if (updateWebView) {
160                             onWebViewProviderChanged(newPackage);
161                         }
162                     } catch (WebViewPackageMissingException e) {
163                         mCurrentWebViewPackage = null;
164                         Slog.e(TAG, "Could not find valid WebView package to create relro with "
165                                 + e);
166                     }
167                     repairNeeded = shouldTriggerRepairLocked();
168                 }
169                 if (updateWebView && !removedOrChangedOldPackage
170                         && oldProviderName != null) {
171                     // If the provider change is the result of adding or replacing a
172                     // package that was not the previous provider then we must kill
173                     // packages dependent on the old package ourselves. The framework
174                     // only kills dependents of packages that are being removed.
175                     mSystemInterface.killPackageDependents(oldProviderName);
176                 }
177                 if (repairNeeded) {
178                     attemptRepair();
179                 }
180                 return;
181             }
182         }
183     }
184 
shouldTriggerRepairLocked()185     private boolean shouldTriggerRepairLocked() {
186         if (mAttemptedToRepairBefore) {
187             return false;
188         }
189         if (mCurrentWebViewPackage == null) {
190             return true;
191         }
192         if (mCurrentWebViewPackage.packageName.equals(mDefaultProvider.packageName)) {
193             List<UserPackage> userPackages =
194                     mSystemInterface.getPackageInfoForProviderAllUsers(mDefaultProvider);
195             return !isInstalledAndEnabledForAllUsers(userPackages);
196         } else {
197             return false;
198         }
199     }
200 
attemptRepair()201     private void attemptRepair() {
202         // We didn't find a valid WebView implementation. Try explicitly re-installing and
203         // re-enabling the default package for all users in case it was disabled. If this actually
204         // changes the state, we will see the PackageManager broadcast shortly and try again.
205         synchronized (mLock) {
206             if (mAttemptedToRepairBefore) {
207                 return;
208             }
209             mAttemptedToRepairBefore = true;
210         }
211         Slog.w(
212                 TAG,
213                 "No provider available for all users, trying to install and enable "
214                         + mDefaultProvider.packageName);
215         mSystemInterface.installExistingPackageForAllUsers(mDefaultProvider.packageName);
216         mSystemInterface.enablePackageForAllUsers(mDefaultProvider.packageName, true);
217     }
218 
prepareWebViewInSystemServer()219     public void prepareWebViewInSystemServer() {
220         try {
221             boolean repairNeeded = true;
222             synchronized (mLock) {
223                 mCurrentWebViewPackage = findPreferredWebViewPackage();
224                 repairNeeded = shouldTriggerRepairLocked();
225                 String userSetting = mSystemInterface.getUserChosenWebViewProvider();
226                 if (userSetting != null
227                         && !userSetting.equals(mCurrentWebViewPackage.packageName)) {
228                     // Don't persist the user-chosen setting across boots if the package being
229                     // chosen is not used (could be disabled or uninstalled) so that the user won't
230                     // be surprised by the device switching to using a certain webview package,
231                     // that was uninstalled/disabled a long time ago, if it is installed/enabled
232                     // again.
233                     mSystemInterface.updateUserSetting(mCurrentWebViewPackage.packageName);
234                 }
235                 onWebViewProviderChanged(mCurrentWebViewPackage);
236             }
237 
238             if (repairNeeded) {
239                 attemptRepair();
240             }
241 
242         } catch (WebViewPackageMissingException e) {
243             Slog.e(TAG, "Could not find valid WebView package to create relro with", e);
244         } catch (Throwable t) {
245             // We don't know a case when this should happen but we log and discard errors at this
246             // stage as we must not crash the system server.
247             Slog.wtf(TAG, "error preparing webview provider from system server", t);
248         }
249     }
250 
startZygoteWhenReady()251     private void startZygoteWhenReady() {
252         // Wait on a background thread for RELRO creation to be done. We ignore the return value
253         // because even if RELRO creation failed we still want to start the zygote.
254         waitForAndGetProvider();
255         mSystemInterface.ensureZygoteStarted();
256     }
257 
handleNewUser(int userId)258     public void handleNewUser(int userId) {
259         // The system user is always started at boot, and by that point we have already run one
260         // round of the package-changing logic (through prepareWebViewInSystemServer()), so early
261         // out here.
262         if (userId == UserHandle.USER_SYSTEM) return;
263         handleUserChange();
264     }
265 
handleUserRemoved(int userId)266     public void handleUserRemoved(int userId) {
267         handleUserChange();
268     }
269 
270     /**
271      * Called when a user was added or removed to ensure WebView preparation is triggered.
272      * This has to be done since the WebView package we use depends on the enabled-state
273      * of packages for all users (so adding or removing a user might cause us to change package).
274      */
handleUserChange()275     private void handleUserChange() {
276         // Potentially trigger package-changing logic.
277         updateCurrentWebViewPackage(null);
278     }
279 
notifyRelroCreationCompleted()280     public void notifyRelroCreationCompleted() {
281         synchronized (mLock) {
282             mNumRelroCreationsFinished++;
283             checkIfRelrosDoneLocked();
284         }
285     }
286 
waitForAndGetProvider()287     public WebViewProviderResponse waitForAndGetProvider() {
288         PackageInfo webViewPackage = null;
289         final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
290         boolean webViewReady = false;
291         int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
292         synchronized (mLock) {
293             webViewReady = webViewIsReadyLocked();
294             while (!webViewReady) {
295                 final long timeNowMs = System.nanoTime() / NS_PER_MS;
296                 if (timeNowMs >= timeoutTimeMs) break;
297                 try {
298                     mLock.wait(timeoutTimeMs - timeNowMs);
299                 } catch (InterruptedException e) {
300                     // ignore
301                 }
302                 webViewReady = webViewIsReadyLocked();
303             }
304             // Make sure we return the provider that was used to create the relro file
305             webViewPackage = mCurrentWebViewPackage;
306             if (webViewReady) {
307                 // success
308             } else if (!mAnyWebViewInstalled) {
309                 webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
310             } else {
311                 // Either the current relro creation  isn't done yet, or the new relro creatioin
312                 // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
313                 webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
314                 String timeoutError = "Timed out waiting for relro creation, relros started "
315                         + mNumRelroCreationsStarted
316                         + " relros finished " + mNumRelroCreationsFinished
317                         + " package dirty? " + mWebViewPackageDirty;
318                 Slog.e(TAG, timeoutError);
319                 Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, timeoutError);
320             }
321         }
322         if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
323         return new WebViewProviderResponse(webViewPackage, webViewStatus);
324     }
325 
326     /**
327      * Change WebView provider and provider setting and kill packages using the old provider.
328      * Return the new provider (in case we are in the middle of creating relro files, or
329      * replacing that provider it will not be in use directly, but will be used when the relros
330      * or the replacement are done).
331      */
changeProviderAndSetting(String newProviderName)332     public String changeProviderAndSetting(String newProviderName) {
333         PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
334         if (newPackage == null) return "";
335         return newPackage.packageName;
336     }
337 
338     /**
339      * Update the current WebView package.
340      * @param newProviderName the package to switch to, null if no package has been explicitly
341      * chosen.
342      */
updateCurrentWebViewPackage(@ullable String newProviderName)343     private PackageInfo updateCurrentWebViewPackage(@Nullable String newProviderName) {
344         PackageInfo oldPackage = null;
345         PackageInfo newPackage = null;
346         boolean providerChanged = false;
347         boolean repairNeeded = false;
348         synchronized (mLock) {
349             oldPackage = mCurrentWebViewPackage;
350 
351             if (newProviderName != null) {
352                 mSystemInterface.updateUserSetting(newProviderName);
353             }
354 
355             try {
356                 newPackage = findPreferredWebViewPackage();
357                 providerChanged = (oldPackage == null)
358                         || !newPackage.packageName.equals(oldPackage.packageName);
359             } catch (WebViewPackageMissingException e) {
360                 // If updated the Setting but don't have an installed WebView package, the
361                 // Setting will be used when a package is available.
362                 mCurrentWebViewPackage = null;
363                 Slog.e(TAG, "Couldn't find WebView package to use " + e);
364                 return null;
365             }
366             // Perform the provider change if we chose a new provider
367             if (providerChanged) {
368                 onWebViewProviderChanged(newPackage);
369             }
370             // Choosing another provider shouldn't break our state. Only check if repair
371             // is needed if this function is called as a result of a user change.
372             if (newProviderName == null) {
373                 repairNeeded = shouldTriggerRepairLocked();
374             }
375         }
376         // Kill apps using the old provider only if we changed provider
377         if (providerChanged && oldPackage != null) {
378             mSystemInterface.killPackageDependents(oldPackage.packageName);
379         }
380         if (repairNeeded) {
381             attemptRepair();
382         }
383         // Return the new provider, this is not necessarily the one we were asked to switch to,
384         // but the persistent setting will now be pointing to the provider we were asked to
385         // switch to anyway.
386         return newPackage;
387     }
388 
389     /**
390      * This is called when we change WebView provider, either when the current provider is
391      * updated or a new provider is chosen / takes precedence.
392      */
onWebViewProviderChanged(PackageInfo newPackage)393     private void onWebViewProviderChanged(PackageInfo newPackage) {
394         synchronized (mLock) {
395             mAnyWebViewInstalled = true;
396             if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
397                 mSystemInterface.pinWebviewIfRequired(newPackage.applicationInfo);
398                 mCurrentWebViewPackage = newPackage;
399 
400                 // The relro creations might 'finish' (not start at all) before
401                 // WebViewFactory.onWebViewProviderChanged which means we might not know the
402                 // number of started creations before they finish.
403                 mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
404                 mNumRelroCreationsFinished = 0;
405                 mNumRelroCreationsStarted =
406                     mSystemInterface.onWebViewProviderChanged(newPackage);
407                 Counter.logIncrement("webview.value_on_webview_provider_changed_counter");
408                 if (newPackage.packageName.equals(getDefaultWebViewPackage().packageName)) {
409                     Counter.logIncrement(
410                             "webview.value_on_webview_provider_changed_"
411                             + "with_default_package_counter");
412                 }
413                 // If the relro creations finish before we know the number of started creations
414                 // we will have to do any cleanup/notifying here.
415                 checkIfRelrosDoneLocked();
416             } else {
417                 mWebViewPackageDirty = true;
418             }
419         }
420 
421         // Once we've notified the system that the provider has changed and started RELRO creation,
422         // try to restart the zygote so that it will be ready when apps use it.
423         AsyncTask.THREAD_POOL_EXECUTOR.execute(this::startZygoteWhenReady);
424     }
425 
426     /** Fetch only the currently valid WebView packages. */
getValidWebViewPackages()427     public WebViewProviderInfo[] getValidWebViewPackages() {
428         ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
429         WebViewProviderInfo[] providers =
430                 new WebViewProviderInfo[providersAndPackageInfos.length];
431         for (int n = 0; n < providersAndPackageInfos.length; n++) {
432             providers[n] = providersAndPackageInfos[n].provider;
433         }
434         return providers;
435     }
436 
437     /**
438      * Returns the default WebView provider which should be first availableByDefault option in the
439      * system config.
440      */
getDefaultWebViewPackage()441     public WebViewProviderInfo getDefaultWebViewPackage() {
442         return mDefaultProvider;
443     }
444 
445     private static class ProviderAndPackageInfo {
446         public final WebViewProviderInfo provider;
447         public final PackageInfo packageInfo;
448 
ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo)449         ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
450             this.provider = provider;
451             this.packageInfo = packageInfo;
452         }
453     }
454 
getValidWebViewPackagesAndInfos()455     private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
456         WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
457         List<ProviderAndPackageInfo> providers = new ArrayList<>();
458         for (int n = 0; n < allProviders.length; n++) {
459             try {
460                 PackageInfo packageInfo =
461                         mSystemInterface.getPackageInfoForProvider(allProviders[n]);
462                 if (validityResult(allProviders[n], packageInfo) == VALIDITY_OK) {
463                     providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
464                 }
465             } catch (NameNotFoundException e) {
466                 // Don't add non-existent packages
467             }
468         }
469         return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
470     }
471 
472     /**
473      * Returns either the package info of the WebView provider determined in the following way:
474      * If the user has chosen a provider then use that if it is valid, enabled and installed
475      * for all users, otherwise use the default provider.
476      */
findPreferredWebViewPackage()477     private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
478         Counter.logIncrement("webview.value_find_preferred_webview_package_counter");
479         // If the user has chosen provider, use that (if it's installed and enabled for all
480         // users).
481         String userChosenPackageName = mSystemInterface.getUserChosenWebViewProvider();
482         WebViewProviderInfo userChosenProvider =
483                 getWebViewProviderForPackage(userChosenPackageName);
484         if (userChosenProvider != null) {
485             try {
486                 PackageInfo packageInfo =
487                         mSystemInterface.getPackageInfoForProvider(userChosenProvider);
488                 if (validityResult(userChosenProvider, packageInfo) == VALIDITY_OK) {
489                     List<UserPackage> userPackages =
490                             mSystemInterface.getPackageInfoForProviderAllUsers(userChosenProvider);
491                     if (isInstalledAndEnabledForAllUsers(userPackages)) {
492                         return packageInfo;
493                     }
494                 }
495             } catch (NameNotFoundException e) {
496                 Slog.w(TAG, "User chosen WebView package (" + userChosenPackageName
497                         + ") not found");
498             }
499         }
500 
501         // User did not choose, or the choice failed; return the default provider even if it is not
502         // installed or enabled for all users.
503         try {
504             PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(mDefaultProvider);
505             if (validityResult(mDefaultProvider, packageInfo) == VALIDITY_OK) {
506                 return packageInfo;
507             } else {
508                 Counter.logIncrement("webview.value_default_webview_package_invalid_counter");
509             }
510         } catch (NameNotFoundException e) {
511             Slog.w(TAG, "Default WebView package (" + mDefaultProvider.packageName + ") not found");
512         }
513 
514         // This should never happen during normal operation (only with modified system images).
515         Counter.logIncrement("webview.value_webview_not_usable_for_all_users_counter");
516         mAnyWebViewInstalled = false;
517         throw new WebViewPackageMissingException("Could not find a loadable WebView package");
518     }
519 
getWebViewProviderForPackage(String packageName)520     private WebViewProviderInfo getWebViewProviderForPackage(String packageName) {
521         WebViewProviderInfo[] allProviders = getWebViewPackages();
522         for (int n = 0; n < allProviders.length; n++) {
523             if (allProviders[n].packageName.equals(packageName)) {
524                 return allProviders[n];
525             }
526         }
527         return null;
528     }
529 
530     /**
531      * Return true iff {@param packageInfos} point to only installed and enabled packages.
532      * The given packages {@param packageInfos} should all be pointing to the same package, but each
533      * PackageInfo representing a different user's package.
534      */
isInstalledAndEnabledForAllUsers( List<UserPackage> userPackages)535     private static boolean isInstalledAndEnabledForAllUsers(
536             List<UserPackage> userPackages) {
537         for (UserPackage userPackage : userPackages) {
538             if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
539                 return false;
540             }
541         }
542         return true;
543     }
544 
getWebViewPackages()545     public WebViewProviderInfo[] getWebViewPackages() {
546         return mSystemInterface.getWebViewPackages();
547     }
548 
getCurrentWebViewPackage()549     public PackageInfo getCurrentWebViewPackage() {
550         synchronized (mLock) {
551             return mCurrentWebViewPackage;
552         }
553     }
554 
555     /**
556      * Returns whether WebView is ready and is not going to go through its preparation phase
557      * again directly.
558      */
webViewIsReadyLocked()559     private boolean webViewIsReadyLocked() {
560         return !mWebViewPackageDirty
561             && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
562             // The current package might be replaced though we haven't received an intent
563             // declaring this yet, the following flag makes anyone loading WebView to wait in
564             // this case.
565             && mAnyWebViewInstalled;
566     }
567 
checkIfRelrosDoneLocked()568     private void checkIfRelrosDoneLocked() {
569         if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
570             if (mWebViewPackageDirty) {
571                 mWebViewPackageDirty = false;
572                 // If we have changed provider since we started the relro creation we need to
573                 // redo the whole process using the new package instead.
574                 try {
575                     PackageInfo newPackage = findPreferredWebViewPackage();
576                     onWebViewProviderChanged(newPackage);
577                 } catch (WebViewPackageMissingException e) {
578                     mCurrentWebViewPackage = null;
579                     // If we can't find any valid WebView package we are now in a state where
580                     // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
581                     // should simply wait until we receive an intent declaring a new package was
582                     // installed.
583                 }
584             } else {
585                 mLock.notifyAll();
586             }
587         }
588     }
589 
validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo)590     private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
591         // Ensure the provider is compatible with this framework release.
592         if (!mSystemInterface.isCompatibleImplementationPackage(packageInfo)) {
593             return VALIDITY_OS_INCOMPATIBLE;
594         }
595         if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
596                 && !mSystemInterface.systemIsDebuggable()) {
597             // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
598             // minimum version code. This check is only enforced for user builds.
599             return VALIDITY_INCORRECT_VERSION_CODE;
600         }
601         if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) {
602             return VALIDITY_INCORRECT_SIGNATURE;
603         }
604         if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) {
605             return VALIDITY_NO_LIBRARY_FLAG;
606         }
607         return VALIDITY_OK;
608     }
609 
610     /**
611      * Both versionCodes should be from a WebView provider package implemented by Chromium.
612      * VersionCodes from other kinds of packages won't make any sense in this method.
613      *
614      * An introduction to Chromium versionCode scheme:
615      * "BBBBPPPXX"
616      * BBBB: 4 digit branch number. It monotonically increases over time.
617      * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits
618      * may change their meaning in the future.
619      * XX: Digits to differentiate different APK builds of the same source version.
620      *
621      * This method takes the "BBBB" of versionCodes and compare them.
622      *
623      * https://www.chromium.org/developers/version-numbers describes general Chromium versioning;
624      * https://source.chromium.org/chromium/chromium/src/+/master:build/util/android_chrome_version.py
625      * is the canonical source for how Chromium versionCodes are calculated.
626      *
627      * @return true if versionCode1 is higher than or equal to versionCode2.
628      */
versionCodeGE(long versionCode1, long versionCode2)629     private static boolean versionCodeGE(long versionCode1, long versionCode2) {
630         long v1 = versionCode1 / 100000;
631         long v2 = versionCode2 / 100000;
632 
633         return v1 >= v2;
634     }
635 
636     /**
637      * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
638      * of all available-by-default WebView provider packages. If there is no such WebView provider
639      * package on the system, then return -1, which means all positive versionCode WebView packages
640      * are accepted.
641      *
642      * Note that this is a private method that handles a variable (mMinimumVersionCode) which is
643      * shared between threads. Furthermore, this method does not hold mLock meaning that we must
644      * take extra care to ensure this method is thread-safe.
645      */
getMinimumVersionCode()646     private long getMinimumVersionCode() {
647         if (mMinimumVersionCode > 0) {
648             return mMinimumVersionCode;
649         }
650 
651         long minimumVersionCode = -1;
652         for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
653             if (provider.availableByDefault) {
654                 try {
655                     long versionCode =
656                             mSystemInterface.getFactoryPackageVersion(provider.packageName);
657                     if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
658                         minimumVersionCode = versionCode;
659                     }
660                 } catch (NameNotFoundException e) {
661                     // Safe to ignore.
662                 }
663             }
664         }
665 
666         mMinimumVersionCode = minimumVersionCode;
667         return mMinimumVersionCode;
668     }
669 
providerHasValidSignature(WebViewProviderInfo provider, PackageInfo packageInfo, SystemInterface systemInterface)670     private static boolean providerHasValidSignature(WebViewProviderInfo provider,
671             PackageInfo packageInfo, SystemInterface systemInterface) {
672         // Skip checking signatures on debuggable builds, for development purposes.
673         if (systemInterface.systemIsDebuggable()) return true;
674 
675         // Allow system apps to be valid providers regardless of signature.
676         if (packageInfo.applicationInfo.isSystemApp()) return true;
677 
678         // We don't support packages with multiple signatures.
679         if (packageInfo.signatures.length != 1) return false;
680 
681         // If any of the declared signatures match the package signature, it's valid.
682         for (Signature signature : provider.signatures) {
683             if (signature.equals(packageInfo.signatures[0])) return true;
684         }
685 
686         return false;
687     }
688 
689     /**
690      * Returns the only fallback provider in the set of given packages, or null if there is none.
691      */
getFallbackProvider(WebViewProviderInfo[] webviewPackages)692     private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
693         for (WebViewProviderInfo provider : webviewPackages) {
694             if (provider.isFallback) {
695                 return provider;
696             }
697         }
698         return null;
699     }
700 
701     /** Dump the state of this Service. */
dumpState(PrintWriter pw)702     public void dumpState(PrintWriter pw) {
703         pw.println("Current WebView Update Service state");
704         synchronized (mLock) {
705             if (mCurrentWebViewPackage == null) {
706                 pw.println("  Current WebView package is null");
707             } else {
708                 pw.println(
709                         TextUtils.formatSimple(
710                                 "  Current WebView package (name, version): (%s, %s)",
711                                 mCurrentWebViewPackage.packageName,
712                                 mCurrentWebViewPackage.versionName));
713             }
714             pw.println(
715                     TextUtils.formatSimple(
716                             "  %s",
717                             WebViewFactoryProvider.describeCompatibleImplementationPackage()));
718             pw.println(
719                     TextUtils.formatSimple(
720                             "  Minimum WebView version code: %d", mMinimumVersionCode));
721             pw.println(
722                     TextUtils.formatSimple(
723                             "  Number of relros started: %d", mNumRelroCreationsStarted));
724             pw.println(
725                     TextUtils.formatSimple(
726                             "  Number of relros finished: %d", mNumRelroCreationsFinished));
727             pw.println(TextUtils.formatSimple("  WebView package dirty: %b", mWebViewPackageDirty));
728             pw.println(
729                     TextUtils.formatSimple(
730                             "  Any WebView package installed: %b", mAnyWebViewInstalled));
731 
732             try {
733                 PackageInfo preferredWebViewPackage = findPreferredWebViewPackage();
734                 pw.println(
735                         TextUtils.formatSimple(
736                                 "  Preferred WebView package (name, version): (%s, %s)",
737                                 preferredWebViewPackage.packageName,
738                                 preferredWebViewPackage.versionName));
739             } catch (WebViewPackageMissingException e) {
740                 pw.println("  Preferred WebView package: none");
741             }
742 
743             dumpAllPackageInformationLocked(pw);
744         }
745     }
746 
dumpAllPackageInformationLocked(PrintWriter pw)747     private void dumpAllPackageInformationLocked(PrintWriter pw) {
748         WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
749         pw.println("  WebView packages:");
750         for (WebViewProviderInfo provider : allProviders) {
751             List<UserPackage> userPackages =
752                     mSystemInterface.getPackageInfoForProviderAllUsers(provider);
753             PackageInfo systemUserPackageInfo =
754                     userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
755             if (systemUserPackageInfo == null) {
756                 pw.println(
757                         TextUtils.formatSimple("    %s is NOT installed.", provider.packageName));
758                 continue;
759             }
760 
761             int validity = validityResult(provider, systemUserPackageInfo);
762             String packageDetails =
763                     TextUtils.formatSimple(
764                             "versionName: %s, versionCode: %d, targetSdkVersion: %d",
765                             systemUserPackageInfo.versionName,
766                             systemUserPackageInfo.getLongVersionCode(),
767                             systemUserPackageInfo.applicationInfo.targetSdkVersion);
768             if (validity == VALIDITY_OK) {
769                 boolean installedForAllUsers =
770                         isInstalledAndEnabledForAllUsers(
771                                 mSystemInterface.getPackageInfoForProviderAllUsers(provider));
772                 pw.println(
773                         TextUtils.formatSimple(
774                                 "    Valid package %s (%s) is %s installed/enabled for all users",
775                                 systemUserPackageInfo.packageName,
776                                 packageDetails,
777                                 installedForAllUsers ? "" : "NOT"));
778             } else {
779                 pw.println(
780                         TextUtils.formatSimple(
781                                 "    Invalid package %s (%s), reason: %s",
782                                 systemUserPackageInfo.packageName,
783                                 packageDetails,
784                                 getInvalidityReason(validity)));
785             }
786         }
787     }
788 
getInvalidityReason(int invalidityReason)789     private static String getInvalidityReason(int invalidityReason) {
790         switch (invalidityReason) {
791             case VALIDITY_OS_INCOMPATIBLE:
792                 return "Not compatible with this OS version";
793             case VALIDITY_INCORRECT_VERSION_CODE:
794                 return "Version code too low";
795             case VALIDITY_INCORRECT_SIGNATURE:
796                 return "Incorrect signature";
797             case VALIDITY_NO_LIBRARY_FLAG:
798                 return "No WebView-library manifest flag";
799             default:
800                 return "Unexcepted validity-reason";
801         }
802     }
803 }
804