Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 22f910d5 authored by Ziad Youssef's avatar Ziad Youssef
Browse files

Attempt to repair the WebView pacakge in more cases

Tries to repair webview if a package or a user change broke our state.

Tested with a physical device uninstalling WebView using adb.

Test:  atest com.android.server.webkit.WebViewUpdateServiceTest

Bug: 308907090

Change-Id: Ic74ae1e990d6202ae1911e4d2bd62eb66505acac
parent 3d736682
Loading
Loading
Loading
Loading
+41 −12
Original line number Diff line number Diff line
@@ -96,6 +96,9 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
    private boolean mWebViewPackageDirty = false;
    private boolean mAnyWebViewInstalled = false;

    // Keeps track of whether we attempted to repair WebView before.
    private boolean mAttemptedToRepairBefore = false;

    private static final int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;

    // The WebView package currently in use (or the one we are preparing).
@@ -136,6 +139,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
                boolean removedOrChangedOldPackage = false;
                String oldProviderName = null;
                PackageInfo newPackage = null;
                boolean repairNeeded = false;
                synchronized (mLock) {
                    try {
                        newPackage = findPreferredWebViewPackage();
@@ -161,6 +165,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
                        Slog.e(TAG, "Could not find valid WebView package to create relro with "
                                + e);
                    }
                    repairNeeded = shouldTriggerRepairLocked();
                }
                if (updateWebView && !removedOrChangedOldPackage
                        && oldProviderName != null) {
@@ -170,12 +175,18 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
                    // only kills dependents of packages that are being removed.
                    mSystemInterface.killPackageDependents(oldProviderName);
                }
                if (repairNeeded) {
                    attemptRepair();
                }
                return;
            }
        }
    }

    private boolean shouldTriggerRepairLocked() {
        if (mAttemptedToRepairBefore) {
            return false;
        }
        if (mCurrentWebViewPackage == null) {
            return true;
        }
@@ -189,6 +200,26 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
        }
    }

    private void attemptRepair() {
        // We didn't find a valid WebView implementation. Try explicitly re-installing and
        // re-enabling the default package for all users in case it was disabled. If this actually
        // changes the state, we will see the PackageManager broadcast shortly and try again.
        synchronized (mLock) {
            if (mAttemptedToRepairBefore) {
                return;
            }
            mAttemptedToRepairBefore = true;
        }
        Slog.w(
                TAG,
                "No provider available for all users, trying to install and enable "
                        + mDefaultProvider.packageName);
        mSystemInterface.installExistingPackageForAllUsers(
                mContext, mDefaultProvider.packageName);
        mSystemInterface.enablePackageForAllUsers(
                mContext, mDefaultProvider.packageName, true);
    }

    @Override
    public void prepareWebViewInSystemServer() {
        try {
@@ -211,18 +242,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
            }

            if (repairNeeded) {
                // We didn't find a valid WebView implementation. Try explicitly re-installing and
                // re-enabling the default package for all users in case it was disabled, even if we
                // already did the one-time migration before. If this actually changes the state, we
                // will see the PackageManager broadcast shortly and try again.
                Slog.w(
                        TAG,
                        "No provider available for all users, trying to install and enable "
                                + mDefaultProvider.packageName);
                mSystemInterface.installExistingPackageForAllUsers(
                        mContext, mDefaultProvider.packageName);
                mSystemInterface.enablePackageForAllUsers(
                        mContext, mDefaultProvider.packageName, true);
                attemptRepair();
            }

        } catch (Throwable t) {
@@ -332,6 +352,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
        PackageInfo oldPackage = null;
        PackageInfo newPackage = null;
        boolean providerChanged = false;
        boolean repairNeeded = false;
        synchronized (mLock) {
            oldPackage = mCurrentWebViewPackage;

@@ -354,11 +375,19 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
            if (providerChanged) {
                onWebViewProviderChanged(newPackage);
            }
            // Choosing another provider shouldn't break our state. Only check if repair
            // is needed if this function is called as a result of a user change.
            if (newProviderName == null) {
                repairNeeded = shouldTriggerRepairLocked();
            }
        }
        // Kill apps using the old provider only if we changed provider
        if (providerChanged && oldPackage != null) {
            mSystemInterface.killPackageDependents(oldPackage.packageName);
        }
        if (repairNeeded) {
            attemptRepair();
        }
        // Return the new provider, this is not necessarily the one we were asked to switch to,
        // but the persistent setting will now be pointing to the provider we were asked to
        // switch to anyway.
+6 −0
Original line number Diff line number Diff line
@@ -96,6 +96,9 @@ public class TestSystemImpl implements SystemInterface {
            return;
        }
        PackageInfo packageInfo = userPackages.get(userId);
        if (packageInfo == null) {
            return;
        }
        packageInfo.applicationInfo.enabled = enable;
        setPackageInfoForUser(userId, packageInfo);
    }
