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 
17 package com.android.server.webkit;
18 
19 import static org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.PackageInfo;
25 import android.content.pm.Signature;
26 import android.os.Build;
27 import android.os.Bundle;
28 import android.test.suitebuilder.annotation.MediumTest;
29 import android.util.Base64;
30 import android.webkit.UserPackage;
31 import android.webkit.WebViewFactory;
32 import android.webkit.WebViewProviderInfo;
33 import android.webkit.WebViewProviderResponse;
34 
35 import androidx.test.InstrumentationRegistry;
36 import androidx.test.runner.AndroidJUnit4;
37 
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 import org.mockito.ArgumentMatcher;
41 import org.mockito.Matchers;
42 import org.mockito.Mockito;
43 
44 import java.util.concurrent.CountDownLatch;
45 
46 /**
47  * Tests for WebViewUpdateService
48  runtest --path frameworks/base/services/tests/servicestests/ \
49      -c com.android.server.webkit.WebViewUpdateServiceTest
50  */
51 // Use MediumTest instead of SmallTest as the implementation of WebViewUpdateService
52 // is intended to work on several threads and uses at least one sleep/wait-statement.
53 @RunWith(AndroidJUnit4.class)
54 @MediumTest
55 public class WebViewUpdateServiceTest {
56     private final static String TAG = WebViewUpdateServiceTest.class.getSimpleName();
57 
58     private WebViewUpdateServiceImpl mWebViewUpdateServiceImpl;
59     private TestSystemImpl mTestSystemImpl;
60 
61     private static final String WEBVIEW_LIBRARY_FLAG = "com.android.webview.WebViewLibrary";
62 
63     /**
64      * Creates a new instance.
65      */
WebViewUpdateServiceTest()66     public WebViewUpdateServiceTest() {
67     }
68 
setupWithPackages(WebViewProviderInfo[] packages)69     private void setupWithPackages(WebViewProviderInfo[] packages) {
70         setupWithAllParameters(packages, 1 /* numRelros */, true /* isDebuggable */,
71                 false /* multiProcessDefault */);
72     }
73 
setupWithPackagesAndRelroCount(WebViewProviderInfo[] packages, int numRelros)74     private void setupWithPackagesAndRelroCount(WebViewProviderInfo[] packages, int numRelros) {
75         setupWithAllParameters(packages, numRelros, true /* isDebuggable */,
76                 false /* multiProcessDefault */);
77     }
78 
setupWithPackagesNonDebuggable(WebViewProviderInfo[] packages)79     private void setupWithPackagesNonDebuggable(WebViewProviderInfo[] packages) {
80         setupWithAllParameters(packages, 1 /* numRelros */, false /* isDebuggable */,
81                 false /* multiProcessDefault */);
82     }
83 
setupWithPackagesAndMultiProcess(WebViewProviderInfo[] packages, boolean multiProcessDefault)84     private void setupWithPackagesAndMultiProcess(WebViewProviderInfo[] packages,
85             boolean multiProcessDefault) {
86         setupWithAllParameters(packages, 1 /* numRelros */, true /* isDebuggable */,
87                 multiProcessDefault);
88     }
89 
setupWithAllParameters(WebViewProviderInfo[] packages, int numRelros, boolean isDebuggable, boolean multiProcessDefault)90     private void setupWithAllParameters(WebViewProviderInfo[] packages, int numRelros,
91             boolean isDebuggable, boolean multiProcessDefault) {
92         TestSystemImpl testing = new TestSystemImpl(packages, numRelros, isDebuggable,
93                 multiProcessDefault);
94         mTestSystemImpl = Mockito.spy(testing);
95         mWebViewUpdateServiceImpl =
96             new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
97     }
98 
setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers)99     private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) {
100         // Set package infos for the primary user (user 0).
101         setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, providers);
102     }
103 
setEnabledAndValidPackageInfosForUser(int userId, WebViewProviderInfo[] providers)104     private void setEnabledAndValidPackageInfosForUser(int userId,
105             WebViewProviderInfo[] providers) {
106         for(WebViewProviderInfo wpi : providers) {
107             mTestSystemImpl.setPackageInfoForUser(userId, createPackageInfo(wpi.packageName,
108                     true /* enabled */, true /* valid */, true /* installed */));
109         }
110     }
111 
checkCertainPackageUsedAfterWebViewBootPreparation(String expectedProviderName, WebViewProviderInfo[] webviewPackages)112     private void checkCertainPackageUsedAfterWebViewBootPreparation(String expectedProviderName,
113             WebViewProviderInfo[] webviewPackages) {
114         checkCertainPackageUsedAfterWebViewBootPreparation(
115                 expectedProviderName, webviewPackages, 1);
116     }
117 
checkCertainPackageUsedAfterWebViewBootPreparation(String expectedProviderName, WebViewProviderInfo[] webviewPackages, int numRelros)118     private void checkCertainPackageUsedAfterWebViewBootPreparation(String expectedProviderName,
119             WebViewProviderInfo[] webviewPackages, int numRelros) {
120         setupWithPackagesAndRelroCount(webviewPackages, numRelros);
121         // Add (enabled and valid) package infos for each provider
122         setEnabledAndValidPackageInfos(webviewPackages);
123 
124         runWebViewBootPreparationOnMainSync();
125 
126         Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
127                 Mockito.argThat(new IsPackageInfoWithName(expectedProviderName)));
128 
129         for (int n = 0; n < numRelros; n++) {
130             mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
131         }
132 
133         WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
134         assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
135         assertEquals(expectedProviderName, response.packageInfo.packageName);
136     }
137 
138     // For matching the package name of a PackageInfo
139     private class IsPackageInfoWithName implements ArgumentMatcher<PackageInfo> {
140         private final String mPackageName;
141 
IsPackageInfoWithName(String name)142         IsPackageInfoWithName(String name) {
143             mPackageName = name;
144         }
145 
146         @Override
matches(PackageInfo p)147         public boolean matches(PackageInfo p) {
148             return p.packageName.equals(mPackageName);
149         }
150 
151         @Override
toString()152         public String toString() {
153             return String.format("PackageInfo with name '%s'", mPackageName);
154         }
155     }
156 
createPackageInfo( String packageName, boolean enabled, boolean valid, boolean installed)157     private static PackageInfo createPackageInfo(
158             String packageName, boolean enabled, boolean valid, boolean installed) {
159         PackageInfo p = new PackageInfo();
160         p.packageName = packageName;
161         p.applicationInfo = new ApplicationInfo();
162         p.applicationInfo.enabled = enabled;
163         p.applicationInfo.metaData = new Bundle();
164         if (installed) {
165             p.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
166         } else {
167             p.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
168         }
169         if (valid) {
170             // no flag means invalid
171             p.applicationInfo.metaData.putString(WEBVIEW_LIBRARY_FLAG, "blah");
172         }
173         // Default to this package being valid in terms of targetSdkVersion.
174         p.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
175         return p;
176     }
177 
createPackageInfo(String packageName, boolean enabled, boolean valid, boolean installed, Signature[] signatures, long updateTime)178     private static PackageInfo createPackageInfo(String packageName, boolean enabled, boolean valid,
179             boolean installed, Signature[] signatures, long updateTime) {
180         PackageInfo p = createPackageInfo(packageName, enabled, valid, installed);
181         p.signatures = signatures;
182         p.lastUpdateTime = updateTime;
183         return p;
184     }
185 
createPackageInfo(String packageName, boolean enabled, boolean valid, boolean installed, Signature[] signatures, long updateTime, boolean hidden)186     private static PackageInfo createPackageInfo(String packageName, boolean enabled, boolean valid,
187             boolean installed, Signature[] signatures, long updateTime, boolean hidden) {
188         PackageInfo p =
189             createPackageInfo(packageName, enabled, valid, installed, signatures, updateTime);
190         if (hidden) {
191             p.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
192         } else {
193             p.applicationInfo.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HIDDEN;
194         }
195         return p;
196     }
197 
createPackageInfo(String packageName, boolean enabled, boolean valid, boolean installed, Signature[] signatures, long updateTime, boolean hidden, long versionCode, boolean isSystemApp)198     private static PackageInfo createPackageInfo(String packageName, boolean enabled, boolean valid,
199             boolean installed, Signature[] signatures, long updateTime, boolean hidden,
200             long versionCode, boolean isSystemApp) {
201         PackageInfo p = createPackageInfo(packageName, enabled, valid, installed, signatures,
202                 updateTime, hidden);
203         p.setLongVersionCode(versionCode);
204         p.applicationInfo.setVersionCode(versionCode);
205         if (isSystemApp) p.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
206         return p;
207     }
208 
checkPreparationPhasesForPackage(String expectedPackage, int numPreparation)209     private void checkPreparationPhasesForPackage(String expectedPackage, int numPreparation) {
210         // Verify that onWebViewProviderChanged was called for the numPreparation'th time for the
211         // expected package
212         Mockito.verify(mTestSystemImpl, Mockito.times(numPreparation)).onWebViewProviderChanged(
213                 Mockito.argThat(new IsPackageInfoWithName(expectedPackage)));
214 
215         mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
216 
217         WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
218         assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
219         assertEquals(expectedPackage, response.packageInfo.packageName);
220     }
221 
222     /**
223      * The WebView preparation boot phase is run on the main thread (especially on a thread with a
224      * looper) so to avoid bugs where our tests fail because a looper hasn't been attached to the
225      * thread running prepareWebViewInSystemServer we run it on the main thread.
226      */
runWebViewBootPreparationOnMainSync()227     private void runWebViewBootPreparationOnMainSync() {
228         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
229             @Override
230             public void run() {
231                 mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
232             }
233         });
234     }
235 
236 
237     // ****************
238     // Tests
239     // ****************
240 
241 
242     @Test
testWithSinglePackage()243     public void testWithSinglePackage() {
244         String testPackageName = "test.package.name";
245         checkCertainPackageUsedAfterWebViewBootPreparation(testPackageName,
246                 new WebViewProviderInfo[] {
247                     new WebViewProviderInfo(testPackageName, "",
248                             true /*default available*/, false /* fallback */, null)});
249     }
250 
251     @Test
testDefaultPackageUsedOverNonDefault()252     public void testDefaultPackageUsedOverNonDefault() {
253         String defaultPackage = "defaultPackage";
254         String nonDefaultPackage = "nonDefaultPackage";
255         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
256             new WebViewProviderInfo(nonDefaultPackage, "", false, false, null),
257             new WebViewProviderInfo(defaultPackage, "", true, false, null)};
258         checkCertainPackageUsedAfterWebViewBootPreparation(defaultPackage, packages);
259     }
260 
261     @Test
testSeveralRelros()262     public void testSeveralRelros() {
263         String singlePackage = "singlePackage";
264         checkCertainPackageUsedAfterWebViewBootPreparation(
265                 singlePackage,
266                 new WebViewProviderInfo[] {
267                     new WebViewProviderInfo(singlePackage, "", true /*def av*/, false, null)},
268                 2);
269     }
270 
271     // Ensure that package with valid signatures is chosen rather than package with invalid
272     // signatures.
273     @Test
testWithSignatures()274     public void testWithSignatures() {
275         String validPackage = "valid package";
276         String invalidPackage = "invalid package";
277 
278         Signature validSignature = new Signature("11");
279         Signature invalidExpectedSignature = new Signature("22");
280         Signature invalidPackageSignature = new Signature("33");
281 
282         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
283             new WebViewProviderInfo(invalidPackage, "", true, false, new String[]{
284                         Base64.encodeToString(
285                                 invalidExpectedSignature.toByteArray(), Base64.DEFAULT)}),
286             new WebViewProviderInfo(validPackage, "", true, false, new String[]{
287                         Base64.encodeToString(
288                                 validSignature.toByteArray(), Base64.DEFAULT)})
289         };
290         setupWithPackagesNonDebuggable(packages);
291         mTestSystemImpl.setPackageInfo(createPackageInfo(invalidPackage, true /* enabled */,
292                     true /* valid */, true /* installed */, new Signature[]{invalidPackageSignature}
293                     , 0 /* updateTime */));
294         mTestSystemImpl.setPackageInfo(createPackageInfo(validPackage, true /* enabled */,
295                     true /* valid */, true /* installed */, new Signature[]{validSignature}
296                     , 0 /* updateTime */));
297 
298         runWebViewBootPreparationOnMainSync();
299 
300 
301         checkPreparationPhasesForPackage(validPackage, 1 /* first preparation for this package */);
302 
303         WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages();
304         assertEquals(1, validPackages.length);
305         assertEquals(validPackage, validPackages[0].packageName);
306     }
307 
308     @Test
testFailWaitingForRelro()309     public void testFailWaitingForRelro() {
310         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
311             new WebViewProviderInfo("packagename", "", true, true, null)};
312         setupWithPackages(packages);
313         setEnabledAndValidPackageInfos(packages);
314 
315         runWebViewBootPreparationOnMainSync();
316 
317         Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
318                 Mockito.argThat(new IsPackageInfoWithName(packages[0].packageName)));
319 
320         // Never call notifyRelroCreation()
321 
322         WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
323         assertEquals(WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO, response.status);
324     }
325 
326     @Test
testFailListingEmptyWebviewPackages()327     public void testFailListingEmptyWebviewPackages() {
328         WebViewProviderInfo[] packages = new WebViewProviderInfo[0];
329         setupWithPackages(packages);
330         setEnabledAndValidPackageInfos(packages);
331 
332         runWebViewBootPreparationOnMainSync();
333 
334         Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
335                 Matchers.anyObject());
336 
337         WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
338         assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
339         assertEquals(null, mWebViewUpdateServiceImpl.getCurrentWebViewPackage());
340 
341         // Now install a package
342         String singlePackage = "singlePackage";
343         packages = new WebViewProviderInfo[]{
344             new WebViewProviderInfo(singlePackage, "", true, false, null)};
345         setupWithPackages(packages);
346         setEnabledAndValidPackageInfos(packages);
347 
348         mWebViewUpdateServiceImpl.packageStateChanged(singlePackage,
349                 WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID);
350 
351         checkPreparationPhasesForPackage(singlePackage, 1 /* number of finished preparations */);
352         assertEquals(singlePackage,
353                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName);
354 
355         // Remove the package again
356         mTestSystemImpl.removePackageInfo(singlePackage);
357         mWebViewUpdateServiceImpl.packageStateChanged(singlePackage,
358                 WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID);
359 
360         // Package removed - ensure our interface states that there is no package
361         response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
362         assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
363         assertEquals(null, mWebViewUpdateServiceImpl.getCurrentWebViewPackage());
364     }
365 
366     @Test
testFailListingInvalidWebviewPackage()367     public void testFailListingInvalidWebviewPackage() {
368         WebViewProviderInfo wpi = new WebViewProviderInfo("package", "", true, true, null);
369         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi};
370         setupWithPackages(packages);
371         mTestSystemImpl.setPackageInfo(
372                 createPackageInfo(wpi.packageName, true /* enabled */, false /* valid */,
373                     true /* installed */));
374 
375         runWebViewBootPreparationOnMainSync();
376 
377         Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
378                 Matchers.anyObject());
379 
380         WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
381         assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
382 
383         // Verify that we can recover from failing to list webview packages.
384         mTestSystemImpl.setPackageInfo(
385                 createPackageInfo(wpi.packageName, true /* enabled */, true /* valid */,
386                     true /* installed */));
387         mWebViewUpdateServiceImpl.packageStateChanged(wpi.packageName,
388                 WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
389 
390         checkPreparationPhasesForPackage(wpi.packageName, 1);
391     }
392 
393     // Test that switching provider using changeProviderAndSetting works.
394     @Test
testSwitchingProvider()395     public void testSwitchingProvider() {
396         String firstPackage = "first";
397         String secondPackage = "second";
398         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
399             new WebViewProviderInfo(firstPackage, "", true, false, null),
400             new WebViewProviderInfo(secondPackage, "", true, false, null)};
401         checkSwitchingProvider(packages, firstPackage, secondPackage);
402     }
403 
404     @Test
testSwitchingProviderToNonDefault()405     public void testSwitchingProviderToNonDefault() {
406         String defaultPackage = "defaultPackage";
407         String nonDefaultPackage = "nonDefaultPackage";
408         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
409             new WebViewProviderInfo(defaultPackage, "", true, false, null),
410             new WebViewProviderInfo(nonDefaultPackage, "", false, false, null)};
411         checkSwitchingProvider(packages, defaultPackage, nonDefaultPackage);
412     }
413 
checkSwitchingProvider(WebViewProviderInfo[] packages, String initialPackage, String finalPackage)414     private void checkSwitchingProvider(WebViewProviderInfo[] packages, String initialPackage,
415             String finalPackage) {
416         checkCertainPackageUsedAfterWebViewBootPreparation(initialPackage, packages);
417 
418         mWebViewUpdateServiceImpl.changeProviderAndSetting(finalPackage);
419         checkPreparationPhasesForPackage(finalPackage, 1 /* first preparation for this package */);
420 
421         Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(initialPackage));
422     }
423 
424     // Change provider during relro creation by using changeProviderAndSetting
425     @Test
testSwitchingProviderDuringRelroCreation()426     public void testSwitchingProviderDuringRelroCreation() {
427         checkChangingProviderDuringRelroCreation(true);
428     }
429 
430     // Change provider during relro creation by enabling a provider
431     @Test
testChangingProviderThroughEnablingDuringRelroCreation()432     public void testChangingProviderThroughEnablingDuringRelroCreation() {
433         checkChangingProviderDuringRelroCreation(false);
434     }
435 
checkChangingProviderDuringRelroCreation(boolean settingsChange)436     private void checkChangingProviderDuringRelroCreation(boolean settingsChange) {
437         String firstPackage = "first";
438         String secondPackage = "second";
439         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
440             new WebViewProviderInfo(firstPackage, "", true, false, null),
441             new WebViewProviderInfo(secondPackage, "", true, false, null)};
442         setupWithPackages(packages);
443         // Have all packages be enabled, so that we can change provider however we want to
444         setEnabledAndValidPackageInfos(packages);
445 
446         CountDownLatch countdown = new CountDownLatch(1);
447 
448         runWebViewBootPreparationOnMainSync();
449 
450         Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
451                 Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
452 
453         assertEquals(firstPackage,
454                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName);
455 
456         new Thread(new Runnable() {
457             @Override
458             public void run() {
459                 WebViewProviderResponse threadResponse =
460                     mWebViewUpdateServiceImpl.waitForAndGetProvider();
461                 assertEquals(WebViewFactory.LIBLOAD_SUCCESS, threadResponse.status);
462                 assertEquals(secondPackage, threadResponse.packageInfo.packageName);
463                 // Verify that we killed the first package if we performed a settings change -
464                 // otherwise we had to disable the first package, in which case its dependents
465                 // should have been killed by the framework.
466                 if (settingsChange) {
467                     Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
468                 }
469                 countdown.countDown();
470             }
471         }).start();
472         try {
473             Thread.sleep(500); // Let the new thread run / be blocked
474         } catch (InterruptedException e) {
475         }
476 
477         if (settingsChange) {
478             mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
479         } else {
480             // Enable the second provider
481             mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
482                         true /* valid */, true /* installed */));
483             mWebViewUpdateServiceImpl.packageStateChanged(
484                     secondPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
485 
486             // Ensure we haven't changed package yet.
487             assertEquals(firstPackage,
488                     mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName);
489 
490             // Switch provider by disabling the first one
491             mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, false /* enabled */,
492                         true /* valid */, true /* installed */));
493             mWebViewUpdateServiceImpl.packageStateChanged(
494                     firstPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
495         }
496         mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
497         // first package done, should start on second
498 
499         Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
500                 Mockito.argThat(new IsPackageInfoWithName(secondPackage)));
501 
502         mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
503         // second package done, the other thread should now be unblocked
504         try {
505             countdown.await();
506         } catch (InterruptedException e) {
507         }
508     }
509 
510     /**
511      * Scenario for testing re-enabling a fallback package.
512      */
513     @Test
testFallbackPackageEnabling()514     public void testFallbackPackageEnabling() {
515         String testPackage = "testFallback";
516         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
517             new WebViewProviderInfo(
518                     testPackage, "", true /* default available */, true /* fallback */, null)};
519         setupWithPackages(packages);
520         mTestSystemImpl.setPackageInfo(
521                 createPackageInfo(testPackage, false /* enabled */ , true /* valid */,
522                     true /* installed */));
523 
524         // Check that the boot time logic re-enables the fallback package.
525         runWebViewBootPreparationOnMainSync();
526         Mockito.verify(mTestSystemImpl).enablePackageForAllUsers(
527                 Matchers.anyObject(), Mockito.eq(testPackage), Mockito.eq(true));
528 
529         // Fake the message about the enabling having changed the package state,
530         // and check we now use that package.
531         mWebViewUpdateServiceImpl.packageStateChanged(
532                 testPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
533         checkPreparationPhasesForPackage(testPackage, 1);
534     }
535 
536     /**
537      * Scenario for installing primary package when secondary in use.
538      * 1. Start with only secondary installed
539      * 2. Install primary
540      * 3. Primary should be used
541      */
542     @Test
testInstallingPrimaryPackage()543     public void testInstallingPrimaryPackage() {
544         String primaryPackage = "primary";
545         String secondaryPackage = "secondary";
546         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
547             new WebViewProviderInfo(
548                     primaryPackage, "", true /* default available */, false /* fallback */, null),
549             new WebViewProviderInfo(
550                     secondaryPackage, "", true /* default available */, false /* fallback */,
551                     null)};
552         setupWithPackages(packages);
553         mTestSystemImpl.setPackageInfo(
554                 createPackageInfo(secondaryPackage, true /* enabled */ , true /* valid */,
555                     true /* installed */));
556 
557         runWebViewBootPreparationOnMainSync();
558         checkPreparationPhasesForPackage(secondaryPackage,
559                 1 /* first preparation for this package*/);
560 
561         // Install primary package
562         mTestSystemImpl.setPackageInfo(
563                 createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */,
564                     true /* installed */));
565         mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
566                 WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
567 
568         // Verify primary package used as provider, and secondary package killed
569         checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation for this package*/);
570         Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(secondaryPackage));
571     }
572 
573     @Test
testRemovingPrimarySelectsSecondarySingleUser()574     public void testRemovingPrimarySelectsSecondarySingleUser() {
575         for (PackageRemovalType removalType : REMOVAL_TYPES) {
576             checkRemovingPrimarySelectsSecondary(false /* multiUser */, removalType);
577         }
578     }
579 
580     @Test
testRemovingPrimarySelectsSecondaryMultiUser()581     public void testRemovingPrimarySelectsSecondaryMultiUser() {
582         for (PackageRemovalType removalType : REMOVAL_TYPES) {
583             checkRemovingPrimarySelectsSecondary(true /* multiUser */, removalType);
584         }
585     }
586 
587     /**
588      * Represents how to remove a package during a tests (disabling it / uninstalling it / hiding
589      * it).
590      */
591     private enum PackageRemovalType {
592         UNINSTALL, DISABLE, HIDE
593     }
594 
595     private PackageRemovalType[] REMOVAL_TYPES = PackageRemovalType.class.getEnumConstants();
596 
checkRemovingPrimarySelectsSecondary(boolean multiUser, PackageRemovalType removalType)597     public void checkRemovingPrimarySelectsSecondary(boolean multiUser,
598             PackageRemovalType removalType) {
599         String primaryPackage = "primary";
600         String secondaryPackage = "secondary";
601         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
602             new WebViewProviderInfo(
603                     primaryPackage, "", true /* default available */, false /* fallback */, null),
604             new WebViewProviderInfo(
605                     secondaryPackage, "", true /* default available */, false /* fallback */,
606                     null)};
607         setupWithPackages(packages);
608         int secondaryUserId = 10;
609         int userIdToChangePackageFor = multiUser ? secondaryUserId : TestSystemImpl.PRIMARY_USER_ID;
610         if (multiUser) {
611             mTestSystemImpl.addUser(secondaryUserId);
612             setEnabledAndValidPackageInfosForUser(secondaryUserId, packages);
613         }
614         setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages);
615 
616         runWebViewBootPreparationOnMainSync();
617         checkPreparationPhasesForPackage(primaryPackage, 1);
618 
619         boolean enabled = !(removalType == PackageRemovalType.DISABLE);
620         boolean installed = !(removalType == PackageRemovalType.UNINSTALL);
621         boolean hidden = (removalType == PackageRemovalType.HIDE);
622         // Disable primary package and ensure secondary becomes used
623         mTestSystemImpl.setPackageInfoForUser(userIdToChangePackageFor,
624                 createPackageInfo(primaryPackage, enabled /* enabled */, true /* valid */,
625                     installed /* installed */, null /* signature */, 0 /* updateTime */,
626                     hidden /* hidden */));
627         mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
628                 removalType == PackageRemovalType.DISABLE
629                 ? WebViewUpdateService.PACKAGE_CHANGED : WebViewUpdateService.PACKAGE_REMOVED,
630                 userIdToChangePackageFor); // USER ID
631         checkPreparationPhasesForPackage(secondaryPackage, 1);
632 
633         // Again enable primary package and verify primary is used
634         mTestSystemImpl.setPackageInfoForUser(userIdToChangePackageFor,
635                 createPackageInfo(primaryPackage, true /* enabled */, true /* valid */,
636                     true /* installed */));
637         mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
638                 removalType == PackageRemovalType.DISABLE
639                 ? WebViewUpdateService.PACKAGE_CHANGED : WebViewUpdateService.PACKAGE_ADDED,
640                 userIdToChangePackageFor);
641         checkPreparationPhasesForPackage(primaryPackage, 2);
642     }
643 
644     /**
645      * Ensures that adding a new user for which the current WebView package is uninstalled causes a
646      * change of WebView provider.
647      */
648     @Test
testAddingNewUserWithUninstalledPackage()649     public void testAddingNewUserWithUninstalledPackage() {
650         String primaryPackage = "primary";
651         String secondaryPackage = "secondary";
652         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
653             new WebViewProviderInfo(
654                     primaryPackage, "", true /* default available */, false /* fallback */, null),
655             new WebViewProviderInfo(
656                     secondaryPackage, "", true /* default available */, false /* fallback */,
657                     null)};
658         setupWithPackages(packages);
659         setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages);
660         int newUser = 100;
661         mTestSystemImpl.addUser(newUser);
662         // Let the primary package be uninstalled for the new user
663         mTestSystemImpl.setPackageInfoForUser(newUser,
664                 createPackageInfo(primaryPackage, true /* enabled */, true /* valid */,
665                         false /* installed */));
666         mTestSystemImpl.setPackageInfoForUser(newUser,
667                 createPackageInfo(secondaryPackage, true /* enabled */, true /* valid */,
668                         true /* installed */));
669         mWebViewUpdateServiceImpl.handleNewUser(newUser);
670         checkPreparationPhasesForPackage(secondaryPackage, 1 /* numRelros */);
671     }
672 
673     /**
674      * Timing dependent test where we verify that the list of valid webview packages becoming empty
675      * at a certain point doesn't crash us or break our state.
676      */
677     @Test
testNotifyRelroDoesntCrashIfNoPackages()678     public void testNotifyRelroDoesntCrashIfNoPackages() {
679         String firstPackage = "first";
680         String secondPackage = "second";
681         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
682             new WebViewProviderInfo(firstPackage, "", true /* default available */,
683                     false /* fallback */, null),
684             new WebViewProviderInfo(secondPackage, "", true /* default available */,
685                     false /* fallback */, null)};
686         setupWithPackages(packages);
687         // Add (enabled and valid) package infos for each provider
688         setEnabledAndValidPackageInfos(packages);
689 
690         runWebViewBootPreparationOnMainSync();
691 
692         Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
693                 Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
694 
695         // Change provider during relro creation to enter a state where we are
696         // waiting for relro creation to complete just to re-run relro creation.
697         // (so that in next notifyRelroCreationCompleted() call we have to list webview packages)
698         mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
699 
700         // Make packages invalid to cause exception to be thrown
701         mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
702                     false /* valid */, true /* installed */, null /* signatures */,
703                     0 /* updateTime */));
704         mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
705                     false /* valid */, true /* installed */));
706 
707         // This shouldn't throw an exception!
708         mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
709 
710         WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
711         assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
712 
713         // Now make a package valid again and verify that we can switch back to that
714         mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
715                     true /* valid */, true /* installed */, null /* signatures */,
716                     1 /* updateTime */ ));
717 
718         mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
719                 WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
720 
721         // Ensure we use firstPackage
722         checkPreparationPhasesForPackage(firstPackage, 2 /* second preparation for this package */);
723     }
724 
725     /**
726      * Verify that even if a user-chosen package is removed temporarily we start using it again when
727      * it is added back.
728      */
729     @Test
testTempRemovePackageDoesntSwitchProviderPermanently()730     public void testTempRemovePackageDoesntSwitchProviderPermanently() {
731         String firstPackage = "first";
732         String secondPackage = "second";
733         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
734             new WebViewProviderInfo(firstPackage, "", true /* default available */,
735                     false /* fallback */, null),
736             new WebViewProviderInfo(secondPackage, "", true /* default available */,
737                     false /* fallback */, null)};
738         checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
739 
740         // Explicitly use the second package
741         mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
742         checkPreparationPhasesForPackage(secondPackage, 1 /* first time for this package */);
743 
744         // Remove second package (invalidate it) and verify that first package is used
745         mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
746                     false /* valid */, true /* installed */));
747         mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
748                 WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID);
749         checkPreparationPhasesForPackage(firstPackage, 2 /* second time for this package */);
750 
751         // Now make the second package valid again and verify that it is used again
752         mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
753                     true /* valid */, true /* installed */));
754         mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
755                 WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID);
756         checkPreparationPhasesForPackage(secondPackage, 2 /* second time for this package */);
757     }
758 
759     /**
760      * Ensure that we update the user-chosen setting across boots if the chosen package is no
761      * longer installed and valid.
762      */
763     @Test
testProviderSettingChangedDuringBootIfProviderNotAvailable()764     public void testProviderSettingChangedDuringBootIfProviderNotAvailable() {
765         String chosenPackage = "chosenPackage";
766         String nonChosenPackage = "non-chosenPackage";
767         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
768             new WebViewProviderInfo(chosenPackage, "", true /* default available */,
769                     false /* fallback */, null),
770             new WebViewProviderInfo(nonChosenPackage, "", true /* default available */,
771                     false /* fallback */, null)};
772 
773         setupWithPackages(packages);
774         // Only 'install' nonChosenPackage
775         mTestSystemImpl.setPackageInfo(
776                 createPackageInfo(nonChosenPackage, true /* enabled */, true /* valid */,
777                         true /* installed */));
778 
779         // Set user-chosen package
780         mTestSystemImpl.updateUserSetting(null, chosenPackage);
781 
782         runWebViewBootPreparationOnMainSync();
783 
784         // Verify that we switch the setting to point to the current package
785         Mockito.verify(mTestSystemImpl).updateUserSetting(
786                 Mockito.anyObject(), Mockito.eq(nonChosenPackage));
787         assertEquals(nonChosenPackage, mTestSystemImpl.getUserChosenWebViewProvider(null));
788 
789         checkPreparationPhasesForPackage(nonChosenPackage, 1);
790     }
791 
792     @Test
testRecoverFailedListingWebViewPackagesSettingsChange()793     public void testRecoverFailedListingWebViewPackagesSettingsChange() {
794         checkRecoverAfterFailListingWebviewPackages(true);
795     }
796 
797     @Test
testRecoverFailedListingWebViewPackagesAddedPackage()798     public void testRecoverFailedListingWebViewPackagesAddedPackage() {
799         checkRecoverAfterFailListingWebviewPackages(false);
800     }
801 
802     /**
803      * Test that we can recover correctly from failing to list WebView packages.
804      * settingsChange: whether to fail during changeProviderAndSetting or packageStateChanged
805      */
checkRecoverAfterFailListingWebviewPackages(boolean settingsChange)806     public void checkRecoverAfterFailListingWebviewPackages(boolean settingsChange) {
807         String firstPackage = "first";
808         String secondPackage = "second";
809         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
810             new WebViewProviderInfo(firstPackage, "", true /* default available */,
811                     false /* fallback */, null),
812             new WebViewProviderInfo(secondPackage, "", true /* default available */,
813                     false /* fallback */, null)};
814         checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
815 
816         // Make both packages invalid so that we fail listing WebView packages
817         mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
818                     false /* valid */, true /* installed */));
819         mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
820                     false /* valid */, true /* installed */));
821 
822         // Change package to hit the webview packages listing problem.
823         if (settingsChange) {
824             mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
825         } else {
826             mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
827                     WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
828         }
829 
830         WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
831         assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
832 
833         // Make second package valid and verify that we can load it again
834         mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
835                     true /* valid */, true /* installed */));
836 
837         mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
838                 WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
839 
840 
841         checkPreparationPhasesForPackage(secondPackage, 1);
842     }
843 
844     @Test
testDontKillIfPackageReplaced()845     public void testDontKillIfPackageReplaced() {
846         checkDontKillIfPackageRemoved(true);
847     }
848 
849     @Test
testDontKillIfPackageRemoved()850     public void testDontKillIfPackageRemoved() {
851         checkDontKillIfPackageRemoved(false);
852     }
853 
checkDontKillIfPackageRemoved(boolean replaced)854     public void checkDontKillIfPackageRemoved(boolean replaced) {
855         String firstPackage = "first";
856         String secondPackage = "second";
857         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
858             new WebViewProviderInfo(firstPackage, "", true /* default available */,
859                     false /* fallback */, null),
860             new WebViewProviderInfo(secondPackage, "", true /* default available */,
861                     false /* fallback */, null)};
862         checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
863 
864         // Replace or remove the current webview package
865         if (replaced) {
866             mTestSystemImpl.setPackageInfo(
867                     createPackageInfo(firstPackage, true /* enabled */, false /* valid */,
868                         true /* installed */));
869             mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
870                     WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
871         } else {
872             mTestSystemImpl.removePackageInfo(firstPackage);
873             mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
874                     WebViewUpdateService.PACKAGE_REMOVED, TestSystemImpl.PRIMARY_USER_ID);
875         }
876 
877         checkPreparationPhasesForPackage(secondPackage, 1);
878 
879         Mockito.verify(mTestSystemImpl, Mockito.never()).killPackageDependents(
880                 Mockito.anyObject());
881     }
882 
883     @Test
testKillIfSettingChanged()884     public void testKillIfSettingChanged() {
885         String firstPackage = "first";
886         String secondPackage = "second";
887         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
888             new WebViewProviderInfo(firstPackage, "", true /* default available */,
889                     false /* fallback */, null),
890             new WebViewProviderInfo(secondPackage, "", true /* default available */,
891                     false /* fallback */, null)};
892         checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
893 
894         mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
895 
896         checkPreparationPhasesForPackage(secondPackage, 1);
897 
898         Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
899     }
900 
901     /**
902      * Test that we kill apps using an old provider when we change the provider setting, even if the
903      * new provider is not the one we intended to change to.
904      */
905     @Test
testKillIfChangeProviderIncorrectly()906     public void testKillIfChangeProviderIncorrectly() {
907         String firstPackage = "first";
908         String secondPackage = "second";
909         String thirdPackage = "third";
910         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
911             new WebViewProviderInfo(firstPackage, "", true /* default available */,
912                     false /* fallback */, null),
913             new WebViewProviderInfo(secondPackage, "", true /* default available */,
914                     false /* fallback */, null),
915             new WebViewProviderInfo(thirdPackage, "", true /* default available */,
916                     false /* fallback */, null)};
917         setupWithPackages(packages);
918         setEnabledAndValidPackageInfos(packages);
919 
920         // Start with the setting pointing to the third package
921         mTestSystemImpl.updateUserSetting(null, thirdPackage);
922 
923         runWebViewBootPreparationOnMainSync();
924         checkPreparationPhasesForPackage(thirdPackage, 1);
925 
926         mTestSystemImpl.setPackageInfo(
927                 createPackageInfo(secondPackage, true /* enabled */, false /* valid */,
928                         true /* installed */));
929 
930         // Try to switch to the invalid second package, this should result in switching to the first
931         // package, since that is more preferred than the third one.
932         assertEquals(firstPackage,
933                 mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage));
934 
935         checkPreparationPhasesForPackage(firstPackage, 1);
936 
937         Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(thirdPackage));
938     }
939 
940     @Test
testLowerPackageVersionNotValid()941     public void testLowerPackageVersionNotValid() {
942         checkPackageVersions(new int[]{200000} /* system version */, 100000/* candidate version */,
943                 false /* expected validity */);
944     }
945 
946     @Test
testEqualPackageVersionValid()947     public void testEqualPackageVersionValid() {
948         checkPackageVersions(new int[]{100000} /* system version */, 100000 /* candidate version */,
949                 true /* expected validity */);
950     }
951 
952     @Test
testGreaterPackageVersionValid()953     public void testGreaterPackageVersionValid() {
954         checkPackageVersions(new int[]{100000} /* system versions */, 200000 /* candidate version */,
955                 true /* expected validity */);
956     }
957 
958     @Test
testLastFiveDigitsIgnored()959     public void testLastFiveDigitsIgnored() {
960         checkPackageVersions(new int[]{654321} /* system version */, 612345 /* candidate version */,
961                 true /* expected validity */);
962     }
963 
964     @Test
testMinimumSystemVersionUsedTwoDefaultsCandidateValid()965     public void testMinimumSystemVersionUsedTwoDefaultsCandidateValid() {
966         checkPackageVersions(new int[]{300000, 100000} /* system versions */,
967                 200000 /* candidate version */, true /* expected validity */);
968     }
969 
970     @Test
testMinimumSystemVersionUsedTwoDefaultsCandidateInvalid()971     public void testMinimumSystemVersionUsedTwoDefaultsCandidateInvalid() {
972         checkPackageVersions(new int[]{300000, 200000} /* system versions */,
973                  100000 /* candidate version */, false /* expected validity */);
974     }
975 
976     @Test
testMinimumSystemVersionUsedSeveralDefaultsCandidateValid()977     public void testMinimumSystemVersionUsedSeveralDefaultsCandidateValid() {
978         checkPackageVersions(new int[]{100000, 200000, 300000, 400000, 500000} /* system versions */,
979                 100000 /* candidate version */, true /* expected validity */);
980     }
981 
982     @Test
testMinimumSystemVersionUsedSeveralDefaultsCandidateInvalid()983     public void testMinimumSystemVersionUsedSeveralDefaultsCandidateInvalid() {
984         checkPackageVersions(new int[]{200000, 300000, 400000, 500000, 600000} /* system versions */,
985                 100000 /* candidate version */, false /* expected validity */);
986     }
987 
988     /**
989      * Utility method for checking that package version restriction works as it should.
990      * I.e. that a package with lower version than the system-default is not valid and that a
991      * package with greater than or equal version code is considered valid.
992      */
checkPackageVersions(int[] systemVersions, int candidateVersion, boolean candidateShouldBeValid)993     private void checkPackageVersions(int[] systemVersions, int candidateVersion,
994             boolean candidateShouldBeValid) {
995         int numSystemPackages = systemVersions.length;
996         int numPackages = systemVersions.length + 1;
997         String candidatePackage = "candidatePackage";
998         String systemPackage = "systemPackage";
999 
1000         // Each package needs a valid signature since we set isDebuggable to false
1001         Signature signature = new Signature("11");
1002         String encodedSignatureString =
1003             Base64.encodeToString(signature.toByteArray(), Base64.DEFAULT);
1004 
1005         // Set up config
1006         // 1. candidatePackage
1007         // 2-N. default available packages
1008         WebViewProviderInfo[] packages = new WebViewProviderInfo[numPackages];
1009         packages[0] = new WebViewProviderInfo(candidatePackage, "",
1010                 false /* available by default */, false /* fallback */,
1011                 new String[]{encodedSignatureString});
1012         for(int n = 1; n < numSystemPackages + 1; n++) {
1013             packages[n] = new WebViewProviderInfo(systemPackage + n, "",
1014                     true /* available by default */, false /* fallback */,
1015                     new String[]{encodedSignatureString});
1016         }
1017         setupWithPackagesNonDebuggable(packages);
1018 
1019         // Set package infos
1020         mTestSystemImpl.setPackageInfo(
1021                 createPackageInfo(candidatePackage, true /* enabled */, true /* valid */,
1022                     true /* installed */, new Signature[]{signature}, 0 /* updateTime */,
1023                     false /* hidden */, candidateVersion, false /* isSystemApp */));
1024         for(int n = 1; n < numSystemPackages + 1; n++) {
1025             mTestSystemImpl.setPackageInfo(
1026                     createPackageInfo(systemPackage + n, true /* enabled */, true /* valid */,
1027                         true /* installed */, new Signature[]{signature}, 0 /* updateTime */,
1028                         false /* hidden */, systemVersions[n-1], true /* isSystemApp */));
1029         }
1030 
1031         WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages();
1032         int expectedNumValidPackages = numSystemPackages;
1033         if (candidateShouldBeValid) {
1034             expectedNumValidPackages++;
1035         } else {
1036             // Ensure the candidate package is not one of the valid packages
1037             for(int n = 0; n < validPackages.length; n++) {
1038                 assertFalse(candidatePackage.equals(validPackages[n].packageName));
1039             }
1040         }
1041 
1042         assertEquals(expectedNumValidPackages, validPackages.length);
1043 
1044         runWebViewBootPreparationOnMainSync();
1045 
1046         // The non-system package is not available by default so it shouldn't be used here
1047         checkPreparationPhasesForPackage(systemPackage + "1", 1);
1048 
1049         // Try explicitly switching to the candidate package
1050         String packageChange = mWebViewUpdateServiceImpl.changeProviderAndSetting(candidatePackage);
1051         if (candidateShouldBeValid) {
1052             assertEquals(candidatePackage, packageChange);
1053             checkPreparationPhasesForPackage(candidatePackage, 1);
1054         } else {
1055             assertEquals(systemPackage + "1", packageChange);
1056             // We didn't change package so the webview preparation won't run here
1057         }
1058     }
1059 
1060     /**
1061      * Ensure that the update service does use an uninstalled package when that is the only
1062      * package available.
1063      */
1064     @Test
testWithSingleUninstalledPackage()1065     public void testWithSingleUninstalledPackage() {
1066         String testPackageName = "test.package.name";
1067         WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
1068                 new WebViewProviderInfo(testPackageName, "",
1069                         true /*default available*/, false /* fallback */, null)};
1070         setupWithPackages(webviewPackages);
1071         mTestSystemImpl.setPackageInfo(createPackageInfo(testPackageName, true /* enabled */,
1072                     true /* valid */, false /* installed */));
1073 
1074         runWebViewBootPreparationOnMainSync();
1075 
1076         Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
1077                 Matchers.anyObject());
1078         WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
1079         assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
1080         assertEquals(null, mWebViewUpdateServiceImpl.getCurrentWebViewPackage());
1081     }
1082 
1083     @Test
testNonhiddenPackageUserOverHidden()1084     public void testNonhiddenPackageUserOverHidden() {
1085         checkVisiblePackageUserOverNonVisible(false /* multiUser*/, PackageRemovalType.HIDE);
1086         checkVisiblePackageUserOverNonVisible(true /* multiUser*/, PackageRemovalType.HIDE);
1087     }
1088 
1089     @Test
testInstalledPackageUsedOverUninstalled()1090     public void testInstalledPackageUsedOverUninstalled() {
1091         checkVisiblePackageUserOverNonVisible(false /* multiUser*/, PackageRemovalType.UNINSTALL);
1092         checkVisiblePackageUserOverNonVisible(true /* multiUser*/, PackageRemovalType.UNINSTALL);
1093     }
1094 
checkVisiblePackageUserOverNonVisible(boolean multiUser, PackageRemovalType removalType)1095     private void checkVisiblePackageUserOverNonVisible(boolean multiUser,
1096             PackageRemovalType removalType) {
1097         assert removalType != PackageRemovalType.DISABLE;
1098         boolean testUninstalled = removalType == PackageRemovalType.UNINSTALL;
1099         boolean testHidden = removalType == PackageRemovalType.HIDE;
1100         String installedPackage = "installedPackage";
1101         String uninstalledPackage = "uninstalledPackage";
1102         WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
1103             new WebViewProviderInfo(uninstalledPackage, "", true /* available by default */,
1104                     false /* fallback */, null),
1105             new WebViewProviderInfo(installedPackage, "", true /* available by default */,
1106                     false /* fallback */, null)};
1107 
1108         setupWithPackages(webviewPackages);
1109         int secondaryUserId = 5;
1110         if (multiUser) {
1111             mTestSystemImpl.addUser(secondaryUserId);
1112             // Install all packages for the primary user.
1113             setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, webviewPackages);
1114             mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(
1115                     installedPackage, true /* enabled */, true /* valid */, true /* installed */));
1116             // Hide or uninstall the primary package for the second user
1117             mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
1118                     true /* valid */, (testUninstalled ? false : true) /* installed */,
1119                     null /* signatures */, 0 /* updateTime */, (testHidden ? true : false)));
1120         } else {
1121             mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */,
1122                     true /* valid */, true /* installed */));
1123             // Hide or uninstall the primary package
1124             mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
1125                     true /* valid */, (testUninstalled ? false : true) /* installed */,
1126                     null /* signatures */, 0 /* updateTime */, (testHidden ? true : false)));
1127         }
1128 
1129         runWebViewBootPreparationOnMainSync();
1130 
1131         checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
1132     }
1133 
1134     @Test
testCantSwitchToHiddenPackage()1135     public void testCantSwitchToHiddenPackage () {
1136         checkCantSwitchToNonVisiblePackage(false /* true == uninstalled, false == hidden */);
1137     }
1138 
1139 
1140     @Test
testCantSwitchToUninstalledPackage()1141     public void testCantSwitchToUninstalledPackage () {
1142         checkCantSwitchToNonVisiblePackage(true /* true == uninstalled, false == hidden */);
1143     }
1144 
1145     /**
1146      * Ensure that we won't prioritize an uninstalled (or hidden) package even if it is user-chosen.
1147      */
checkCantSwitchToNonVisiblePackage(boolean uninstalledNotHidden)1148     private void checkCantSwitchToNonVisiblePackage(boolean uninstalledNotHidden) {
1149         boolean testUninstalled = uninstalledNotHidden;
1150         boolean testHidden = !uninstalledNotHidden;
1151         String installedPackage = "installedPackage";
1152         String uninstalledPackage = "uninstalledPackage";
1153         WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
1154             new WebViewProviderInfo(uninstalledPackage, "", true /* available by default */,
1155                     false /* fallback */, null),
1156             new WebViewProviderInfo(installedPackage, "", true /* available by default */,
1157                     false /* fallback */, null)};
1158 
1159         setupWithPackages(webviewPackages);
1160         int secondaryUserId = 412;
1161         mTestSystemImpl.addUser(secondaryUserId);
1162 
1163         // Let all packages be installed and enabled for the primary user.
1164         setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, webviewPackages);
1165         // Only uninstall the 'uninstalled package' for the secondary user.
1166         mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(installedPackage,
1167                 true /* enabled */, true /* valid */, true /* installed */));
1168         mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(uninstalledPackage,
1169                 true /* enabled */, true /* valid */, !testUninstalled /* installed */,
1170                 null /* signatures */, 0 /* updateTime */, testHidden /* hidden */));
1171 
1172         runWebViewBootPreparationOnMainSync();
1173 
1174         checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
1175 
1176         // ensure that we don't switch to the uninstalled package (it will be used if it becomes
1177         // installed later)
1178         assertEquals(installedPackage,
1179                 mWebViewUpdateServiceImpl.changeProviderAndSetting(uninstalledPackage));
1180 
1181         // Ensure both packages are considered valid.
1182         assertEquals(2, mWebViewUpdateServiceImpl.getValidWebViewPackages().length);
1183 
1184 
1185         // We should only have called onWebViewProviderChanged once (before calling
1186         // changeProviderAndSetting
1187         Mockito.verify(mTestSystemImpl, Mockito.times(1)).onWebViewProviderChanged(
1188                 Mockito.argThat(new IsPackageInfoWithName(installedPackage)));
1189     }
1190 
1191     @Test
testHiddenPackageNotPrioritizedEvenIfChosen()1192     public void testHiddenPackageNotPrioritizedEvenIfChosen() {
1193         checkNonvisiblePackageNotPrioritizedEvenIfChosen(
1194                 false /* true == uninstalled, false == hidden */);
1195     }
1196 
1197     @Test
testUninstalledPackageNotPrioritizedEvenIfChosen()1198     public void testUninstalledPackageNotPrioritizedEvenIfChosen() {
1199         checkNonvisiblePackageNotPrioritizedEvenIfChosen(
1200                 true /* true == uninstalled, false == hidden */);
1201     }
1202 
checkNonvisiblePackageNotPrioritizedEvenIfChosen(boolean uninstalledNotHidden)1203     public void checkNonvisiblePackageNotPrioritizedEvenIfChosen(boolean uninstalledNotHidden) {
1204         boolean testUninstalled = uninstalledNotHidden;
1205         boolean testHidden = !uninstalledNotHidden;
1206         String installedPackage = "installedPackage";
1207         String uninstalledPackage = "uninstalledPackage";
1208         WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
1209             new WebViewProviderInfo(uninstalledPackage, "", true /* available by default */,
1210                     false /* fallback */, null),
1211             new WebViewProviderInfo(installedPackage, "", true /* available by default */,
1212                     false /* fallback */, null)};
1213 
1214         setupWithPackages(webviewPackages);
1215         int secondaryUserId = 4;
1216         mTestSystemImpl.addUser(secondaryUserId);
1217 
1218         setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, webviewPackages);
1219         mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(installedPackage,
1220                 true /* enabled */, true /* valid */, true /* installed */));
1221         mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(uninstalledPackage,
1222                 true /* enabled */, true /* valid */,
1223                 (testUninstalled ? false : true) /* installed */, null /* signatures */,
1224                 0 /* updateTime */, (testHidden ? true : false) /* hidden */));
1225 
1226         // Start with the setting pointing to the uninstalled package
1227         mTestSystemImpl.updateUserSetting(null, uninstalledPackage);
1228 
1229         runWebViewBootPreparationOnMainSync();
1230 
1231         checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
1232     }
1233 
1234     @Test
testPreparationRunsIffNewPackage()1235     public void testPreparationRunsIffNewPackage() {
1236         String primaryPackage = "primary";
1237         String secondaryPackage = "secondary";
1238         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
1239             new WebViewProviderInfo(
1240                     primaryPackage, "", true /* default available */, false /* fallback */, null),
1241             new WebViewProviderInfo(
1242                     secondaryPackage, "", true /* default available */, false /* fallback */,
1243                     null)};
1244         setupWithPackages(packages);
1245         mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
1246                     true /* valid */, true /* installed */, null /* signatures */,
1247                     10 /* lastUpdateTime*/ ));
1248         mTestSystemImpl.setPackageInfo(createPackageInfo(secondaryPackage, true /* enabled */,
1249                     true /* valid */, true /* installed */));
1250 
1251         runWebViewBootPreparationOnMainSync();
1252         checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation phase */);
1253 
1254         mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
1255                     true /* valid */, true /* installed */, null /* signatures */,
1256                     20 /* lastUpdateTime*/ ));
1257         mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
1258                 WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
1259         // The package has now changed - ensure that we have run the preparation phase a second time
1260         checkPreparationPhasesForPackage(primaryPackage, 2 /* second preparation phase */);
1261 
1262 
1263         mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
1264                     true /* valid */, true /* installed */, null /* signatures */,
1265                     50 /* lastUpdateTime*/ ));
1266         // Receive intent for different user
1267         mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
1268                 WebViewUpdateService.PACKAGE_ADDED_REPLACED, 2);
1269 
1270         checkPreparationPhasesForPackage(primaryPackage, 3 /* third preparation phase */);
1271     }
1272 
1273     @Test
testGetCurrentWebViewPackage()1274     public void testGetCurrentWebViewPackage() {
1275         PackageInfo firstPackage = createPackageInfo("first", true /* enabled */,
1276                         true /* valid */, true /* installed */);
1277         firstPackage.setLongVersionCode(100);
1278         firstPackage.versionName = "first package version";
1279         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
1280             new WebViewProviderInfo(firstPackage.packageName, "", true, false, null)};
1281         setupWithPackages(packages);
1282         mTestSystemImpl.setPackageInfo(firstPackage);
1283 
1284         runWebViewBootPreparationOnMainSync();
1285 
1286         Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
1287                 Mockito.argThat(new IsPackageInfoWithName(firstPackage.packageName)));
1288 
1289         mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
1290 
1291         // Ensure the API is correct before running waitForAndGetProvider
1292         assertEquals(firstPackage.packageName,
1293                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName);
1294         assertEquals(firstPackage.getLongVersionCode(),
1295                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().getLongVersionCode());
1296         assertEquals(firstPackage.versionName,
1297                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionName);
1298 
1299         WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
1300         assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
1301         assertEquals(firstPackage.packageName, response.packageInfo.packageName);
1302 
1303         // Ensure the API is still correct after running waitForAndGetProvider
1304         assertEquals(firstPackage.packageName,
1305                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName);
1306         assertEquals(firstPackage.getLongVersionCode(),
1307                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().getLongVersionCode());
1308         assertEquals(firstPackage.versionName,
1309                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionName);
1310     }
1311 
1312     @Test
testMultiProcessEnabledByDefault()1313     public void testMultiProcessEnabledByDefault() {
1314         testMultiProcessByDefault(true /* enabledByDefault */);
1315     }
1316 
1317     @Test
testMultiProcessDisabledByDefault()1318     public void testMultiProcessDisabledByDefault() {
1319         testMultiProcessByDefault(false /* enabledByDefault */);
1320     }
1321 
testMultiProcessByDefault(boolean enabledByDefault)1322     private void testMultiProcessByDefault(boolean enabledByDefault) {
1323         String primaryPackage = "primary";
1324         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
1325             new WebViewProviderInfo(
1326                     primaryPackage, "", true /* default available */, false /* fallback */, null)};
1327         setupWithPackagesAndMultiProcess(packages, enabledByDefault);
1328         mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
1329                     true /* valid */, true /* installed */, null /* signatures */,
1330                     10 /* lastUpdateTime*/, false /* not hidden */, 1000 /* versionCode */,
1331                     false /* isSystemApp */));
1332 
1333         runWebViewBootPreparationOnMainSync();
1334         checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation phase */);
1335 
1336         // Check it's off by default
1337         assertEquals(enabledByDefault, mWebViewUpdateServiceImpl.isMultiProcessEnabled());
1338 
1339         // Test toggling it
1340         mWebViewUpdateServiceImpl.enableMultiProcess(!enabledByDefault);
1341         assertEquals(!enabledByDefault, mWebViewUpdateServiceImpl.isMultiProcessEnabled());
1342         mWebViewUpdateServiceImpl.enableMultiProcess(enabledByDefault);
1343         assertEquals(enabledByDefault, mWebViewUpdateServiceImpl.isMultiProcessEnabled());
1344     }
1345 
1346     @Test
testMultiProcessEnabledByDefaultWithSettingsValue()1347     public void testMultiProcessEnabledByDefaultWithSettingsValue() {
1348         testMultiProcessByDefaultWithSettingsValue(
1349                 true /* enabledByDefault */, Integer.MIN_VALUE, false /* expectEnabled */);
1350         testMultiProcessByDefaultWithSettingsValue(
1351                 true /* enabledByDefault */, -999999, true /* expectEnabled */);
1352         testMultiProcessByDefaultWithSettingsValue(
1353                 true /* enabledByDefault */, 0, true /* expectEnabled */);
1354         testMultiProcessByDefaultWithSettingsValue(
1355                 true /* enabledByDefault */, 999999, true /* expectEnabled */);
1356     }
1357 
1358     @Test
testMultiProcessDisabledByDefaultWithSettingsValue()1359     public void testMultiProcessDisabledByDefaultWithSettingsValue() {
1360         testMultiProcessByDefaultWithSettingsValue(
1361                 false /* enabledByDefault */, Integer.MIN_VALUE, false /* expectEnabled */);
1362         testMultiProcessByDefaultWithSettingsValue(
1363                 false /* enabledByDefault */, 0, false /* expectEnabled */);
1364         testMultiProcessByDefaultWithSettingsValue(
1365                 false /* enabledByDefault */, 999999, false /* expectEnabled */);
1366         testMultiProcessByDefaultWithSettingsValue(
1367                 false /* enabledByDefault */, Integer.MAX_VALUE, true /* expectEnabled */);
1368     }
1369 
1370     /**
1371      * Test the logic of the multiprocess setting depending on whether multiprocess is enabled by
1372      * default, and what the setting is set to.
1373      * @param enabledByDefault whether multiprocess is enabled by default.
1374      * @param settingValue value of the multiprocess setting.
1375      */
testMultiProcessByDefaultWithSettingsValue( boolean enabledByDefault, int settingValue, boolean expectEnabled)1376     private void testMultiProcessByDefaultWithSettingsValue(
1377             boolean enabledByDefault, int settingValue, boolean expectEnabled) {
1378         String primaryPackage = "primary";
1379         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
1380             new WebViewProviderInfo(
1381                     primaryPackage, "", true /* default available */, false /* fallback */, null)};
1382         setupWithPackagesAndMultiProcess(packages, enabledByDefault);
1383         mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
1384                     true /* valid */, true /* installed */, null /* signatures */,
1385                     10 /* lastUpdateTime*/, false /* not hidden */, 1000 /* versionCode */,
1386                     false /* isSystemApp */));
1387 
1388         runWebViewBootPreparationOnMainSync();
1389         checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation phase */);
1390 
1391         mTestSystemImpl.setMultiProcessSetting(null /* context */, settingValue);
1392 
1393         assertEquals(expectEnabled, mWebViewUpdateServiceImpl.isMultiProcessEnabled());
1394     }
1395 
1396 
1397     /**
1398      * Ensure that packages with a targetSdkVersion targeting the current platform are valid, and
1399      * that packages targeting an older version are not valid.
1400      */
1401     @Test
testTargetSdkVersionValidity()1402     public void testTargetSdkVersionValidity() {
1403         PackageInfo newSdkPackage = createPackageInfo("newTargetSdkPackage",
1404             true /* enabled */, true /* valid */, true /* installed */);
1405         newSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
1406         PackageInfo currentSdkPackage = createPackageInfo("currentTargetSdkPackage",
1407             true /* enabled */, true /* valid */, true /* installed */);
1408         currentSdkPackage.applicationInfo.targetSdkVersion = UserPackage.MINIMUM_SUPPORTED_SDK;
1409         PackageInfo oldSdkPackage = createPackageInfo("oldTargetSdkPackage",
1410             true /* enabled */, true /* valid */, true /* installed */);
1411         oldSdkPackage.applicationInfo.targetSdkVersion = UserPackage.MINIMUM_SUPPORTED_SDK - 1;
1412 
1413         WebViewProviderInfo newSdkProviderInfo =
1414                 new WebViewProviderInfo(newSdkPackage.packageName, "", true, false, null);
1415         WebViewProviderInfo currentSdkProviderInfo =
1416                 new WebViewProviderInfo(currentSdkPackage.packageName, "", true, false, null);
1417         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
1418             new WebViewProviderInfo(oldSdkPackage.packageName, "", true, false, null),
1419             currentSdkProviderInfo, newSdkProviderInfo};
1420         setupWithPackages(packages);
1421 ;
1422         mTestSystemImpl.setPackageInfo(newSdkPackage);
1423         mTestSystemImpl.setPackageInfo(currentSdkPackage);
1424         mTestSystemImpl.setPackageInfo(oldSdkPackage);
1425 
1426         assertArrayEquals(new WebViewProviderInfo[]{currentSdkProviderInfo, newSdkProviderInfo},
1427                 mWebViewUpdateServiceImpl.getValidWebViewPackages());
1428 
1429         runWebViewBootPreparationOnMainSync();
1430 
1431         checkPreparationPhasesForPackage(currentSdkPackage.packageName,
1432                 1 /* first preparation phase */);
1433     }
1434 }
1435