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