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 */, true /* installed */));
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, boolean installed) {
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 (installed) {
147            p.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
148        } else {
149            p.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
150        }
151        if (valid) {
152            // no flag means invalid
153            p.applicationInfo.metaData.putString(WEBVIEW_LIBRARY_FLAG, "blah");
154        }
155        return p;
156    }
157
158    private static PackageInfo createPackageInfo(String packageName, boolean enabled, boolean valid,
159            boolean installed, Signature[] signatures, long updateTime) {
160        PackageInfo p = createPackageInfo(packageName, enabled, valid, installed);
161        p.signatures = signatures;
162        p.lastUpdateTime = updateTime;
163        return p;
164    }
165
166    private static PackageInfo createPackageInfo(String packageName, boolean enabled, boolean valid,
167            boolean installed, Signature[] signatures, long updateTime, boolean hidden) {
168        PackageInfo p =
169            createPackageInfo(packageName, enabled, valid, installed, signatures, updateTime);
170        if (hidden) {
171            p.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
172        } else {
173            p.applicationInfo.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HIDDEN;
174        }
175        return p;
176    }
177
178    private void checkPreparationPhasesForPackage(String expectedPackage, int numPreparation) {
179        // Verify that onWebViewProviderChanged was called for the numPreparation'th time for the
180        // expected package
181        Mockito.verify(mTestSystemImpl, Mockito.times(numPreparation)).onWebViewProviderChanged(
182                Mockito.argThat(new IsPackageInfoWithName(expectedPackage)));
183
184        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
185
186        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
187        assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
188        assertEquals(expectedPackage, response.packageInfo.packageName);
189    }
190
191
192    // ****************
193    // Tests
194    // ****************
195
196
197    public void testWithSinglePackage() {
198        String testPackageName = "test.package.name";
199        checkCertainPackageUsedAfterWebViewBootPreparation(testPackageName,
200                new WebViewProviderInfo[] {
201                    new WebViewProviderInfo(testPackageName, "",
202                            true /*default available*/, false /* fallback */, null)});
203    }
204
205    public void testDefaultPackageUsedOverNonDefault() {
206        String defaultPackage = "defaultPackage";
207        String nonDefaultPackage = "nonDefaultPackage";
208        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
209            new WebViewProviderInfo(nonDefaultPackage, "", false, false, null),
210            new WebViewProviderInfo(defaultPackage, "", true, false, null)};
211        checkCertainPackageUsedAfterWebViewBootPreparation(defaultPackage, packages);
212    }
213
214    public void testSeveralRelros() {
215        String singlePackage = "singlePackage";
216        checkCertainPackageUsedAfterWebViewBootPreparation(
217                singlePackage,
218                new WebViewProviderInfo[] {
219                    new WebViewProviderInfo(singlePackage, "", true /*def av*/, false, null)},
220                2);
221    }
222
223    // Ensure that package with valid signatures is chosen rather than package with invalid
224    // signatures.
225    public void testWithSignatures() {
226        String validPackage = "valid package";
227        String invalidPackage = "invalid package";
228
229        Signature validSignature = new Signature("11");
230        Signature invalidExpectedSignature = new Signature("22");
231        Signature invalidPackageSignature = new Signature("33");
232
233        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
234            new WebViewProviderInfo(invalidPackage, "", true, false, new String[]{
235                        Base64.encodeToString(
236                                invalidExpectedSignature.toByteArray(), Base64.DEFAULT)}),
237            new WebViewProviderInfo(validPackage, "", true, false, new String[]{
238                        Base64.encodeToString(
239                                validSignature.toByteArray(), Base64.DEFAULT)})
240        };
241        setupWithPackages(packages, true /* fallback logic enabled */, 1 /* numRelros */,
242                false /* isDebuggable */);
243        mTestSystemImpl.setPackageInfo(createPackageInfo(invalidPackage, true /* enabled */,
244                    true /* valid */, true /* installed */, new Signature[]{invalidPackageSignature}
245                    , 0 /* updateTime */));
246        mTestSystemImpl.setPackageInfo(createPackageInfo(validPackage, true /* enabled */,
247                    true /* valid */, true /* installed */, new Signature[]{validSignature}
248                    , 0 /* updateTime */));
249
250        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
251
252
253        checkPreparationPhasesForPackage(validPackage, 1 /* first preparation for this package */);
254
255        WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages();
256        assertEquals(1, validPackages.length);
257        assertEquals(validPackage, validPackages[0].packageName);
258    }
259
260    public void testFailWaitingForRelro() {
261        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
262            new WebViewProviderInfo("packagename", "", true, true, null)};
263        setupWithPackages(packages);
264        setEnabledAndValidPackageInfos(packages);
265
266        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
267
268        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
269                Mockito.argThat(new IsPackageInfoWithName(packages[0].packageName)));
270
271        // Never call notifyRelroCreation()
272
273        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
274        assertEquals(WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO, response.status);
275    }
276
277    public void testFailListingEmptyWebviewPackages() {
278        WebViewProviderInfo[] packages = new WebViewProviderInfo[0];
279        setupWithPackages(packages);
280        setEnabledAndValidPackageInfos(packages);
281
282        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
283
284        Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
285                Matchers.anyObject());
286
287        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
288        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
289    }
290
291    public void testFailListingInvalidWebviewPackage() {
292        WebViewProviderInfo wpi = new WebViewProviderInfo("package", "", true, true, null);
293        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi};
294        setupWithPackages(packages);
295        mTestSystemImpl.setPackageInfo(
296                createPackageInfo(wpi.packageName, true /* enabled */, false /* valid */,
297                    true /* installed */));
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        // Verify that we can recover from failing to list webview packages.
308        mTestSystemImpl.setPackageInfo(
309                createPackageInfo(wpi.packageName, true /* enabled */, true /* valid */,
310                    true /* installed */));
311        mWebViewUpdateServiceImpl.packageStateChanged(wpi.packageName,
312                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
313
314        checkPreparationPhasesForPackage(wpi.packageName, 1);
315    }
316
317    // Test that switching provider using changeProviderAndSetting works.
318    public void testSwitchingProvider() {
319        String firstPackage = "first";
320        String secondPackage = "second";
321        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
322            new WebViewProviderInfo(firstPackage, "", true, false, null),
323            new WebViewProviderInfo(secondPackage, "", true, false, null)};
324        checkSwitchingProvider(packages, firstPackage, secondPackage);
325    }
326
327    public void testSwitchingProviderToNonDefault() {
328        String defaultPackage = "defaultPackage";
329        String nonDefaultPackage = "nonDefaultPackage";
330        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
331            new WebViewProviderInfo(defaultPackage, "", true, false, null),
332            new WebViewProviderInfo(nonDefaultPackage, "", false, false, null)};
333        checkSwitchingProvider(packages, defaultPackage, nonDefaultPackage);
334    }
335
336    private void checkSwitchingProvider(WebViewProviderInfo[] packages, String initialPackage,
337            String finalPackage) {
338        checkCertainPackageUsedAfterWebViewBootPreparation(initialPackage, packages);
339
340        mWebViewUpdateServiceImpl.changeProviderAndSetting(finalPackage);
341        checkPreparationPhasesForPackage(finalPackage, 1 /* first preparation for this package */);
342
343        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(initialPackage));
344    }
345
346    // Change provider during relro creation by using changeProviderAndSetting
347    public void testSwitchingProviderDuringRelroCreation() {
348        checkChangingProviderDuringRelroCreation(true);
349    }
350
351    // Change provider during relro creation by enabling a provider
352    public void testChangingProviderThroughEnablingDuringRelroCreation() {
353        checkChangingProviderDuringRelroCreation(false);
354    }
355
356    private void checkChangingProviderDuringRelroCreation(boolean settingsChange) {
357        String firstPackage = "first";
358        String secondPackage = "second";
359        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
360            new WebViewProviderInfo(firstPackage, "", true, false, null),
361            new WebViewProviderInfo(secondPackage, "", true, false, null)};
362        setupWithPackages(packages);
363        if (settingsChange) {
364            // Have all packages be enabled, so that we can change provider however we want to
365            setEnabledAndValidPackageInfos(packages);
366        } else {
367            // Have all packages be disabled so that we can change one to enabled later
368            for(WebViewProviderInfo wpi : packages) {
369                mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName,
370                            false /* enabled */, true /* valid */, true /* installed */));
371            }
372        }
373
374        CountDownLatch countdown = new CountDownLatch(1);
375
376        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
377
378        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
379                Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
380
381        assertEquals(firstPackage, mWebViewUpdateServiceImpl.getCurrentWebViewPackageName());
382
383        new Thread(new Runnable() {
384            @Override
385            public void run() {
386                WebViewProviderResponse threadResponse =
387                    mWebViewUpdateServiceImpl.waitForAndGetProvider();
388                assertEquals(WebViewFactory.LIBLOAD_SUCCESS, threadResponse.status);
389                assertEquals(secondPackage, threadResponse.packageInfo.packageName);
390                // Verify that we killed the first package
391                Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
392                countdown.countDown();
393            }
394        }).start();
395        try {
396            Thread.sleep(500); // Let the new thread run / be blocked
397        } catch (InterruptedException e) {
398        }
399
400        if (settingsChange) {
401            mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
402        } else {
403            // Switch provider by enabling the second one
404            mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
405                        true /* valid */, true /* installed */));
406            mWebViewUpdateServiceImpl.packageStateChanged(
407                    secondPackage, WebViewUpdateService.PACKAGE_CHANGED, 0);
408        }
409        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
410        // first package done, should start on second
411
412        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
413                Mockito.argThat(new IsPackageInfoWithName(secondPackage)));
414
415        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
416        // second package done, the other thread should now be unblocked
417        try {
418            countdown.await();
419        } catch (InterruptedException e) {
420        }
421    }
422
423    public void testRunFallbackLogicIfEnabled() {
424        checkFallbackLogicBeingRun(true);
425    }
426
427    public void testDontRunFallbackLogicIfDisabled() {
428        checkFallbackLogicBeingRun(false);
429    }
430
431    private void checkFallbackLogicBeingRun(boolean fallbackLogicEnabled) {
432        String primaryPackage = "primary";
433        String fallbackPackage = "fallback";
434        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
435            new WebViewProviderInfo(
436                    primaryPackage, "", true /* default available */, false /* fallback */, null),
437            new WebViewProviderInfo(
438                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
439        setupWithPackages(packages, fallbackLogicEnabled);
440        setEnabledAndValidPackageInfos(packages);
441
442        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
443        // Verify that we disable the fallback package if fallback logic enabled, and don't disable
444        // the fallback package if that logic is disabled
445        if (fallbackLogicEnabled) {
446            Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
447                    Matchers.anyObject(), Mockito.eq(fallbackPackage));
448        } else {
449            Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
450                    Matchers.anyObject(), Matchers.anyObject());
451        }
452        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
453                Mockito.argThat(new IsPackageInfoWithName(primaryPackage)));
454
455        // Enable fallback package
456        mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
457                        true /* valid */, true /* installed */));
458        mWebViewUpdateServiceImpl.packageStateChanged(
459                fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED, 0);
460
461        if (fallbackLogicEnabled) {
462            // Check that we have now disabled the fallback package twice
463            Mockito.verify(mTestSystemImpl, Mockito.times(2)).uninstallAndDisablePackageForAllUsers(
464                    Matchers.anyObject(), Mockito.eq(fallbackPackage));
465        } else {
466            // Check that we still haven't disabled any package
467            Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
468                    Matchers.anyObject(), Matchers.anyObject());
469        }
470    }
471
472    /**
473     * Scenario for installing primary package when fallback enabled.
474     * 1. Start with only fallback installed
475     * 2. Install non-fallback
476     * 3. Fallback should be disabled
477     */
478    public void testInstallingNonFallbackPackage() {
479        String primaryPackage = "primary";
480        String fallbackPackage = "fallback";
481        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
482            new WebViewProviderInfo(
483                    primaryPackage, "", true /* default available */, false /* fallback */, null),
484            new WebViewProviderInfo(
485                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
486        setupWithPackages(packages, true /* isFallbackLogicEnabled */);
487        mTestSystemImpl.setPackageInfo(
488                createPackageInfo(fallbackPackage, true /* enabled */ , true /* valid */,
489                    true /* installed */));
490
491        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
492        Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
493                Matchers.anyObject(), Matchers.anyObject());
494
495        checkPreparationPhasesForPackage(fallbackPackage,
496                1 /* first preparation for this package*/);
497
498        // Install primary package
499        mTestSystemImpl.setPackageInfo(
500                createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */,
501                    true /* installed */));
502        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
503                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
504
505        // Verify fallback disabled, primary package used as provider, and fallback package killed
506        Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
507                Matchers.anyObject(), Mockito.eq(fallbackPackage));
508        checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation for this package*/);
509        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(fallbackPackage));
510    }
511
512    public void testFallbackChangesEnabledState() {
513        String primaryPackage = "primary";
514        String fallbackPackage = "fallback";
515        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
516            new WebViewProviderInfo(
517                    primaryPackage, "", true /* default available */, false /* fallback */, null),
518            new WebViewProviderInfo(
519                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
520        setupWithPackages(packages, true /* fallbackLogicEnabled */);
521        setEnabledAndValidPackageInfos(packages);
522
523        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
524
525        // Verify fallback disabled at boot when primary package enabled
526        Mockito.verify(mTestSystemImpl).enablePackageForUser(
527                Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
528                Matchers.anyInt());
529
530        checkPreparationPhasesForPackage(primaryPackage, 1);
531
532        // Disable primary package and ensure fallback becomes enabled and used
533        mTestSystemImpl.setPackageInfo(
534                createPackageInfo(primaryPackage, false /* enabled */, true /* valid */,
535                    true /* installed */));
536        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
537                WebViewUpdateService.PACKAGE_CHANGED, 0);
538
539        Mockito.verify(mTestSystemImpl).enablePackageForUser(
540                Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */,
541                Matchers.anyInt());
542
543        checkPreparationPhasesForPackage(fallbackPackage, 1);
544
545
546        // Again enable primary package and verify primary is used and fallback becomes disabled
547        mTestSystemImpl.setPackageInfo(
548                createPackageInfo(primaryPackage, true /* enabled */, true /* valid */,
549                    true /* installed */));
550        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
551                WebViewUpdateService.PACKAGE_CHANGED, 0);
552
553        // Verify fallback is disabled a second time when primary package becomes enabled
554        Mockito.verify(mTestSystemImpl, Mockito.times(2)).enablePackageForUser(
555                Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
556                Matchers.anyInt());
557
558        checkPreparationPhasesForPackage(primaryPackage, 2);
559    }
560
561    public void testAddUserWhenFallbackLogicEnabled() {
562        checkAddingNewUser(true);
563    }
564
565    public void testAddUserWhenFallbackLogicDisabled() {
566        checkAddingNewUser(false);
567    }
568
569    public void checkAddingNewUser(boolean fallbackLogicEnabled) {
570        String primaryPackage = "primary";
571        String fallbackPackage = "fallback";
572        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
573            new WebViewProviderInfo(
574                    primaryPackage, "", true /* default available */, false /* fallback */, null),
575            new WebViewProviderInfo(
576                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
577        setupWithPackages(packages, fallbackLogicEnabled);
578        setEnabledAndValidPackageInfos(packages);
579        int newUser = 100;
580        mWebViewUpdateServiceImpl.handleNewUser(newUser);
581        if (fallbackLogicEnabled) {
582            // Verify fallback package becomes disabled for new user
583            Mockito.verify(mTestSystemImpl).enablePackageForUser(
584                    Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
585                    Mockito.eq(newUser));
586        } else {
587            // Verify that we don't disable fallback for new user
588            Mockito.verify(mTestSystemImpl, Mockito.never()).enablePackageForUser(
589                    Mockito.anyObject(), Matchers.anyBoolean() /* enable */,
590                    Matchers.anyInt() /* user */);
591        }
592    }
593
594    /**
595     * Timing dependent test where we verify that the list of valid webview packages becoming empty
596     * at a certain point doesn't crash us or break our state.
597     */
598    public void testNotifyRelroDoesntCrashIfNoPackages() {
599        String firstPackage = "first";
600        String secondPackage = "second";
601        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
602            new WebViewProviderInfo(firstPackage, "", true /* default available */,
603                    false /* fallback */, null),
604            new WebViewProviderInfo(secondPackage, "", true /* default available */,
605                    false /* fallback */, null)};
606        setupWithPackages(packages);
607        // Add (enabled and valid) package infos for each provider
608        setEnabledAndValidPackageInfos(packages);
609
610        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
611
612        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
613                Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
614
615        // Change provider during relro creation to enter a state where we are
616        // waiting for relro creation to complete just to re-run relro creation.
617        // (so that in next notifyRelroCreationCompleted() call we have to list webview packages)
618        mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
619
620        // Make packages invalid to cause exception to be thrown
621        mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
622                    false /* valid */, true /* installed */, null /* signatures */,
623                    0 /* updateTime */));
624        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
625                    false /* valid */, true /* installed */));
626
627        // This shouldn't throw an exception!
628        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
629
630        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
631        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
632
633        // Now make a package valid again and verify that we can switch back to that
634        mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
635                    true /* valid */, true /* installed */, null /* signatures */,
636                    1 /* updateTime */ ));
637
638        mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
639                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
640
641        // Ensure we use firstPackage
642        checkPreparationPhasesForPackage(firstPackage, 2 /* second preparation for this package */);
643    }
644
645    /**
646     * Verify that even if a user-chosen package is removed temporarily we start using it again when
647     * it is added back.
648     */
649    public void testTempRemovePackageDoesntSwitchProviderPermanently() {
650        String firstPackage = "first";
651        String secondPackage = "second";
652        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
653            new WebViewProviderInfo(firstPackage, "", true /* default available */,
654                    false /* fallback */, null),
655            new WebViewProviderInfo(secondPackage, "", true /* default available */,
656                    false /* fallback */, null)};
657        checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
658
659        // Explicitly use the second package
660        mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
661        checkPreparationPhasesForPackage(secondPackage, 1 /* first time for this package */);
662
663        // Remove second package (invalidate it) and verify that first package is used
664        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
665                    false /* valid */, true /* installed */));
666        mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
667                WebViewUpdateService.PACKAGE_ADDED, 0);
668        checkPreparationPhasesForPackage(firstPackage, 2 /* second time for this package */);
669
670        // Now make the second package valid again and verify that it is used again
671        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
672                    true /* valid */, true /* installed */));
673        mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
674                WebViewUpdateService.PACKAGE_ADDED, 0);
675        checkPreparationPhasesForPackage(secondPackage, 2 /* second time for this package */);
676    }
677
678    /**
679     * Ensure that we update the user-chosen setting across boots if the chosen package is no
680     * longer installed and valid.
681     */
682    public void testProviderSettingChangedDuringBootIfProviderNotAvailable() {
683        String chosenPackage = "chosenPackage";
684        String nonChosenPackage = "non-chosenPackage";
685        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
686            new WebViewProviderInfo(chosenPackage, "", true /* default available */,
687                    false /* fallback */, null),
688            new WebViewProviderInfo(nonChosenPackage, "", true /* default available */,
689                    false /* fallback */, null)};
690
691        setupWithPackages(packages);
692        // Only 'install' nonChosenPackage
693        mTestSystemImpl.setPackageInfo(
694                createPackageInfo(nonChosenPackage, true /* enabled */, true /* valid */, true /* installed */));
695
696        // Set user-chosen package
697        mTestSystemImpl.updateUserSetting(null, chosenPackage);
698
699        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
700
701        // Verify that we switch the setting to point to the current package
702        Mockito.verify(mTestSystemImpl).updateUserSetting(
703                Mockito.anyObject(), Mockito.eq(nonChosenPackage));
704        assertEquals(nonChosenPackage, mTestSystemImpl.getUserChosenWebViewProvider(null));
705
706        checkPreparationPhasesForPackage(nonChosenPackage, 1);
707    }
708
709    public void testRecoverFailedListingWebViewPackagesSettingsChange() {
710        checkRecoverAfterFailListingWebviewPackages(true);
711    }
712
713    public void testRecoverFailedListingWebViewPackagesAddedPackage() {
714        checkRecoverAfterFailListingWebviewPackages(false);
715    }
716
717    /**
718     * Test that we can recover correctly from failing to list WebView packages.
719     * settingsChange: whether to fail during changeProviderAndSetting or packageStateChanged
720     */
721    public void checkRecoverAfterFailListingWebviewPackages(boolean settingsChange) {
722        String firstPackage = "first";
723        String secondPackage = "second";
724        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
725            new WebViewProviderInfo(firstPackage, "", true /* default available */,
726                    false /* fallback */, null),
727            new WebViewProviderInfo(secondPackage, "", true /* default available */,
728                    false /* fallback */, null)};
729        checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
730
731        // Make both packages invalid so that we fail listing WebView packages
732        mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
733                    false /* valid */, true /* installed */));
734        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
735                    false /* valid */, true /* installed */));
736
737        // Change package to hit the webview packages listing problem.
738        if (settingsChange) {
739            mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
740        } else {
741            mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
742                    WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
743        }
744
745        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
746        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
747
748        // Make second package valid and verify that we can load it again
749        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
750                    true /* valid */, true /* installed */));
751
752        mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
753                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
754
755
756        checkPreparationPhasesForPackage(secondPackage, 1);
757    }
758
759    public void testDontKillIfPackageReplaced() {
760        checkDontKillIfPackageRemoved(true);
761    }
762
763    public void testDontKillIfPackageRemoved() {
764        checkDontKillIfPackageRemoved(false);
765    }
766
767    public void checkDontKillIfPackageRemoved(boolean replaced) {
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        // Replace or remove the current webview package
778        if (replaced) {
779            mTestSystemImpl.setPackageInfo(
780                    createPackageInfo(firstPackage, true /* enabled */, false /* valid */,
781                        true /* installed */));
782            mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
783                    WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
784        } else {
785            mTestSystemImpl.removePackageInfo(firstPackage);
786            mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
787                    WebViewUpdateService.PACKAGE_REMOVED, 0);
788        }
789
790        checkPreparationPhasesForPackage(secondPackage, 1);
791
792        Mockito.verify(mTestSystemImpl, Mockito.never()).killPackageDependents(
793                Mockito.anyObject());
794    }
795
796    public void testKillIfSettingChanged() {
797        String firstPackage = "first";
798        String secondPackage = "second";
799        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
800            new WebViewProviderInfo(firstPackage, "", true /* default available */,
801                    false /* fallback */, null),
802            new WebViewProviderInfo(secondPackage, "", true /* default available */,
803                    false /* fallback */, null)};
804        checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
805
806        mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
807
808        checkPreparationPhasesForPackage(secondPackage, 1);
809
810        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
811    }
812
813    /**
814     * Test that we kill apps using an old provider when we change the provider setting, even if the
815     * new provider is not the one we intended to change to.
816     */
817    public void testKillIfChangeProviderIncorrectly() {
818        String firstPackage = "first";
819        String secondPackage = "second";
820        String thirdPackage = "third";
821        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
822            new WebViewProviderInfo(firstPackage, "", true /* default available */,
823                    false /* fallback */, null),
824            new WebViewProviderInfo(secondPackage, "", true /* default available */,
825                    false /* fallback */, null),
826            new WebViewProviderInfo(thirdPackage, "", true /* default available */,
827                    false /* fallback */, null)};
828        setupWithPackages(packages);
829        setEnabledAndValidPackageInfos(packages);
830
831        // Start with the setting pointing to the third package
832        mTestSystemImpl.updateUserSetting(null, thirdPackage);
833
834        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
835        checkPreparationPhasesForPackage(thirdPackage, 1);
836
837        mTestSystemImpl.setPackageInfo(
838                createPackageInfo(secondPackage, true /* enabled */, false /* valid */, true /* installed */));
839
840        // Try to switch to the invalid second package, this should result in switching to the first
841        // package, since that is more preferred than the third one.
842        assertEquals(firstPackage,
843                mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage));
844
845        checkPreparationPhasesForPackage(firstPackage, 1);
846
847        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(thirdPackage));
848    }
849
850    // Ensure that the update service uses an uninstalled package if that is the only package
851    // available.
852    public void testWithSingleUninstalledPackage() {
853        String testPackageName = "test.package.name";
854        WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
855                new WebViewProviderInfo(testPackageName, "",
856                        true /*default available*/, false /* fallback */, null)};
857        setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */);
858        mTestSystemImpl.setPackageInfo(createPackageInfo(testPackageName, true /* enabled */,
859                    true /* valid */, false /* installed */));
860
861        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
862
863        checkPreparationPhasesForPackage(testPackageName, 1 /* first preparation phase */);
864    }
865
866    public void testNonhiddenPackageUserOverHidden() {
867        checkVisiblePackageUserOverNonVisible(false /* true == uninstalled, false == hidden */);
868    }
869
870    public void testInstalledPackageUsedOverUninstalled() {
871        checkVisiblePackageUserOverNonVisible(true /* true == uninstalled, false == hidden */);
872    }
873
874    private void checkVisiblePackageUserOverNonVisible(boolean uninstalledNotHidden) {
875        boolean testUninstalled = uninstalledNotHidden;
876        boolean testHidden = !uninstalledNotHidden;
877        String installedPackage = "installedPackage";
878        String uninstalledPackage = "uninstalledPackage";
879        WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
880            new WebViewProviderInfo(uninstalledPackage, "", true /* available by default */,
881                    false /* fallback */, null),
882            new WebViewProviderInfo(installedPackage, "", true /* available by default */,
883                    false /* fallback */, null)};
884
885        setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */);
886        mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */,
887                    true /* valid */, true /* installed */));
888        mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
889                    true /* valid */, (testUninstalled ? false : true) /* installed */,
890                    null /* signatures */, 0 /* updateTime */, (testHidden ? true : false)));
891
892        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
893
894        checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
895    }
896
897    public void testCantSwitchToHiddenPackage () {
898        checkCantSwitchToNonVisiblePackage(false /* true == uninstalled, false == hidden */);
899    }
900
901
902    public void testCantSwitchToUninstalledPackage () {
903        checkCantSwitchToNonVisiblePackage(true /* true == uninstalled, false == hidden */);
904    }
905
906    /**
907     * Ensure that we won't prioritize an uninstalled (or hidden) package even if it is user-chosen,
908     * and that an uninstalled (or hidden) package is not considered valid (in the
909     * getValidWebViewPackages() API).
910     */
911    private void checkCantSwitchToNonVisiblePackage(boolean uninstalledNotHidden) {
912        boolean testUninstalled = uninstalledNotHidden;
913        boolean testHidden = !uninstalledNotHidden;
914        String installedPackage = "installedPackage";
915        String uninstalledPackage = "uninstalledPackage";
916        WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
917            new WebViewProviderInfo(uninstalledPackage, "", true /* available by default */,
918                    false /* fallback */, null),
919            new WebViewProviderInfo(installedPackage, "", true /* available by default */,
920                    false /* fallback */, null)};
921
922        setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */);
923        mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */,
924                    true /* valid */, true /* installed */));
925        mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
926                    true /* valid */, (testUninstalled ? false : true) /* installed */,
927                    null /* signatures */, 0 /* updateTime */,
928                    (testHidden ? true : false) /* hidden */));
929
930        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
931
932        checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
933
934        // Ensure that only the installed package is considered valid
935        WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages();
936        assertEquals(1, validPackages.length);
937        assertEquals(installedPackage, validPackages[0].packageName);
938
939        // ensure that we don't switch to the uninstalled package (it will be used if it becomes
940        // installed later)
941        assertEquals(installedPackage,
942                mWebViewUpdateServiceImpl.changeProviderAndSetting(uninstalledPackage));
943
944        // We should only have called onWebViewProviderChanged once (before calling
945        // changeProviderAndSetting
946        Mockito.verify(mTestSystemImpl, Mockito.times(1)).onWebViewProviderChanged(
947                Mockito.argThat(new IsPackageInfoWithName(installedPackage)));
948    }
949
950    public void testHiddenPackageNotPrioritizedEvenIfChosen() {
951        checkNonvisiblePackageNotPrioritizedEvenIfChosen(
952                false /* true == uninstalled, false == hidden */);
953    }
954
955    public void testUninstalledPackageNotPrioritizedEvenIfChosen() {
956        checkNonvisiblePackageNotPrioritizedEvenIfChosen(
957                true /* true == uninstalled, false == hidden */);
958    }
959
960    public void checkNonvisiblePackageNotPrioritizedEvenIfChosen(boolean uninstalledNotHidden) {
961        boolean testUninstalled = uninstalledNotHidden;
962        boolean testHidden = !uninstalledNotHidden;
963        String installedPackage = "installedPackage";
964        String uninstalledPackage = "uninstalledPackage";
965        WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
966            new WebViewProviderInfo(uninstalledPackage, "", true /* available by default */,
967                    false /* fallback */, null),
968            new WebViewProviderInfo(installedPackage, "", true /* available by default */,
969                    false /* fallback */, null)};
970
971        setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */);
972        mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */,
973                    true /* valid */, true /* installed */));
974        mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
975                    true /* valid */, (testUninstalled ? false : true) /* installed */,
976                    null /* signatures */, 0 /* updateTime */,
977                    (testHidden ? true : false) /* hidden */));
978
979        // Start with the setting pointing to the uninstalled package
980        mTestSystemImpl.updateUserSetting(null, uninstalledPackage);
981
982        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
983
984        checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
985    }
986
987    /**
988     * Ensures that fallback becomes enabled if the primary package is uninstalled for the current
989     * user.
990     */
991    public void testFallbackEnabledIfPrimaryUninstalled() {
992        String primaryPackage = "primary";
993        String fallbackPackage = "fallback";
994        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
995            new WebViewProviderInfo(
996                    primaryPackage, "", true /* default available */, false /* fallback */, null),
997            new WebViewProviderInfo(
998                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
999        setupWithPackages(packages, true /* fallback logic enabled */);
1000        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
1001                    true /* valid */, false /* installed */));
1002        mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
1003                    true /* valid */, true /* installed */));
1004
1005        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
1006        // Verify that we enable the fallback package
1007        Mockito.verify(mTestSystemImpl).enablePackageForAllUsers(
1008                Mockito.anyObject(), Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */);
1009
1010        checkPreparationPhasesForPackage(fallbackPackage, 1 /* first preparation phase */);
1011    }
1012
1013    public void testPreparationRunsIffNewPackage() {
1014        String primaryPackage = "primary";
1015        String fallbackPackage = "fallback";
1016        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
1017            new WebViewProviderInfo(
1018                    primaryPackage, "", true /* default available */, false /* fallback */, null),
1019            new WebViewProviderInfo(
1020                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
1021        setupWithPackages(packages, true /* fallback logic enabled */);
1022        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
1023                    true /* valid */, true /* installed */, null /* signatures */,
1024                    10 /* lastUpdateTime*/ ));
1025        mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
1026                    true /* valid */, true /* installed */));
1027
1028        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
1029
1030        checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation phase */);
1031        Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForUser(
1032                Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
1033                Matchers.anyInt() /* user */);
1034
1035
1036        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
1037                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0 /* userId */);
1038        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
1039                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 1 /* userId */);
1040        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
1041                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 2 /* userId */);
1042        // package still has the same update-time so we shouldn't run preparation here
1043        Mockito.verify(mTestSystemImpl, Mockito.times(1)).onWebViewProviderChanged(
1044                Mockito.argThat(new IsPackageInfoWithName(primaryPackage)));
1045        Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForUser(
1046                Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
1047                Matchers.anyInt() /* user */);
1048
1049        // Ensure we can still load the package
1050        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
1051        assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
1052        assertEquals(primaryPackage, response.packageInfo.packageName);
1053
1054
1055        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
1056                    true /* valid */, true /* installed */, null /* signatures */,
1057                    20 /* lastUpdateTime*/ ));
1058        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
1059                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
1060        // The package has now changed - ensure that we have run the preparation phase a second time
1061        checkPreparationPhasesForPackage(primaryPackage, 2 /* second preparation phase */);
1062
1063
1064        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
1065                    true /* valid */, true /* installed */, null /* signatures */,
1066                    50 /* lastUpdateTime*/ ));
1067        // Receive intent for different user
1068        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
1069                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 2);
1070
1071        checkPreparationPhasesForPackage(primaryPackage, 3 /* third preparation phase */);
1072    }
1073
1074}
1075