blob: a460040d0a6041087523a55f6f129cf4b1cc4728 [file] [log] [blame]
Gustav Sennton924dacd2017-03-10 14:37:15 +00001/*
2 * Copyright (C) 2017 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.PackageInfo;
20import android.content.pm.PackageManager.NameNotFoundException;
21import android.content.pm.Signature;
22import android.os.UserHandle;
Gustav Sennton924dacd2017-03-10 14:37:15 +000023import android.util.Slog;
24import android.webkit.UserPackage;
25import android.webkit.WebViewFactory;
26import android.webkit.WebViewProviderInfo;
27import android.webkit.WebViewProviderResponse;
28
29import java.io.PrintWriter;
30import java.util.ArrayList;
Gustav Sennton924dacd2017-03-10 14:37:15 +000031import java.util.List;
32
33/**
34 * Class that decides what WebView implementation to use and prepares that implementation for
35 * use.
36 */
37class WebViewUpdater {
38 private static final String TAG = WebViewUpdater.class.getSimpleName();
39
40 private static class WebViewPackageMissingException extends Exception {
41 public WebViewPackageMissingException(String message) { super(message); }
42 public WebViewPackageMissingException(Exception e) { super(e); }
43 }
44
45 private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
46
47 private final static int VALIDITY_OK = 0;
48 private final static int VALIDITY_INCORRECT_SDK_VERSION = 1;
49 private final static int VALIDITY_INCORRECT_VERSION_CODE = 2;
50 private final static int VALIDITY_INCORRECT_SIGNATURE = 3;
51 private final static int VALIDITY_NO_LIBRARY_FLAG = 4;
52
53 private Context mContext;
54 private SystemInterface mSystemInterface;
Dianne Hackborn3accca02013-09-20 09:32:11 -070055 private long mMinimumVersionCode = -1;
Gustav Sennton924dacd2017-03-10 14:37:15 +000056
57 // Keeps track of the number of running relro creations
58 private int mNumRelroCreationsStarted = 0;
59 private int mNumRelroCreationsFinished = 0;
60 // Implies that we need to rerun relro creation because we are using an out-of-date package
61 private boolean mWebViewPackageDirty = false;
62 private boolean mAnyWebViewInstalled = false;
63
64 private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
65
66 // The WebView package currently in use (or the one we are preparing).
67 private PackageInfo mCurrentWebViewPackage = null;
68
Andrew Scull33bae882017-05-19 10:33:26 +010069 private final Object mLock = new Object();
Gustav Sennton924dacd2017-03-10 14:37:15 +000070
71 WebViewUpdater(Context context, SystemInterface systemInterface) {
72 mContext = context;
73 mSystemInterface = systemInterface;
74 }
75
76 void packageStateChanged(String packageName, int changedState) {
77 for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
78 String webviewPackage = provider.packageName;
79
80 if (webviewPackage.equals(packageName)) {
81 boolean updateWebView = false;
82 boolean removedOrChangedOldPackage = false;
83 String oldProviderName = null;
84 PackageInfo newPackage = null;
85 synchronized(mLock) {
86 try {
87 newPackage = findPreferredWebViewPackage();
88 if (mCurrentWebViewPackage != null) {
89 oldProviderName = mCurrentWebViewPackage.packageName;
90 if (changedState == WebViewUpdateService.PACKAGE_CHANGED
91 && newPackage.packageName.equals(oldProviderName)) {
92 // If we don't change package name we should only rerun the
93 // preparation phase if the current package has been replaced
94 // (not if it has been enabled/disabled).
95 return;
96 }
97 if (newPackage.packageName.equals(oldProviderName)
98 && (newPackage.lastUpdateTime
99 == mCurrentWebViewPackage.lastUpdateTime)) {
100 // If the chosen package hasn't been updated, then early-out
101 return;
102 }
103 }
104 // Only trigger update actions if the updated package is the one
105 // that will be used, or the one that was in use before the
106 // update, or if we haven't seen a valid WebView package before.
107 updateWebView =
108 provider.packageName.equals(newPackage.packageName)
109 || provider.packageName.equals(oldProviderName)
110 || mCurrentWebViewPackage == null;
111 // We removed the old package if we received an intent to remove
112 // or replace the old package.
113 removedOrChangedOldPackage =
114 provider.packageName.equals(oldProviderName);
115 if (updateWebView) {
116 onWebViewProviderChanged(newPackage);
117 }
118 } catch (WebViewPackageMissingException e) {
119 mCurrentWebViewPackage = null;
120 Slog.e(TAG, "Could not find valid WebView package to create " +
121 "relro with " + e);
122 }
123 }
124 if(updateWebView && !removedOrChangedOldPackage
125 && oldProviderName != null) {
126 // If the provider change is the result of adding or replacing a
127 // package that was not the previous provider then we must kill
128 // packages dependent on the old package ourselves. The framework
129 // only kills dependents of packages that are being removed.
130 mSystemInterface.killPackageDependents(oldProviderName);
131 }
132 return;
133 }
134 }
135 }
136
137 void prepareWebViewInSystemServer() {
138 try {
139 synchronized(mLock) {
140 mCurrentWebViewPackage = findPreferredWebViewPackage();
Torne (Richard Coles)ef478902019-03-28 15:03:21 -0400141 String userSetting = mSystemInterface.getUserChosenWebViewProvider(mContext);
142 if (userSetting != null
143 && !userSetting.equals(mCurrentWebViewPackage.packageName)) {
144 // Don't persist the user-chosen setting across boots if the package being
145 // chosen is not used (could be disabled or uninstalled) so that the user won't
146 // be surprised by the device switching to using a certain webview package,
147 // that was uninstalled/disabled a long time ago, if it is installed/enabled
148 // again.
149 mSystemInterface.updateUserSetting(mContext,
150 mCurrentWebViewPackage.packageName);
151 }
Gustav Sennton924dacd2017-03-10 14:37:15 +0000152 onWebViewProviderChanged(mCurrentWebViewPackage);
153 }
154 } catch (Throwable t) {
155 // Log and discard errors at this stage as we must not crash the system server.
156 Slog.e(TAG, "error preparing webview provider from system server", t);
157 }
158 }
159
160 /**
161 * Change WebView provider and provider setting and kill packages using the old provider.
162 * Return the new provider (in case we are in the middle of creating relro files, or
163 * replacing that provider it will not be in use directly, but will be used when the relros
164 * or the replacement are done).
165 */
166 String changeProviderAndSetting(String newProviderName) {
167 PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
168 if (newPackage == null) return "";
169 return newPackage.packageName;
170 }
171
172 /**
173 * Update the current WebView package.
174 * @param newProviderName the package to switch to, null if no package has been explicitly
175 * chosen.
176 */
177 PackageInfo updateCurrentWebViewPackage(String newProviderName) {
178 PackageInfo oldPackage = null;
179 PackageInfo newPackage = null;
180 boolean providerChanged = false;
181 synchronized(mLock) {
182 oldPackage = mCurrentWebViewPackage;
183
184 if (newProviderName != null) {
185 mSystemInterface.updateUserSetting(mContext, newProviderName);
186 }
187
188 try {
189 newPackage = findPreferredWebViewPackage();
190 providerChanged = (oldPackage == null)
191 || !newPackage.packageName.equals(oldPackage.packageName);
192 } catch (WebViewPackageMissingException e) {
193 // If updated the Setting but don't have an installed WebView package, the
194 // Setting will be used when a package is available.
195 mCurrentWebViewPackage = null;
196 Slog.e(TAG, "Couldn't find WebView package to use " + e);
197 return null;
198 }
199 // Perform the provider change if we chose a new provider
200 if (providerChanged) {
201 onWebViewProviderChanged(newPackage);
202 }
203 }
204 // Kill apps using the old provider only if we changed provider
205 if (providerChanged && oldPackage != null) {
206 mSystemInterface.killPackageDependents(oldPackage.packageName);
207 }
208 // Return the new provider, this is not necessarily the one we were asked to switch to,
209 // but the persistent setting will now be pointing to the provider we were asked to
210 // switch to anyway.
211 return newPackage;
212 }
213
214 /**
215 * This is called when we change WebView provider, either when the current provider is
216 * updated or a new provider is chosen / takes precedence.
217 */
218 private void onWebViewProviderChanged(PackageInfo newPackage) {
219 synchronized(mLock) {
220 mAnyWebViewInstalled = true;
221 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
222 mCurrentWebViewPackage = newPackage;
223
224 // The relro creations might 'finish' (not start at all) before
225 // WebViewFactory.onWebViewProviderChanged which means we might not know the
226 // number of started creations before they finish.
227 mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
228 mNumRelroCreationsFinished = 0;
229 mNumRelroCreationsStarted =
230 mSystemInterface.onWebViewProviderChanged(newPackage);
231 // If the relro creations finish before we know the number of started creations
232 // we will have to do any cleanup/notifying here.
233 checkIfRelrosDoneLocked();
234 } else {
235 mWebViewPackageDirty = true;
236 }
237 }
238 }
239
240 /**
241 * Fetch only the currently valid WebView packages.
242 **/
243 WebViewProviderInfo[] getValidWebViewPackages() {
244 ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
245 WebViewProviderInfo[] providers =
246 new WebViewProviderInfo[providersAndPackageInfos.length];
247 for(int n = 0; n < providersAndPackageInfos.length; n++) {
248 providers[n] = providersAndPackageInfos[n].provider;
249 }
250 return providers;
251 }
252
253 private static class ProviderAndPackageInfo {
254 public final WebViewProviderInfo provider;
255 public final PackageInfo packageInfo;
256
257 public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
258 this.provider = provider;
259 this.packageInfo = packageInfo;
260 }
261 }
262
263 private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
264 WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
265 List<ProviderAndPackageInfo> providers = new ArrayList<>();
266 for(int n = 0; n < allProviders.length; n++) {
267 try {
268 PackageInfo packageInfo =
269 mSystemInterface.getPackageInfoForProvider(allProviders[n]);
270 if (isValidProvider(allProviders[n], packageInfo)) {
271 providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
272 }
273 } catch (NameNotFoundException e) {
274 // Don't add non-existent packages
275 }
276 }
277 return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
278 }
279
280 /**
281 * Returns either the package info of the WebView provider determined in the following way:
282 * If the user has chosen a provider then use that if it is valid,
283 * otherwise use the first package in the webview priority list that is valid.
284 *
285 */
286 private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
287 ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
288
289 String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
290
291 // If the user has chosen provider, use that (if it's installed and enabled for all
292 // users).
293 for (ProviderAndPackageInfo providerAndPackage : providers) {
294 if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
295 // userPackages can contain null objects.
296 List<UserPackage> userPackages =
297 mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
298 providerAndPackage.provider);
299 if (isInstalledAndEnabledForAllUsers(userPackages)) {
300 return providerAndPackage.packageInfo;
301 }
302 }
303 }
304
305 // User did not choose, or the choice failed; use the most stable provider that is
306 // installed and enabled for all users, and available by default (not through
307 // user choice).
308 for (ProviderAndPackageInfo providerAndPackage : providers) {
309 if (providerAndPackage.provider.availableByDefault) {
310 // userPackages can contain null objects.
311 List<UserPackage> userPackages =
312 mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
313 providerAndPackage.provider);
314 if (isInstalledAndEnabledForAllUsers(userPackages)) {
315 return providerAndPackage.packageInfo;
316 }
317 }
318 }
319
Gustav Sennton924dacd2017-03-10 14:37:15 +0000320 // This should never happen during normal operation (only with modified system images).
321 mAnyWebViewInstalled = false;
322 throw new WebViewPackageMissingException("Could not find a loadable WebView package");
323 }
324
325 /**
326 * Return true iff {@param packageInfos} point to only installed and enabled packages.
327 * The given packages {@param packageInfos} should all be pointing to the same package, but each
328 * PackageInfo representing a different user's package.
329 */
330 static boolean isInstalledAndEnabledForAllUsers(
331 List<UserPackage> userPackages) {
332 for (UserPackage userPackage : userPackages) {
333 if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
334 return false;
335 }
336 }
337 return true;
338 }
339
340 void notifyRelroCreationCompleted() {
341 synchronized (mLock) {
342 mNumRelroCreationsFinished++;
343 checkIfRelrosDoneLocked();
344 }
345 }
346
347 WebViewProviderResponse waitForAndGetProvider() {
348 PackageInfo webViewPackage = null;
349 final long NS_PER_MS = 1000000;
350 final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
351 boolean webViewReady = false;
352 int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
353 synchronized (mLock) {
354 webViewReady = webViewIsReadyLocked();
355 while (!webViewReady) {
356 final long timeNowMs = System.nanoTime() / NS_PER_MS;
357 if (timeNowMs >= timeoutTimeMs) break;
358 try {
359 mLock.wait(timeoutTimeMs - timeNowMs);
360 } catch (InterruptedException e) {}
361 webViewReady = webViewIsReadyLocked();
362 }
363 // Make sure we return the provider that was used to create the relro file
364 webViewPackage = mCurrentWebViewPackage;
365 if (webViewReady) {
366 } else if (!mAnyWebViewInstalled) {
367 webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
368 } else {
369 // Either the current relro creation isn't done yet, or the new relro creatioin
370 // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
371 webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
372 Slog.e(TAG, "Timed out waiting for relro creation, relros started "
373 + mNumRelroCreationsStarted
374 + " relros finished " + mNumRelroCreationsFinished
375 + " package dirty? " + mWebViewPackageDirty);
376 }
377 }
378 if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
379 return new WebViewProviderResponse(webViewPackage, webViewStatus);
380 }
381
382 PackageInfo getCurrentWebViewPackage() {
383 synchronized(mLock) {
384 return mCurrentWebViewPackage;
385 }
386 }
387
388 /**
389 * Returns whether WebView is ready and is not going to go through its preparation phase
390 * again directly.
391 */
392 private boolean webViewIsReadyLocked() {
393 return !mWebViewPackageDirty
394 && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
395 // The current package might be replaced though we haven't received an intent
396 // declaring this yet, the following flag makes anyone loading WebView to wait in
397 // this case.
398 && mAnyWebViewInstalled;
399 }
400
401 private void checkIfRelrosDoneLocked() {
402 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
403 if (mWebViewPackageDirty) {
404 mWebViewPackageDirty = false;
405 // If we have changed provider since we started the relro creation we need to
406 // redo the whole process using the new package instead.
407 try {
408 PackageInfo newPackage = findPreferredWebViewPackage();
409 onWebViewProviderChanged(newPackage);
410 } catch (WebViewPackageMissingException e) {
411 mCurrentWebViewPackage = null;
412 // If we can't find any valid WebView package we are now in a state where
413 // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
414 // should simply wait until we receive an intent declaring a new package was
415 // installed.
416 }
417 } else {
418 mLock.notifyAll();
419 }
420 }
421 }
422
423 /**
424 * Returns whether this provider is valid for use as a WebView provider.
425 */
426 boolean isValidProvider(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
427 return VALIDITY_OK == validityResult(configInfo, packageInfo);
428 }
429
430 private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
431 // Ensure the provider targets this framework release (or a later one).
432 if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
433 return VALIDITY_INCORRECT_SDK_VERSION;
434 }
Dianne Hackborn3accca02013-09-20 09:32:11 -0700435 if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
Gustav Sennton924dacd2017-03-10 14:37:15 +0000436 && !mSystemInterface.systemIsDebuggable()) {
437 // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
438 // minimum version code. This check is only enforced for user builds.
439 return VALIDITY_INCORRECT_VERSION_CODE;
440 }
441 if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) {
442 return VALIDITY_INCORRECT_SIGNATURE;
443 }
444 if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) {
445 return VALIDITY_NO_LIBRARY_FLAG;
446 }
447 return VALIDITY_OK;
448 }
449
450 /**
451 * Both versionCodes should be from a WebView provider package implemented by Chromium.
452 * VersionCodes from other kinds of packages won't make any sense in this method.
453 *
454 * An introduction to Chromium versionCode scheme:
455 * "BBBBPPPAX"
456 * BBBB: 4 digit branch number. It monotonically increases over time.
457 * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits
458 * may change their meaning in the future.
459 * A: architecture digit.
460 * X: A digit to differentiate APKs for other reasons.
461 *
462 * This method takes the "BBBB" of versionCodes and compare them.
463 *
464 * @return true if versionCode1 is higher than or equal to versionCode2.
465 */
Dianne Hackborn3accca02013-09-20 09:32:11 -0700466 private static boolean versionCodeGE(long versionCode1, long versionCode2) {
467 long v1 = versionCode1 / 100000;
468 long v2 = versionCode2 / 100000;
Gustav Sennton924dacd2017-03-10 14:37:15 +0000469
470 return v1 >= v2;
471 }
472
473 /**
474 * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
Torne (Richard Coles)ef478902019-03-28 15:03:21 -0400475 * of all available-by-default WebView provider packages. If there is no such WebView provider
476 * package on the system, then return -1, which means all positive versionCode WebView packages
477 * are accepted.
Gustav Sennton924dacd2017-03-10 14:37:15 +0000478 *
479 * Note that this is a private method in WebViewUpdater that handles a variable
480 * (mMinimumVersionCode) which is shared between threads. Furthermore, this method does not
481 * hold mLock meaning that we must take extra care to ensure this method is thread-safe.
482 */
Dianne Hackborn3accca02013-09-20 09:32:11 -0700483 private long getMinimumVersionCode() {
Gustav Sennton924dacd2017-03-10 14:37:15 +0000484 if (mMinimumVersionCode > 0) {
485 return mMinimumVersionCode;
486 }
487
Dianne Hackborn3accca02013-09-20 09:32:11 -0700488 long minimumVersionCode = -1;
Gustav Sennton924dacd2017-03-10 14:37:15 +0000489 for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
Torne (Richard Coles)ef478902019-03-28 15:03:21 -0400490 if (provider.availableByDefault) {
Gustav Sennton924dacd2017-03-10 14:37:15 +0000491 try {
Dianne Hackborn3accca02013-09-20 09:32:11 -0700492 long versionCode =
Gustav Sennton924dacd2017-03-10 14:37:15 +0000493 mSystemInterface.getFactoryPackageVersion(provider.packageName);
494 if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
495 minimumVersionCode = versionCode;
496 }
497 } catch (NameNotFoundException e) {
498 // Safe to ignore.
499 }
500 }
501 }
502
503 mMinimumVersionCode = minimumVersionCode;
504 return mMinimumVersionCode;
505 }
506
507 private static boolean providerHasValidSignature(WebViewProviderInfo provider,
508 PackageInfo packageInfo, SystemInterface systemInterface) {
Torne (Richard Coles)8f1ec882018-07-19 14:27:06 -0400509 // Skip checking signatures on debuggable builds, for development purposes.
510 if (systemInterface.systemIsDebuggable()) return true;
511
512 // Allow system apps to be valid providers regardless of signature.
513 if (packageInfo.applicationInfo.isSystemApp()) return true;
514
515 // We don't support packages with multiple signatures.
Gustav Sennton74473592017-11-20 20:14:17 +0000516 if (packageInfo.signatures.length != 1) return false;
Gustav Sennton924dacd2017-03-10 14:37:15 +0000517
Torne (Richard Coles)8f1ec882018-07-19 14:27:06 -0400518 // If any of the declared signatures match the package signature, it's valid.
Gustav Sennton74473592017-11-20 20:14:17 +0000519 for (Signature signature : provider.signatures) {
520 if (signature.equals(packageInfo.signatures[0])) return true;
Gustav Sennton924dacd2017-03-10 14:37:15 +0000521 }
Torne (Richard Coles)8f1ec882018-07-19 14:27:06 -0400522
Gustav Sennton924dacd2017-03-10 14:37:15 +0000523 return false;
524 }
525
526 void dumpState(PrintWriter pw) {
527 synchronized (mLock) {
528 if (mCurrentWebViewPackage == null) {
529 pw.println(" Current WebView package is null");
530 } else {
531 pw.println(String.format(" Current WebView package (name, version): (%s, %s)",
532 mCurrentWebViewPackage.packageName,
533 mCurrentWebViewPackage.versionName));
534 }
Gustav Sennton6823c482018-05-04 13:50:58 +0100535 pw.println(String.format(" Minimum targetSdkVersion: %d",
536 UserPackage.MINIMUM_SUPPORTED_SDK));
Gustav Sennton924dacd2017-03-10 14:37:15 +0000537 pw.println(String.format(" Minimum WebView version code: %d",
538 mMinimumVersionCode));
539 pw.println(String.format(" Number of relros started: %d",
540 mNumRelroCreationsStarted));
541 pw.println(String.format(" Number of relros finished: %d",
542 mNumRelroCreationsFinished));
543 pw.println(String.format(" WebView package dirty: %b", mWebViewPackageDirty));
544 pw.println(String.format(" Any WebView package installed: %b",
545 mAnyWebViewInstalled));
546
547 try {
548 PackageInfo preferredWebViewPackage = findPreferredWebViewPackage();
549 pw.println(String.format(
550 " Preferred WebView package (name, version): (%s, %s)",
551 preferredWebViewPackage.packageName,
552 preferredWebViewPackage.versionName));
553 } catch (WebViewPackageMissingException e) {
554 pw.println(String.format(" Preferred WebView package: none"));
555 }
556
557 dumpAllPackageInformationLocked(pw);
558 }
559 }
560
561 private void dumpAllPackageInformationLocked(PrintWriter pw) {
562 WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
563 pw.println(" WebView packages:");
564 for (WebViewProviderInfo provider : allProviders) {
565 List<UserPackage> userPackages =
566 mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider);
567 PackageInfo systemUserPackageInfo =
568 userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
569 if (systemUserPackageInfo == null) {
Gustav Sennton232a77b2017-09-11 16:04:00 +0100570 pw.println(String.format(" %s is NOT installed.", provider.packageName));
Gustav Sennton924dacd2017-03-10 14:37:15 +0000571 continue;
572 }
573
574 int validity = validityResult(provider, systemUserPackageInfo);
575 String packageDetails = String.format(
576 "versionName: %s, versionCode: %d, targetSdkVersion: %d",
577 systemUserPackageInfo.versionName,
Dianne Hackborn3accca02013-09-20 09:32:11 -0700578 systemUserPackageInfo.getLongVersionCode(),
Gustav Sennton924dacd2017-03-10 14:37:15 +0000579 systemUserPackageInfo.applicationInfo.targetSdkVersion);
580 if (validity == VALIDITY_OK) {
581 boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
582 mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider));
583 pw.println(String.format(
584 " Valid package %s (%s) is %s installed/enabled for all users",
585 systemUserPackageInfo.packageName,
586 packageDetails,
587 installedForAllUsers ? "" : "NOT"));
588 } else {
589 pw.println(String.format(" Invalid package %s (%s), reason: %s",
590 systemUserPackageInfo.packageName,
591 packageDetails,
592 getInvalidityReason(validity)));
593 }
594 }
595 }
596
597 private static String getInvalidityReason(int invalidityReason) {
598 switch (invalidityReason) {
599 case VALIDITY_INCORRECT_SDK_VERSION:
600 return "SDK version too low";
601 case VALIDITY_INCORRECT_VERSION_CODE:
602 return "Version code too low";
603 case VALIDITY_INCORRECT_SIGNATURE:
604 return "Incorrect signature";
605 case VALIDITY_NO_LIBRARY_FLAG:
606 return "No WebView-library manifest flag";
607 default:
608 return "Unexcepted validity-reason";
609 }
610 }
611
612}