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

Commit 5b031543 authored by Reema Bajwa's avatar Reema Bajwa Committed by Android (Google) Code Review
Browse files

Merge "Remove service when package is modified and service is no longer valid" into main

parents 83513c8a a89d00f8
Loading
Loading
Loading
Loading
+49 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.credentials.flags.Flags;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
@@ -1173,8 +1174,19 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
                    final String[] serviceNames = mServiceNameResolver.getDefaultServiceNameList(
                            userId);
                    if (serviceNames != null) {
                        if (Flags.packageUpdateFixEnabled()) {
                            if (mServiceNameResolver.isConfiguredInMultipleMode()) {
                                // Remove any service that is in the cache but is no longer valid
                                // after this modification for this particular package
                                removeInvalidCachedServicesLocked(serviceNames, packageName,
                                        userId);
                            }
                        }

                        // Update services that are still valid
                        for (int i = 0; i < serviceNames.length; i++) {
                            peekAndUpdateCachedServiceLocked(packageName, userId, serviceNames[i]);
                            peekAndUpdateCachedServiceLocked(packageName, userId,
                                    serviceNames[i]);
                        }
                    }
                }
@@ -1230,6 +1242,36 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
        monitor.register(getContext(), null, UserHandle.ALL, true);
    }

    @GuardedBy("mLock")
    @SuppressWarnings("GuardedBy") // ErrorProne requires this.mLock for
    // handleServiceRemovedMultiModeLocked which is the same
    private void removeInvalidCachedServicesLocked(String[] validServices,
            String packageName, int userId) {
        visitServicesLocked((s) -> {
            ComponentName serviceComponentName = s
                    .getServiceComponentName();
            if (serviceComponentName != null && serviceComponentName
                    .getPackageName().equals(packageName)) {
                if (!serviceInValidServiceList(serviceComponentName,
                        validServices)) {
                    handleServiceRemovedMultiModeLocked(
                            serviceComponentName, userId);
                }
            }
        });
    }

    private boolean serviceInValidServiceList(ComponentName serviceComponentName,
            String[] serviceNames) {
        for (String service: serviceNames) {
            ComponentName componentName = ComponentName.unflattenFromString(service);
            if (componentName != null && componentName.equals(serviceComponentName)) {
                return true;
            }
        }
        return false;
    }

    @GuardedBy("mLock")
    @SuppressWarnings("unused")
    protected void onServicePackageDataClearedMultiModeLocked(String packageName, int userId) {
@@ -1245,6 +1287,12 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
        if (verbose) Slog.v(mTag, "handlePackageRemovedMultiModeLocked(" + userId + ")");
    }

    @GuardedBy("mLock")
    protected void handleServiceRemovedMultiModeLocked(ComponentName serviceComponentName,
            int userId) {
        if (verbose) Slog.v(mTag, "handleServiceRemovedMultiModeLocked(" + userId + ")");
    }

    @GuardedBy("mLock")
    protected void removeServiceFromCache(@NonNull S service, int userId) {
        if (mServicesCacheList.get(userId) != null) {
+1 −1
Original line number Diff line number Diff line
@@ -323,7 +323,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
     * if the service is disabled.
     */
    @Nullable
    public final ComponentName getServiceComponentName() {
    public ComponentName getServiceComponentName() {
        synchronized (mLock) {
            return mServiceInfo == null ? null : mServiceInfo.getComponentName();
        }
+212 −37
Original line number Diff line number Diff line
@@ -204,7 +204,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(new SettingsWrapper(mContext), packageName);
        updateProvidersWhenPackageRemoved(new SettingsWrapper(mContext), packageName, userId);

        List<CredentialManagerServiceImpl> services = peekServiceListForUserLocked(userId);
        if (services == null) {
@@ -233,6 +233,47 @@ public final class CredentialManagerService
        }
    }

    @GuardedBy("mLock")
    @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same
    // this.mLock
    protected void handleServiceRemovedMultiModeLocked(ComponentName componentName, int userId) {
        updateProvidersWhenServiceRemoved(new SettingsWrapper(mContext), componentName, userId);

        List<CredentialManagerServiceImpl> services = peekServiceListForUserLocked(userId);
        if (services == null) {
            return;
        }

        List<CredentialManagerServiceImpl> servicesToBeRemoved = new ArrayList<>();
        for (CredentialManagerServiceImpl service : services) {
            if (service != null) {
                CredentialProviderInfo credentialProviderInfo = service.getCredentialProviderInfo();
                ComponentName serviceComponentName =
                        credentialProviderInfo.getServiceInfo().getComponentName();
                if (serviceComponentName != null && serviceComponentName.equals(componentName)) {
                    servicesToBeRemoved.add(service);
                }
            }
        }

        removeServicesLocked(servicesToBeRemoved, userId);
    }

    @GuardedBy("mLock")
    @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same
    // this.mLock
    private void removeServicesLocked(
            List<CredentialManagerServiceImpl> servicesToBeRemoved, int userId) {
        // Iterate over all the services to be removed, and remove them from the user configurable
        // services cache, the system services cache as well as the setting key-value pair.
        for (CredentialManagerServiceImpl serviceToBeRemoved : servicesToBeRemoved) {
            removeServiceFromCache(serviceToBeRemoved, userId);
            removeServiceFromSystemServicesCache(serviceToBeRemoved, userId);
            CredentialDescriptionRegistry.forUser(userId)
                    .evictProviderWithPackageName(serviceToBeRemoved.getServicePackageName());
        }
    }

    @GuardedBy("mLock")
    private void removeServiceFromSystemServicesCache(
            CredentialManagerServiceImpl serviceToBeRemoved, int userId) {
@@ -1136,27 +1177,123 @@ public final class CredentialManagerService
        }
    }

    /** Updates the list of providers when a particular service within an app is to be removed. */
    public static void updateProvidersWhenServiceRemoved(
            SettingsWrapper settingsWrapper, ComponentName componentName, int userId) {
        Slog.i(TAG, "updateProvidersWhenServiceRemoved for: "
                + componentName.flattenToString());

        // Get the current primary providers.
        String rawPrimaryProviders =
                settingsWrapper.getStringForUser(
                        Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, userId);
        if (TextUtils.isEmpty(rawPrimaryProviders)) {
            Slog.w(TAG, "primary settings key is null");
        } else {
            // Remove any service from the primary setting that matches with the service
            // being removed, and set the filtered list back to the settings key.
            Set<String> filteredPrimaryProviders = getStoredProvidersExceptService(
                    rawPrimaryProviders, componentName);

            // If there are no more primary providers AND there is no autofill provider either,
            // that means all providers must be cleared as that is what the Settings UI app
            // displays to the user.If the autofill provider is present then UI shows that as the
            // preferred service and other credential provider services can continue to work.
            if (filteredPrimaryProviders.isEmpty()
                    && !isAutofillProviderPresent(settingsWrapper, userId)) {
                Slog.d(TAG, "Clearing all credential providers");
                if (clearAllCredentialProviders(settingsWrapper, userId)) {
                    return;
                }
                Slog.e(TAG, "Failed to clear all credential providers");
            }

            // Set the filtered primary providers to the settings key
            if (!settingsWrapper.putStringForUser(
                    Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
                    String.join(":", filteredPrimaryProviders),
                    UserHandle.myUserId(),
                    /* overrideableByRestore= */ true)) {
                Slog.e(TAG, "Failed to remove primary service: " + componentName);
                return;
            }
        }

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

        // Remove any provider services that are same as the one being removed.
        Set<String> filteredCredentialProviders = getStoredProvidersExceptService(
                rawCredentialProviders, componentName);
        if (!settingsWrapper.putStringForUser(
                Settings.Secure.CREDENTIAL_SERVICE,
                String.join(":", filteredCredentialProviders),
                UserHandle.myUserId(),
                /* overrideableByRestore= */ true)) {
            Slog.e(TAG, "Failed to remove secondary service: " + componentName);
        }
    }

    private static boolean isAutofillProviderPresent(SettingsWrapper settingsWrapper, int userId) {
        // Read the autofill provider so we don't accidentally erase it.
        String autofillProvider =
                settingsWrapper.getStringForUser(
                        Settings.Secure.AUTOFILL_SERVICE, userId);

        return autofillProvider != null && !autofillProvider.isEmpty()
                && !isPlaceholderAutofillProvider(autofillProvider, settingsWrapper);
    }

    private static boolean isPlaceholderAutofillProvider(String autofillProvider,
            SettingsWrapper settingsWrapper) {

        // If there is an autofill provider and it is the credential autofill service indicating
        // that the currently selected primary provider does not support autofill
        // then we should keep as is
        String credentialAutofillService = settingsWrapper.mContext.getResources().getString(
                R.string.config_defaultCredentialManagerAutofillService);
        return autofillProvider != null && TextUtils.equals(
                autofillProvider, credentialAutofillService);
    }

    private static boolean clearAllCredentialProviders(SettingsWrapper settingsWrapper,
            int userId) {
        if (!settingsWrapper.putStringForUser(
                Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
                null,
                userId,
                /* overrideableByRestore= */ true)) {
            return false;
        }
        return settingsWrapper.putStringForUser(
                Settings.Secure.CREDENTIAL_SERVICE,
                null,
                userId,
                /* overrideableByRestore= */ true);
    }

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

        // Get the current providers.
        String rawProviders =
                settingsWrapper.getStringForUser(
                        Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, UserHandle.myUserId());
                        Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, userId);
        if (rawProviders == null) {
            Slog.w(TAG, "settings key is null");
            return;
        }

        } else {
            // Remove any providers from the primary setting that contain the package name
            // being removed.
        Set<String> primaryProviders = getStoredProviders(rawProviders, packageName);
            Set<String> primaryProviders = getStoredProvidersExceptPackage(rawProviders,
                    packageName);
            if (!settingsWrapper.putStringForUser(
                    Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
                    String.join(":", primaryProviders),
                UserHandle.myUserId(),
                    userId,
                    /* overrideableByRestore= */ true)) {
                Slog.e(TAG, "Failed to remove primary package: " + packageName);
                return;
@@ -1165,7 +1302,7 @@ public final class CredentialManagerService
            // Read the autofill provider so we don't accidentally erase it.
            String autofillProvider =
                    settingsWrapper.getStringForUser(
                        Settings.Secure.AUTOFILL_SERVICE, UserHandle.myUserId());
                            Settings.Secure.AUTOFILL_SERVICE, userId);

            // If there is an autofill provider and it is the credential autofill service indicating
            // that the currently selected primary provider does not support autofill
@@ -1181,31 +1318,70 @@ public final class CredentialManagerService
                    if (!settingsWrapper.putStringForUser(
                            Settings.Secure.AUTOFILL_SERVICE,
                            "",
                        UserHandle.myUserId(),
                            userId,
                            /* overrideableByRestore= */ true)) {
                        Slog.e(TAG, "Failed to remove autofill package: " + packageName);
                    }
                }
            }

            // If there are no more primary providers AND there is no autofill provider either,
            // that means all providers must be cleared as that is what the Settings UI app
            // displays to the user.If the autofill provider is present then UI shows that as the
            // preferred service and other credential provider services can continue to work.
            if (primaryProviders.isEmpty() && !isAutofillProviderPresent(settingsWrapper, userId)) {
                Slog.d(TAG, "Clearing all credential providers");
                if (clearAllCredentialProviders(settingsWrapper, userId)) {
                    return;
                }
                Slog.e(TAG, "Failed to clear all credential providers");
            }
        }

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

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

    /** Gets the list of stored providers from a string removing any mention of package name. */
    public static Set<String> getStoredProviders(String rawProviders, String packageName) {
    public static Set<String> getStoredProvidersExceptService(String rawProviders,
            ComponentName componentName) {
        // If the app being removed matches any of the package names from
        // this list then don't add it in the output.
        Set<String> providers = new HashSet<>();
        if (rawProviders == null || componentName == null) {
            return providers;
        }
        for (String rawComponentName : rawProviders.split(":")) {
            if (TextUtils.isEmpty(rawComponentName) || rawComponentName.equals("null")) {
                Slog.d(TAG, "provider component name is empty or null");
                continue;
            }

            ComponentName cn = ComponentName.unflattenFromString(rawComponentName);
            if (cn != null && !cn.equals(componentName)) {
                providers.add(cn.flattenToString());
            }
        }

        return providers;
    }

    /** Gets the list of stored providers from a string removing any mention of package name. */
    public static Set<String> getStoredProvidersExceptPackage(
            String rawProviders, String packageName) {
        // If the app being removed matches any of the package names from
        // this list then don't add it in the output.
        Set<String> providers = new HashSet<>();
@@ -1213,8 +1389,7 @@ public final class CredentialManagerService
            return providers;
        }
        for (String rawComponentName : rawProviders.split(":")) {
            if (TextUtils.isEmpty(rawComponentName)
                    || rawComponentName.equals("null")) {
            if (TextUtils.isEmpty(rawComponentName) || rawComponentName.equals("null")) {
                Slog.d(TAG, "provider component name is empty or null");
                continue;
            }
+7 −0
Original line number Diff line number Diff line
@@ -57,6 +57,13 @@ public final class CredentialManagerServiceImpl extends
        }
    }

    @Nullable
    @Override
    @GuardedBy("mLock")
    public ComponentName getServiceComponentName() {
        return getComponentName();
    }

    @GuardedBy("mLock")
    public ComponentName getComponentName() {
        return mInfo.getServiceInfo().getComponentName();
+13 −7
Original line number Diff line number Diff line
@@ -51,20 +51,22 @@ public final class CredentialManagerServiceTest {

    @Test
    public void getStoredProviders_emptyValue_success() {
        Set<String> providers = CredentialManagerService.getStoredProviders("", "");
        Set<String> providers = CredentialManagerService.getStoredProvidersExceptPackage(
                "", "");
        assertThat(providers.size()).isEqualTo(0);
    }

    @Test
    public void getStoredProviders_nullValue_success() {
        Set<String> providers = CredentialManagerService.getStoredProviders(null, null);
        Set<String> providers = CredentialManagerService.getStoredProvidersExceptPackage(
                null, null);
        assertThat(providers.size()).isEqualTo(0);
    }

    @Test
    public void getStoredProviders_success() {
        Set<String> providers =
                CredentialManagerService.getStoredProviders(
                CredentialManagerService.getStoredProvidersExceptPackage(
                        "com.example.test/.TestActivity:com.example.test/.TestActivity2:"
                                + "com.example.test2/.TestActivity:blank",
                        "com.example.test");
@@ -74,6 +76,7 @@ public final class CredentialManagerServiceTest {

    @Test
    public void onProviderRemoved_success() {
        int userId = UserHandle.myUserId();
        setSettingsKey(
                Settings.Secure.AUTOFILL_SERVICE,
                CredentialManagerService.AUTOFILL_PLACEHOLDER_VALUE);
@@ -85,7 +88,7 @@ public final class CredentialManagerServiceTest {
                "com.example.test/com.example.test.TestActivity");

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

        assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE)).isEqualTo("");
        assertThat(getSettingsKey(Settings.Secure.CREDENTIAL_SERVICE))
@@ -95,6 +98,7 @@ public final class CredentialManagerServiceTest {

    @Test
    public void onProviderRemoved_notPrimaryRemoved_success() {
        int userId = UserHandle.myUserId();
        final String testCredentialPrimaryValue = "com.example.test/com.example.test.TestActivity";
        final String testCredentialValue =
                "com.example.test/com.example.test.TestActivity:com.example.test2/com.example.test2.TestActivity";
@@ -106,7 +110,7 @@ public final class CredentialManagerServiceTest {
        setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, testCredentialPrimaryValue);

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

        // Since the provider removed was not a primary provider then we should do nothing.
        assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE))
@@ -120,6 +124,7 @@ public final class CredentialManagerServiceTest {

    @Test
    public void onProviderRemoved_isAlsoAutofillProvider_success() {
        int userId = UserHandle.myUserId();
        setSettingsKey(
                Settings.Secure.AUTOFILL_SERVICE,
                "com.example.test/com.example.test.AutofillProvider");
@@ -131,7 +136,7 @@ public final class CredentialManagerServiceTest {
                "com.example.test/com.example.test.TestActivity");

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

        assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE)).isEqualTo("");
        assertThat(getSettingsKey(Settings.Secure.CREDENTIAL_SERVICE))
@@ -141,6 +146,7 @@ public final class CredentialManagerServiceTest {

    @Test
    public void onProviderRemoved_notPrimaryRemoved_isAlsoAutofillProvider_success() {
        int userId = UserHandle.myUserId();
        final String testCredentialPrimaryValue = "com.example.test/com.example.test.TestActivity";
        final String testCredentialValue =
                "com.example.test/com.example.test.TestActivity:com.example.test2/com.example.test2.TestActivity";
@@ -151,7 +157,7 @@ public final class CredentialManagerServiceTest {
        setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, testCredentialPrimaryValue);

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

        // Since the provider removed was not a primary provider then we should do nothing.
        assertCredentialPropertyEquals(