@@ -106,6 +109,9 @@ public class TestSystemImpl implements SystemInterface {
            return;
        }
        PackageInfo packageInfo = userPackages.get(userId);
        if (packageInfo == null) {
            return;
        }
        packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
        packageInfo.applicationInfo.privateFlags &= (~ApplicationInfo.PRIVATE_FLAG_HIDDEN);
        setPackageInfoForUser(userId, packageInfo);
+63 −1
Original line number Diff line number Diff line
@@ -1551,7 +1551,7 @@ public class WebViewUpdateServiceTest {

    @Test
    @RequiresFlagsEnabled("android.webkit.update_service_v2")
    public void testDefaultWebViewPackageInstalling() {
    public void testDefaultWebViewPackageInstallingDuringStartUp() {
        String testPackage = "testDefault";
        WebViewProviderInfo[] packages =
                new WebViewProviderInfo[] {
@@ -1574,6 +1574,68 @@ public class WebViewUpdateServiceTest {
                        Matchers.anyObject(), Mockito.eq(testPackage));
    }

    @Test
    @RequiresFlagsEnabled("android.webkit.update_service_v2")
    public void testDefaultWebViewPackageInstallingAfterStartUp() {
        String testPackage = "testDefault";
        WebViewProviderInfo[] packages =
                new WebViewProviderInfo[] {
                    new WebViewProviderInfo(
                            testPackage,
                            "",
                            true /* default available */,
                            false /* fallback */,
                            null)
                };
        checkCertainPackageUsedAfterWebViewBootPreparation(testPackage, packages);

        // uninstall the default package.
        mTestSystemImpl.setPackageInfo(
                createPackageInfo(
                        testPackage, true /* enabled */, true /* valid */, false /* installed */));
        mWebViewUpdateServiceImpl.packageStateChanged(testPackage,
                WebViewUpdateService.PACKAGE_REMOVED, 0);

        // Check that we try to re-install the default package.
        Mockito.verify(mTestSystemImpl)
                .installExistingPackageForAllUsers(
                        Matchers.anyObject(), Mockito.eq(testPackage));
    }

    /**
     * Ensures that adding a new user for which the current WebView package is uninstalled triggers
     * the repair logic.
     */
    @Test
    @RequiresFlagsEnabled("android.webkit.update_service_v2")
    public void testAddingNewUserWithDefaultdPackageNotInstalled() {
        String testPackage = "testDefault";
        WebViewProviderInfo[] packages =
                new WebViewProviderInfo[] {
                    new WebViewProviderInfo(
                            testPackage,
                            "",
                            true /* default available */,
                            false /* fallback */,
                            null)
                };
        checkCertainPackageUsedAfterWebViewBootPreparation(testPackage, packages);

        // Add new user with the default package not installed.
        int newUser = 100;
        mTestSystemImpl.addUser(newUser);
        mTestSystemImpl.setPackageInfoForUser(newUser,
                createPackageInfo(testPackage, true /* enabled */, true /* valid */,
                        false /* installed */));

        mWebViewUpdateServiceImpl.handleNewUser(newUser);

        // Check that we try to re-install the default package for all users.
        Mockito.verify(mTestSystemImpl)
                .installExistingPackageForAllUsers(
                        Matchers.anyObject(), Mockito.eq(testPackage));
    }

    private void testDefaultPackageChosen(PackageInfo packageInfo) {
        WebViewProviderInfo[] packages =
                new WebViewProviderInfo[] {