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