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