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