WebViewUpdateServiceTest.java revision 95f7e8e06dd0003a813f7a4b3c718c68d0fa4a01
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;
26
27import android.webkit.WebViewFactory;
28import android.webkit.WebViewProviderInfo;
29import android.webkit.WebViewProviderResponse;
30
31import java.util.concurrent.CountDownLatch;
32
33import org.hamcrest.Description;
34
35import org.mockito.Mockito;
36import org.mockito.Matchers;
37import org.mockito.ArgumentMatcher;
38
39
40/**
41 * Tests for WebViewUpdateService
42 */
43public class WebViewUpdateServiceTest extends AndroidTestCase {
44    private final static String TAG = WebViewUpdateServiceTest.class.getSimpleName();
45
46    private WebViewUpdateServiceImpl mWebViewUpdateServiceImpl;
47    private TestSystemImpl mTestSystemImpl;
48
49    private static final String WEBVIEW_LIBRARY_FLAG = "com.android.webview.WebViewLibrary";
50
51    @Override
52    protected void setUp() throws Exception {
53        super.setUp();
54    }
55
56    /**
57     * Creates a new instance.
58     */
59    public WebViewUpdateServiceTest() {
60    }
61
62    private void setupWithPackages(WebViewProviderInfo[] packages) {
63        setupWithPackages(packages, true);
64    }
65
66    private void setupWithPackages(WebViewProviderInfo[] packages,
67            boolean fallbackLogicEnabled) {
68        setupWithPackages(packages, fallbackLogicEnabled, 1);
69    }
70
71    private void setupWithPackages(WebViewProviderInfo[] packages,
72            boolean fallbackLogicEnabled, int numRelros) {
73        setupWithPackages(packages, fallbackLogicEnabled, numRelros,
74                true /* isDebuggable == true -> don't check package signatures */);
75    }
76
77    private void setupWithPackages(WebViewProviderInfo[] packages,
78            boolean fallbackLogicEnabled, int numRelros, boolean isDebuggable) {
79        TestSystemImpl testing = new TestSystemImpl(packages, fallbackLogicEnabled, numRelros,
80                isDebuggable);
81        mTestSystemImpl = Mockito.spy(testing);
82        mWebViewUpdateServiceImpl =
83            new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
84    }
85
86    private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) {
87        for(WebViewProviderInfo wpi : providers) {
88            mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true /* enabled */,
89                        true /* valid */));
90        }
91    }
92
93    private void checkCertainPackageUsedAfterWebViewBootPreparation(String expectedProviderName,
94            WebViewProviderInfo[] webviewPackages) {
95        checkCertainPackageUsedAfterWebViewBootPreparation(
96                expectedProviderName, webviewPackages, 1);
97    }
98
99    private void checkCertainPackageUsedAfterWebViewBootPreparation(String expectedProviderName,
100            WebViewProviderInfo[] webviewPackages, int numRelros) {
101        setupWithPackages(webviewPackages, true, numRelros);
102        // Add (enabled and valid) package infos for each provider
103        setEnabledAndValidPackageInfos(webviewPackages);
104
105        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
106
107        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
108                Mockito.argThat(new IsPackageInfoWithName(expectedProviderName)));
109
110        for (int n = 0; n < numRelros; n++) {
111            mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
112        }
113
114        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
115        assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
116        assertEquals(expectedProviderName, response.packageInfo.packageName);
117    }
118
119    // For matching the package name of a PackageInfo
120    private class IsPackageInfoWithName extends ArgumentMatcher<PackageInfo> {
121        private final String mPackageName;
122
123        IsPackageInfoWithName(String name) {
124            mPackageName = name;
125        }
126
127        @Override
128        public boolean matches(Object p) {
129            return ((PackageInfo) p).packageName.equals(mPackageName);
130        }
131
132        // Provide a more useful description in case of mismatch
133        @Override
134        public void describeTo (Description description) {
135            description.appendText(String.format("PackageInfo with name '%s'", mPackageName));
136        }
137    }
138
139    private static PackageInfo createPackageInfo(
140            String packageName, boolean enabled, boolean valid) {
141        PackageInfo p = new PackageInfo();
142        p.packageName = packageName;
143        p.applicationInfo = new ApplicationInfo();
144        p.applicationInfo.enabled = enabled;
145        p.applicationInfo.metaData = new Bundle();
146        if (valid) {
147            // no flag means invalid
148            p.applicationInfo.metaData.putString(WEBVIEW_LIBRARY_FLAG, "blah");
149        }
150        return p;
151    }
152
153    private static PackageInfo createPackageInfo(
154            String packageName, boolean enabled, boolean valid, Signature[] signatures) {
155        PackageInfo p = createPackageInfo(packageName, enabled, valid);
156        p.signatures = signatures;
157        return p;
158    }
159
160    private void checkPreparationPhasesForPackage(String expectedPackage, int numPreparation) {
161        // Verify that onWebViewProviderChanged was called for the numPreparation'th time for the
162        // expected package
163        Mockito.verify(mTestSystemImpl, Mockito.times(numPreparation)).onWebViewProviderChanged(
164                Mockito.argThat(new IsPackageInfoWithName(expectedPackage)));
165
166        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
167
168        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
169        assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
170        assertEquals(expectedPackage, response.packageInfo.packageName);
171    }
172
173
174    // ****************
175    // Tests
176    // ****************
177
178
179    public void testWithSinglePackage() {
180        String testPackageName = "test.package.name";
181        checkCertainPackageUsedAfterWebViewBootPreparation(testPackageName,
182                new WebViewProviderInfo[] {
183                    new WebViewProviderInfo(testPackageName, "",
184                            true /*default available*/, false /* fallback */, null)});
185    }
186
187    public void testDefaultPackageUsedOverNonDefault() {
188        String defaultPackage = "defaultPackage";
189        String nonDefaultPackage = "nonDefaultPackage";
190        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
191            new WebViewProviderInfo(nonDefaultPackage, "", false, false, null),
192            new WebViewProviderInfo(defaultPackage, "", true, false, null)};
193        checkCertainPackageUsedAfterWebViewBootPreparation(defaultPackage, packages);
194    }
195
196    public void testSeveralRelros() {
197        String singlePackage = "singlePackage";
198        checkCertainPackageUsedAfterWebViewBootPreparation(
199                singlePackage,
200                new WebViewProviderInfo[] {
201                    new WebViewProviderInfo(singlePackage, "", true /*def av*/, false, null)},
202                2);
203    }
204
205    // Ensure that package with valid signatures is chosen rather than package with invalid
206    // signatures.
207    public void testWithSignatures() {
208        String validPackage = "valid package";
209        String invalidPackage = "invalid package";
210
211        Signature validSignature = new Signature("11");
212        Signature invalidExpectedSignature = new Signature("22");
213        Signature invalidPackageSignature = new Signature("33");
214
215        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
216            new WebViewProviderInfo(invalidPackage, "", true, false, new String[]{
217                        Base64.encodeToString(
218                                invalidExpectedSignature.toByteArray(), Base64.DEFAULT)}),
219            new WebViewProviderInfo(validPackage, "", true, false, new String[]{
220                        Base64.encodeToString(
221                                validSignature.toByteArray(), Base64.DEFAULT)})
222        };
223        setupWithPackages(packages, true /* fallback logic enabled */, 1 /* numRelros */,
224                false /* isDebuggable */);
225        mTestSystemImpl.setPackageInfo(createPackageInfo(invalidPackage, true /* enabled */,
226                    true /* valid */, new Signature[]{invalidPackageSignature}));
227        mTestSystemImpl.setPackageInfo(createPackageInfo(validPackage, true /* enabled */,
228                    true /* valid */, new Signature[]{validSignature}));
229
230        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
231
232
233        checkPreparationPhasesForPackage(validPackage, 1 /* first preparation for this package */);
234
235        WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages();
236        assertEquals(1, validPackages.length);
237        assertEquals(validPackage, validPackages[0].packageName);
238    }
239
240    public void testFailWaitingForRelro() {
241        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
242            new WebViewProviderInfo("packagename", "", true, true, null)};
243        setupWithPackages(packages);
244        setEnabledAndValidPackageInfos(packages);
245
246        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
247
248        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
249                Mockito.argThat(new IsPackageInfoWithName(packages[0].packageName)));
250
251        // Never call notifyRelroCreation()
252
253        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
254        assertEquals(WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO, response.status);
255    }
256
257    public void testFailListingEmptyWebviewPackages() {
258        WebViewProviderInfo[] packages = new WebViewProviderInfo[0];
259        setupWithPackages(packages);
260        setEnabledAndValidPackageInfos(packages);
261
262        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
263
264        Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
265                Matchers.anyObject());
266
267        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
268        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
269    }
270
271    public void testFailListingInvalidWebviewPackage() {
272        WebViewProviderInfo wpi = new WebViewProviderInfo("package", "", true, true, null);
273        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi};
274        setupWithPackages(packages);
275        mTestSystemImpl.setPackageInfo(
276                createPackageInfo(wpi.packageName, true /* enabled */, false /* valid */));
277
278        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
279
280        Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
281                Matchers.anyObject());
282
283        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
284        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
285
286        // Verify that we can recover from failing to list webview packages.
287        mTestSystemImpl.setPackageInfo(
288                createPackageInfo(wpi.packageName, true /* enabled */, true /* valid */));
289        mWebViewUpdateServiceImpl.packageStateChanged(wpi.packageName,
290                WebViewUpdateService.PACKAGE_ADDED_REPLACED);
291
292        checkPreparationPhasesForPackage(wpi.packageName, 1);
293    }
294
295    // Test that switching provider using changeProviderAndSetting works.
296    public void testSwitchingProvider() {
297        String firstPackage = "first";
298        String secondPackage = "second";
299        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
300            new WebViewProviderInfo(firstPackage, "", true, false, null),
301            new WebViewProviderInfo(secondPackage, "", true, false, null)};
302        checkSwitchingProvider(packages, firstPackage, secondPackage);
303    }
304
305    public void testSwitchingProviderToNonDefault() {
306        String defaultPackage = "defaultPackage";
307        String nonDefaultPackage = "nonDefaultPackage";
308        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
309            new WebViewProviderInfo(defaultPackage, "", true, false, null),
310            new WebViewProviderInfo(nonDefaultPackage, "", false, false, null)};
311        checkSwitchingProvider(packages, defaultPackage, nonDefaultPackage);
312    }
313
314    private void checkSwitchingProvider(WebViewProviderInfo[] packages, String initialPackage,
315            String finalPackage) {
316        checkCertainPackageUsedAfterWebViewBootPreparation(initialPackage, packages);
317
318        mWebViewUpdateServiceImpl.changeProviderAndSetting(finalPackage);
319        checkPreparationPhasesForPackage(finalPackage, 1 /* first preparation for this package */);
320
321        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(initialPackage));
322    }
323
324    // Change provider during relro creation by using changeProviderAndSetting
325    public void testSwitchingProviderDuringRelroCreation() {
326        checkChangingProviderDuringRelroCreation(true);
327    }
328
329    // Change provider during relro creation by enabling a provider
330    public void testChangingProviderThroughEnablingDuringRelroCreation() {
331        checkChangingProviderDuringRelroCreation(false);
332    }
333
334    private void checkChangingProviderDuringRelroCreation(boolean settingsChange) {
335        String firstPackage = "first";
336        String secondPackage = "second";
337        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
338            new WebViewProviderInfo(firstPackage, "", true, false, null),
339            new WebViewProviderInfo(secondPackage, "", true, false, null)};
340        setupWithPackages(packages);
341        if (settingsChange) {
342            // Have all packages be enabled, so that we can change provider however we want to
343            setEnabledAndValidPackageInfos(packages);
344        } else {
345            // Have all packages be disabled so that we can change one to enabled later
346            for(WebViewProviderInfo wpi : packages) {
347                mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName,
348                            false /* enabled */, true /* valid */));
349            }
350        }
351
352        CountDownLatch countdown = new CountDownLatch(1);
353
354        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
355
356        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
357                Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
358
359        assertEquals(firstPackage, mWebViewUpdateServiceImpl.getCurrentWebViewPackageName());
360
361        new Thread(new Runnable() {
362            @Override
363            public void run() {
364                WebViewProviderResponse threadResponse =
365                    mWebViewUpdateServiceImpl.waitForAndGetProvider();
366                assertEquals(WebViewFactory.LIBLOAD_SUCCESS, threadResponse.status);
367                assertEquals(secondPackage, threadResponse.packageInfo.packageName);
368                // Verify that we killed the first package
369                Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
370                countdown.countDown();
371            }
372        }).start();
373        try {
374            Thread.sleep(1000); // Let the new thread run / be blocked
375        } catch (InterruptedException e) {
376        }
377
378        if (settingsChange) {
379            mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
380        } else {
381            // Switch provider by enabling the second one
382            mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
383                        true /* valid */));
384            mWebViewUpdateServiceImpl.packageStateChanged(
385                    secondPackage, WebViewUpdateService.PACKAGE_CHANGED);
386        }
387        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
388        // first package done, should start on second
389
390        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
391                Mockito.argThat(new IsPackageInfoWithName(secondPackage)));
392
393        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
394        // second package done, the other thread should now be unblocked
395        try {
396            countdown.await();
397        } catch (InterruptedException e) {
398        }
399    }
400
401    public void testRunFallbackLogicIfEnabled() {
402        checkFallbackLogicBeingRun(true);
403    }
404
405    public void testDontRunFallbackLogicIfDisabled() {
406        checkFallbackLogicBeingRun(false);
407    }
408
409    private void checkFallbackLogicBeingRun(boolean fallbackLogicEnabled) {
410        String primaryPackage = "primary";
411        String fallbackPackage = "fallback";
412        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
413            new WebViewProviderInfo(
414                    primaryPackage, "", true /* default available */, false /* fallback */, null),
415            new WebViewProviderInfo(
416                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
417        setupWithPackages(packages, fallbackLogicEnabled);
418        setEnabledAndValidPackageInfos(packages);
419
420        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
421        // Verify that we disable the fallback package if fallback logic enabled, and don't disable
422        // the fallback package if that logic is disabled
423        if (fallbackLogicEnabled) {
424            Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
425                    Matchers.anyObject(), Mockito.eq(fallbackPackage));
426        } else {
427            Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
428                    Matchers.anyObject(), Matchers.anyObject());
429        }
430        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
431                Mockito.argThat(new IsPackageInfoWithName(primaryPackage)));
432
433        // Enable fallback package
434        mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
435                        true /* valid */));
436        mWebViewUpdateServiceImpl.packageStateChanged(
437                fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED);
438
439        if (fallbackLogicEnabled) {
440            // Check that we have now disabled the fallback package twice
441            Mockito.verify(mTestSystemImpl, Mockito.times(2)).uninstallAndDisablePackageForAllUsers(
442                    Matchers.anyObject(), Mockito.eq(fallbackPackage));
443        } else {
444            // Check that we still haven't disabled any package
445            Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
446                    Matchers.anyObject(), Matchers.anyObject());
447        }
448    }
449
450    /**
451     * Scenario for installing primary package when fallback enabled.
452     * 1. Start with only fallback installed
453     * 2. Install non-fallback
454     * 3. Fallback should be disabled
455     */
456    public void testInstallingNonFallbackPackage() {
457        String primaryPackage = "primary";
458        String fallbackPackage = "fallback";
459        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
460            new WebViewProviderInfo(
461                    primaryPackage, "", true /* default available */, false /* fallback */, null),
462            new WebViewProviderInfo(
463                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
464        setupWithPackages(packages, true /* isFallbackLogicEnabled */);
465        mTestSystemImpl.setPackageInfo(
466                createPackageInfo(fallbackPackage, true /* enabled */ , true /* valid */));
467
468        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
469        Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
470                Matchers.anyObject(), Matchers.anyObject());
471
472        checkPreparationPhasesForPackage(fallbackPackage,
473                1 /* first preparation for this package*/);
474
475        // Install primary package
476        mTestSystemImpl.setPackageInfo(
477                createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */));
478        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
479                WebViewUpdateService.PACKAGE_ADDED_REPLACED);
480
481        // Verify fallback disabled, primary package used as provider, and fallback package killed
482        Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
483                Matchers.anyObject(), Mockito.eq(fallbackPackage));
484        checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation for this package*/);
485        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(fallbackPackage));
486    }
487
488    public void testFallbackChangesEnabledState() {
489        String primaryPackage = "primary";
490        String fallbackPackage = "fallback";
491        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
492            new WebViewProviderInfo(
493                    primaryPackage, "", true /* default available */, false /* fallback */, null),
494            new WebViewProviderInfo(
495                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
496        setupWithPackages(packages, true /* fallbackLogicEnabled */);
497        setEnabledAndValidPackageInfos(packages);
498
499        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
500
501        // Verify fallback disabled at boot when primary package enabled
502        Mockito.verify(mTestSystemImpl).enablePackageForUser(
503                Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
504                Matchers.anyInt());
505
506        checkPreparationPhasesForPackage(primaryPackage, 1);
507
508        // Disable primary package and ensure fallback becomes enabled and used
509        mTestSystemImpl.setPackageInfo(
510                createPackageInfo(primaryPackage, false /* enabled */, true /* valid */));
511        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
512                WebViewUpdateService.PACKAGE_CHANGED);
513
514        Mockito.verify(mTestSystemImpl).enablePackageForUser(
515                Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */,
516                Matchers.anyInt());
517
518        checkPreparationPhasesForPackage(fallbackPackage, 1);
519
520
521        // Again enable primary package and verify primary is used and fallback becomes disabled
522        mTestSystemImpl.setPackageInfo(
523                createPackageInfo(primaryPackage, true /* enabled */, true /* valid */));
524        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
525                WebViewUpdateService.PACKAGE_CHANGED);
526
527        // Verify fallback is disabled a second time when primary package becomes enabled
528        Mockito.verify(mTestSystemImpl, Mockito.times(2)).enablePackageForUser(
529                Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
530                Matchers.anyInt());
531
532        checkPreparationPhasesForPackage(primaryPackage, 2);
533    }
534
535    public void testAddUserWhenFallbackLogicEnabled() {
536        checkAddingNewUser(true);
537    }
538
539    public void testAddUserWhenFallbackLogicDisabled() {
540        checkAddingNewUser(false);
541    }
542
543    public void checkAddingNewUser(boolean fallbackLogicEnabled) {
544        String primaryPackage = "primary";
545        String fallbackPackage = "fallback";
546        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
547            new WebViewProviderInfo(
548                    primaryPackage, "", true /* default available */, false /* fallback */, null),
549            new WebViewProviderInfo(
550                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
551        setupWithPackages(packages, fallbackLogicEnabled);
552        setEnabledAndValidPackageInfos(packages);
553        int newUser = 100;
554        mWebViewUpdateServiceImpl.handleNewUser(newUser);
555        if (fallbackLogicEnabled) {
556            // Verify fallback package becomes disabled for new user
557            Mockito.verify(mTestSystemImpl).enablePackageForUser(
558                    Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
559                    Mockito.eq(newUser));
560        } else {
561            // Verify that we don't disable fallback for new user
562            Mockito.verify(mTestSystemImpl, Mockito.never()).enablePackageForUser(
563                    Mockito.anyObject(), Matchers.anyBoolean() /* enable */,
564                    Matchers.anyInt() /* user */);
565        }
566    }
567
568    /**
569     * Timing dependent test where we verify that the list of valid webview packages becoming empty
570     * at a certain point doesn't crash us or break our state.
571     */
572    public void testNotifyRelroDoesntCrashIfNoPackages() {
573        String firstPackage = "first";
574        String secondPackage = "second";
575        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
576            new WebViewProviderInfo(firstPackage, "", true /* default available */,
577                    false /* fallback */, null),
578            new WebViewProviderInfo(secondPackage, "", true /* default available */,
579                    false /* fallback */, null)};
580        setupWithPackages(packages);
581        // Add (enabled and valid) package infos for each provider
582        setEnabledAndValidPackageInfos(packages);
583
584        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
585
586        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
587                Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
588
589        // Change provider during relro creation to enter a state where we are
590        // waiting for relro creation to complete just to re-run relro creation.
591        // (so that in next notifyRelroCreationCompleted() call we have to list webview packages)
592        mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
593
594        // Make packages invalid to cause exception to be thrown
595        mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
596                    false /* valid */));
597        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
598                    false /* valid */));
599
600        // This shouldn't throw an exception!
601        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
602
603        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
604        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
605
606        // Now make a package valid again and verify that we can switch back to that
607        mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
608                    true /* valid */));
609
610        mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
611                WebViewUpdateService.PACKAGE_ADDED_REPLACED);
612
613        // Ensure we use firstPackage
614        checkPreparationPhasesForPackage(firstPackage, 2 /* second preparation for this package */);
615    }
616
617    /**
618     * Verify that even if a user-chosen package is removed temporarily we start using it again when
619     * it is added back.
620     */
621    public void testTempRemovePackageDoesntSwitchProviderPermanently() {
622        String firstPackage = "first";
623        String secondPackage = "second";
624        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
625            new WebViewProviderInfo(firstPackage, "", true /* default available */,
626                    false /* fallback */, null),
627            new WebViewProviderInfo(secondPackage, "", true /* default available */,
628                    false /* fallback */, null)};
629        checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
630
631        // Explicitly use the second package
632        mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
633        checkPreparationPhasesForPackage(secondPackage, 1 /* first time for this package */);
634
635        // Remove second package (invalidate it) and verify that first package is used
636        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
637                    false /* valid */));
638        mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
639                WebViewUpdateService.PACKAGE_ADDED);
640        checkPreparationPhasesForPackage(firstPackage, 2 /* second time for this package */);
641
642        // Now make the second package valid again and verify that it is used again
643        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
644                    true /* valid */));
645        mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
646                WebViewUpdateService.PACKAGE_ADDED);
647        checkPreparationPhasesForPackage(secondPackage, 2 /* second time for this package */);
648    }
649
650    /**
651     * Ensure that we update the user-chosen setting across boots if the chosen package is no
652     * longer installed and valid.
653     */
654    public void testProviderSettingChangedDuringBootIfProviderNotAvailable() {
655        String chosenPackage = "chosenPackage";
656        String nonChosenPackage = "non-chosenPackage";
657        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
658            new WebViewProviderInfo(chosenPackage, "", true /* default available */,
659                    false /* fallback */, null),
660            new WebViewProviderInfo(nonChosenPackage, "", true /* default available */,
661                    false /* fallback */, null)};
662
663        setupWithPackages(packages);
664        // Only 'install' nonChosenPackage
665        mTestSystemImpl.setPackageInfo(
666                createPackageInfo(nonChosenPackage, true /* enabled */, true /* valid */));
667
668        // Set user-chosen package
669        mTestSystemImpl.updateUserSetting(null, chosenPackage);
670
671        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
672
673        // Verify that we switch the setting to point to the current package
674        Mockito.verify(mTestSystemImpl).updateUserSetting(
675                Mockito.anyObject(), Mockito.eq(nonChosenPackage));
676        assertEquals(nonChosenPackage, mTestSystemImpl.getUserChosenWebViewProvider(null));
677
678        checkPreparationPhasesForPackage(nonChosenPackage, 1);
679    }
680
681    public void testRecoverFailedListingWebViewPackagesSettingsChange() {
682        checkRecoverAfterFailListingWebviewPackages(true);
683    }
684
685    public void testRecoverFailedListingWebViewPackagesAddedPackage() {
686        checkRecoverAfterFailListingWebviewPackages(false);
687    }
688
689    /**
690     * Test that we can recover correctly from failing to list WebView packages.
691     * settingsChange: whether to fail during changeProviderAndSetting or packageStateChanged
692     */
693    public void checkRecoverAfterFailListingWebviewPackages(boolean settingsChange) {
694        String firstPackage = "first";
695        String secondPackage = "second";
696        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
697            new WebViewProviderInfo(firstPackage, "", true /* default available */,
698                    false /* fallback */, null),
699            new WebViewProviderInfo(secondPackage, "", true /* default available */,
700                    false /* fallback */, null)};
701        checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
702
703        // Make both packages invalid so that we fail listing WebView packages
704        mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
705                    false /* valid */));
706        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
707                    false /* valid */));
708
709        // Change package to hit the webview packages listing problem.
710        if (settingsChange) {
711            mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
712        } else {
713            mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
714                    WebViewUpdateService.PACKAGE_ADDED_REPLACED);
715        }
716
717        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
718        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
719
720        // Make second package valid and verify that we can load it again
721        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
722                    true /* valid */));
723
724        mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
725                WebViewUpdateService.PACKAGE_ADDED_REPLACED);
726
727
728        checkPreparationPhasesForPackage(secondPackage, 1);
729    }
730
731    public void testDontKillIfPackageReplaced() {
732        checkDontKillIfPackageRemoved(true);
733    }
734
735    public void testDontKillIfPackageRemoved() {
736        checkDontKillIfPackageRemoved(false);
737    }
738
739    public void checkDontKillIfPackageRemoved(boolean replaced) {
740        String firstPackage = "first";
741        String secondPackage = "second";
742        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
743            new WebViewProviderInfo(firstPackage, "", true /* default available */,
744                    false /* fallback */, null),
745            new WebViewProviderInfo(secondPackage, "", true /* default available */,
746                    false /* fallback */, null)};
747        checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
748
749        // Replace or remove the current webview package
750        if (replaced) {
751            mTestSystemImpl.setPackageInfo(
752                    createPackageInfo(firstPackage, true /* enabled */, false /* valid */));
753            mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
754                    WebViewUpdateService.PACKAGE_ADDED_REPLACED);
755        } else {
756            mTestSystemImpl.removePackageInfo(firstPackage);
757            mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
758                    WebViewUpdateService.PACKAGE_REMOVED);
759        }
760
761        checkPreparationPhasesForPackage(secondPackage, 1);
762
763        Mockito.verify(mTestSystemImpl, Mockito.never()).killPackageDependents(
764                Mockito.anyObject());
765    }
766
767    public void testKillIfSettingChanged() {
768        String firstPackage = "first";
769        String secondPackage = "second";
770        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
771            new WebViewProviderInfo(firstPackage, "", true /* default available */,
772                    false /* fallback */, null),
773            new WebViewProviderInfo(secondPackage, "", true /* default available */,
774                    false /* fallback */, null)};
775        checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
776
777        mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
778
779        checkPreparationPhasesForPackage(secondPackage, 1);
780
781        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
782    }
783
784    /**
785     * Test that we kill apps using an old provider when we change the provider setting, even if the
786     * new provider is not the one we intended to change to.
787     */
788    public void testKillIfChangeProviderIncorrectly() {
789        String firstPackage = "first";
790        String secondPackage = "second";
791        String thirdPackage = "third";
792        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
793            new WebViewProviderInfo(firstPackage, "", true /* default available */,
794                    false /* fallback */, null),
795            new WebViewProviderInfo(secondPackage, "", true /* default available */,
796                    false /* fallback */, null),
797            new WebViewProviderInfo(thirdPackage, "", true /* default available */,
798                    false /* fallback */, null)};
799        setupWithPackages(packages);
800        setEnabledAndValidPackageInfos(packages);
801
802        // Start with the setting pointing to the third package
803        mTestSystemImpl.updateUserSetting(null, thirdPackage);
804
805        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
806        checkPreparationPhasesForPackage(thirdPackage, 1);
807
808        mTestSystemImpl.setPackageInfo(
809                createPackageInfo(secondPackage, true /* enabled */, false /* valid */));
810
811        // Try to switch to the invalid second package, this should result in switching to the first
812        // package, since that is more preferred than the third one.
813        assertEquals(firstPackage,
814                mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage));
815
816        checkPreparationPhasesForPackage(firstPackage, 1);
817
818        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(thirdPackage));
819    }
820}
821