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

Commit 1b804b14 authored by Becca Hughes's avatar Becca Hughes Committed by Android (Google) Code Review
Browse files

Merge "Fix restore credential manager providers" into main

parents 3d4748a8 006c6ea2
Loading
Loading
Loading
Loading
+68 −34
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -201,7 +202,7 @@ public final class CredentialManagerService
    @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same
    // this.mLock
    protected void handlePackageRemovedMultiModeLocked(String packageName, int userId) {
        updateProvidersWhenPackageRemoved(mContext, packageName);
        updateProvidersWhenPackageRemoved(new SettingsWrapper(mContext), packageName);

        List<CredentialManagerServiceImpl> services = peekServiceListForUserLocked(userId);
        if (services == null) {
@@ -1134,13 +1135,14 @@ public final class CredentialManagerService
    }

    /** Updates the list of providers when an app is uninstalled. */
    public static void updateProvidersWhenPackageRemoved(Context context, String packageName) {
    public static void updateProvidersWhenPackageRemoved(
            SettingsWrapper settingsWrapper, String packageName) {
        Slog.i(TAG, "updateProvidersWhenPackageRemoved");

        // Get the current providers.
        String rawProviders =
                Settings.Secure.getStringForUser(
                    context.getContentResolver(),
                    Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
                    UserHandle.myUserId());
                settingsWrapper.getStringForUser(
                        Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, UserHandle.myUserId());
        if (rawProviders == null) {
            Slog.w(TAG, "settings key is null");
            return;
@@ -1148,44 +1150,44 @@ public final class CredentialManagerService

        // Remove any providers from the primary setting that contain the package name
        // being removed.
        Set<String> primaryProviders =
                getStoredProviders(rawProviders, packageName);
        if (!Settings.Secure.putString(
                context.getContentResolver(),
        Set<String> primaryProviders = getStoredProviders(rawProviders, packageName);
        if (!settingsWrapper.putStringForUser(
                Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
                String.join(":", primaryProviders))) {
            Slog.w(TAG, "Failed to remove primary package: " + packageName);
                String.join(":", primaryProviders),
                UserHandle.myUserId(),
                /* overrideableByRestore= */ true)) {
            Slog.e(TAG, "Failed to remove primary package: " + packageName);
            return;
        }

        // Read the autofill provider so we don't accidentally erase it.
        String autofillProvider =
                Settings.Secure.getStringForUser(
                    context.getContentResolver(),
                    Settings.Secure.AUTOFILL_SERVICE,
                    UserHandle.myUserId());
                settingsWrapper.getStringForUser(
                        Settings.Secure.AUTOFILL_SERVICE, UserHandle.myUserId());

        // If there is an autofill provider and it is the placeholder indicating
        // that the currently selected primary provider does not support autofill
        // then we should wipe the setting to keep it in sync.
        if (autofillProvider != null && primaryProviders.isEmpty()) {
            if (autofillProvider.equals(AUTOFILL_PLACEHOLDER_VALUE)) {
                if (!Settings.Secure.putString(
                        context.getContentResolver(),
                if (!settingsWrapper.putStringForUser(
                        Settings.Secure.AUTOFILL_SERVICE,
                        "")) {
                    Slog.w(TAG, "Failed to remove autofill package: " + packageName);
                        "",
                        UserHandle.myUserId(),
                        /* overrideableByRestore= */ true)) {
                    Slog.e(TAG, "Failed to remove autofill package: " + packageName);
                }
            } else {
                // If the existing autofill provider is from the app being removed
                // then erase the autofill service setting.
                ComponentName cn = ComponentName.unflattenFromString(autofillProvider);
                if (cn != null && cn.getPackageName().equals(packageName)) {
                   if (!Settings.Secure.putString(
                            context.getContentResolver(),
                    if (!settingsWrapper.putStringForUser(
                            Settings.Secure.AUTOFILL_SERVICE,
                            "")) {
                        Slog.w(TAG, "Failed to remove autofill package: " + packageName);
                            "",
                            UserHandle.myUserId(),
                            /* overrideableByRestore= */ true)) {
                        Slog.e(TAG, "Failed to remove autofill package: " + packageName);
                    }
                }
            }
@@ -1193,19 +1195,17 @@ public final class CredentialManagerService

        // Read the credential providers to remove any reference of the removed app.
        String rawCredentialProviders =
                Settings.Secure.getStringForUser(
                    context.getContentResolver(),
                    Settings.Secure.CREDENTIAL_SERVICE,
                    UserHandle.myUserId());
                settingsWrapper.getStringForUser(
                        Settings.Secure.CREDENTIAL_SERVICE, UserHandle.myUserId());

        // Remove any providers that belong to the removed app.
        Set<String> credentialProviders =
                getStoredProviders(rawCredentialProviders, packageName);
        if (!Settings.Secure.putString(
                context.getContentResolver(),
        Set<String> credentialProviders = getStoredProviders(rawCredentialProviders, packageName);
        if (!settingsWrapper.putStringForUser(
                Settings.Secure.CREDENTIAL_SERVICE,
                String.join(":", credentialProviders))) {
            Slog.w(TAG, "Failed to remove secondary package: " + packageName);
                String.join(":", credentialProviders),
                UserHandle.myUserId(),
                /* overrideableByRestore= */ true)) {
            Slog.e(TAG, "Failed to remove secondary package: " + packageName);
        }
    }

@@ -1232,4 +1232,38 @@ public final class CredentialManagerService

        return providers;
    }

    /** A wrapper class that can be used by tests for intercepting reads/writes. */
    public static class SettingsWrapper {
        private final Context mContext;

        public SettingsWrapper(@NonNull Context context) {
            this.mContext = context;
        }

        ContentResolver getContentResolver() {
            return mContext.getContentResolver();
        }

        /** Retrieves the string value of a system setting */
        public String getStringForUser(String name, int userHandle) {
            return Settings.Secure.getStringForUser(getContentResolver(), name, userHandle);
        }

        /** Updates the string value of a system setting */
        public boolean putStringForUser(
                String name,
                String value,
                int userHandle,
                boolean overrideableByRestore) {
            return Settings.Secure.putStringForUser(
                    getContentResolver(),
                    name,
                    value,
                    null,
                    false,
                    userHandle,
                    overrideableByRestore);
        }
    }
}
+40 −9
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;

import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -40,10 +41,12 @@ import java.util.Set;
public final class CredentialManagerServiceTest {

    Context mContext = null;
    MockSettingsWrapper mSettingsWrapper = null;

    @Before
    public void setUp() throws CertificateException {
        mContext = ApplicationProvider.getApplicationContext();
        mSettingsWrapper = new MockSettingsWrapper(mContext);
    }

    @Test
@@ -81,7 +84,8 @@ public final class CredentialManagerServiceTest {
                Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
                "com.example.test/com.example.test.TestActivity");

        CredentialManagerService.updateProvidersWhenPackageRemoved(mContext, "com.example.test");
        CredentialManagerService.updateProvidersWhenPackageRemoved(
                mSettingsWrapper, "com.example.test");

        assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE)).isEqualTo("");
        assertThat(getSettingsKey(Settings.Secure.CREDENTIAL_SERVICE))
@@ -101,7 +105,8 @@ public final class CredentialManagerServiceTest {
        setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE, testCredentialValue);
        setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, testCredentialPrimaryValue);

        CredentialManagerService.updateProvidersWhenPackageRemoved(mContext, "com.example.test3");
        CredentialManagerService.updateProvidersWhenPackageRemoved(
                mSettingsWrapper, "com.example.test3");

        // Since the provider removed was not a primary provider then we should do nothing.
        assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE))
@@ -125,7 +130,8 @@ public final class CredentialManagerServiceTest {
                Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
                "com.example.test/com.example.test.TestActivity");

        CredentialManagerService.updateProvidersWhenPackageRemoved(mContext, "com.example.test");
        CredentialManagerService.updateProvidersWhenPackageRemoved(
                mSettingsWrapper, "com.example.test");

        assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE)).isEqualTo("");
        assertThat(getSettingsKey(Settings.Secure.CREDENTIAL_SERVICE))
