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