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