@@ -144,7 +150,8 @@ public final class CredentialManagerServiceTest {
        setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE, testCredentialValue);
        setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, testCredentialPrimaryValue);

        CredentialManagerService.updateProvidersWhenPackageRemoved(mContext, "com.example.test3");
        CredentialManagerService.updateProvidersWhenPackageRemoved(
                mSettingsWrapper, "com.example.test3");

        // Since the provider removed was not a primary provider then we should do nothing.
        assertCredentialPropertyEquals(
@@ -176,12 +183,36 @@ public final class CredentialManagerServiceTest {
        assertThat(actualValueSet).isEqualTo(newValueSet);
    }

    private void setSettingsKey(String key, String value) {
        assertThat(Settings.Secure.putString(mContext.getContentResolver(), key, value)).isTrue();
    private void setSettingsKey(String name, String value) {
        assertThat(
                        mSettingsWrapper.putStringForUser(
                                name, value, UserHandle.myUserId(), true))
                .isTrue();
    }

    private String getSettingsKey(String key) {
        return Settings.Secure.getStringForUser(
                mContext.getContentResolver(), key, UserHandle.myUserId());
    private String getSettingsKey(String name) {
        return mSettingsWrapper.getStringForUser(name, UserHandle.myUserId());
    }

    private static final class MockSettingsWrapper
            extends CredentialManagerService.SettingsWrapper {

        MockSettingsWrapper(@NonNull Context context) {
            super(context);
        }

        /** Updates the string value of a system setting */
        @Override
        public boolean putStringForUser(
                String name,
                String value,
                int userHandle,
                boolean overrideableByRestore) {
            // This will ensure that when the settings putStringForUser method is called by
            // CredentialManagerService that the overrideableByRestore bit is true.
            assertThat(overrideableByRestore).isTrue();

            return Settings.Secure.putStringForUser(getContentResolver(), name, value, userHandle);
        }
    }
}