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