diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java index 5253d34a38f01..d4e8f27a7b344 100644 --- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java +++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java @@ -19,28 +19,9 @@ import android.annotation.ArrayRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; -import android.annotation.UserIdInt; -import android.app.AppGlobals; -import android.content.ComponentName; import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.ServiceInfo; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; -import android.text.TextUtils; -import android.util.Slog; -import android.util.SparseArray; -import android.util.SparseBooleanArray; -import android.util.TimeUtils; - -import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; /** * Gets the service name using a framework resources, temporarily changing the service if necessary @@ -48,259 +29,42 @@ import java.util.List; * * @hide */ -public final class FrameworkResourcesServiceNameResolver implements ServiceNameResolver { +public final class FrameworkResourcesServiceNameResolver extends ServiceNameBaseResolver { - private static final String TAG = FrameworkResourcesServiceNameResolver.class.getSimpleName(); - - /** Handler message to {@link #resetTemporaryService(int)} */ - private static final int MSG_RESET_TEMPORARY_SERVICE = 0; - - @NonNull - private final Context mContext; - @NonNull - private final Object mLock = new Object(); - @StringRes private final int mStringResourceId; @ArrayRes private final int mArrayResourceId; - private final boolean mIsMultiple; - /** - * Map of temporary service name list set by {@link #setTemporaryServices(int, String[], int)}, - * keyed by {@code userId}. - * - *

Typically used by Shell command and/or CTS tests to configure temporary services if - * mIsMultiple is true. - */ - @GuardedBy("mLock") - private final SparseArray mTemporaryServiceNamesList = new SparseArray<>(); - /** - * Map of default services that have been disabled by - * {@link #setDefaultServiceEnabled(int, boolean)},keyed by {@code userId}. - * - *

Typically used by Shell command and/or CTS tests. - */ - @GuardedBy("mLock") - private final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray(); - @Nullable - private NameResolverListener mOnSetCallback; - /** - * When the temporary service will expire (and reset back to the default). - */ - @GuardedBy("mLock") - private long mTemporaryServiceExpiration; - - /** - * Handler used to reset the temporary service name. - */ - @GuardedBy("mLock") - private Handler mTemporaryHandler; public FrameworkResourcesServiceNameResolver(@NonNull Context context, @StringRes int resourceId) { - mContext = context; + super(context, false); mStringResourceId = resourceId; mArrayResourceId = -1; - mIsMultiple = false; } public FrameworkResourcesServiceNameResolver(@NonNull Context context, @ArrayRes int resourceId, boolean isMultiple) { + super(context, isMultiple); if (!isMultiple) { throw new UnsupportedOperationException("Please use " + "FrameworkResourcesServiceNameResolver(context, @StringRes int) constructor " + "if single service mode is requested."); } - mContext = context; mStringResourceId = -1; mArrayResourceId = resourceId; - mIsMultiple = true; } @Override - public void setOnTemporaryServiceNameChangedCallback(@NonNull NameResolverListener callback) { - synchronized (mLock) { - this.mOnSetCallback = callback; - } + public String[] readServiceNameList(int userId) { + return mContext.getResources().getStringArray(mArrayResourceId); } + @Nullable @Override - public String getServiceName(@UserIdInt int userId) { - String[] serviceNames = getServiceNameList(userId); - return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0]; + public String readServiceName(int userId) { + return mContext.getResources().getString(mStringResourceId); } - @Override - public String getDefaultServiceName(@UserIdInt int userId) { - String[] serviceNames = getDefaultServiceNameList(userId); - return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0]; - } - - /** - * Gets the default list of the service names for the given user. - * - *

Typically implemented by services which want to provide multiple backends. - */ - @Override - public String[] getServiceNameList(int userId) { - synchronized (mLock) { - String[] temporaryNames = mTemporaryServiceNamesList.get(userId); - if (temporaryNames != null) { - // Always log it, as it should only be used on CTS or during development - Slog.w(TAG, "getServiceName(): using temporary name " - + Arrays.toString(temporaryNames) + " for user " + userId); - return temporaryNames; - } - final boolean disabled = mDefaultServicesDisabled.get(userId); - if (disabled) { - // Always log it, as it should only be used on CTS or during development - Slog.w(TAG, "getServiceName(): temporary name not set and default disabled for " - + "user " + userId); - return null; - } - return getDefaultServiceNameList(userId); - - } - } - - /** - * Gets the default list of the service names for the given user. - * - *

Typically implemented by services which want to provide multiple backends. - */ - @Override - public String[] getDefaultServiceNameList(int userId) { - synchronized (mLock) { - if (mIsMultiple) { - String[] serviceNameList = mContext.getResources().getStringArray(mArrayResourceId); - // Filter out unimplemented services - // Initialize the validated array as null because we do not know the final size. - List validatedServiceNameList = new ArrayList<>(); - try { - for (int i = 0; i < serviceNameList.length; i++) { - if (TextUtils.isEmpty(serviceNameList[i])) { - continue; - } - ComponentName serviceComponent = ComponentName.unflattenFromString( - serviceNameList[i]); - ServiceInfo serviceInfo = AppGlobals.getPackageManager().getServiceInfo( - serviceComponent, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); - if (serviceInfo != null) { - validatedServiceNameList.add(serviceNameList[i]); - } - } - } catch (Exception e) { - Slog.e(TAG, "Could not validate provided services.", e); - } - String[] validatedServiceNameArray = new String[validatedServiceNameList.size()]; - return validatedServiceNameList.toArray(validatedServiceNameArray); - } else { - final String name = mContext.getString(mStringResourceId); - return TextUtils.isEmpty(name) ? new String[0] : new String[]{name}; - } - } - } - - @Override - public boolean isConfiguredInMultipleMode() { - return mIsMultiple; - } - - @Override - public boolean isTemporary(@UserIdInt int userId) { - synchronized (mLock) { - return mTemporaryServiceNamesList.get(userId) != null; - } - } - - @Override - public void setTemporaryService(@UserIdInt int userId, @NonNull String componentName, - int durationMs) { - setTemporaryServices(userId, new String[]{componentName}, durationMs); - } - - @Override - public void setTemporaryServices(int userId, @NonNull String[] componentNames, int durationMs) { - synchronized (mLock) { - mTemporaryServiceNamesList.put(userId, componentNames); - - if (mTemporaryHandler == null) { - mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) { - @Override - public void handleMessage(Message msg) { - if (msg.what == MSG_RESET_TEMPORARY_SERVICE) { - synchronized (mLock) { - resetTemporaryService(userId); - } - } else { - Slog.wtf(TAG, "invalid handler msg: " + msg); - } - } - }; - } else { - mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE); - } - mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs; - mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs); - for (int i = 0; i < componentNames.length; i++) { - notifyTemporaryServiceNameChangedLocked(userId, componentNames[i], - /* isTemporary= */ true); - } - } - } - - @Override - public void resetTemporaryService(@UserIdInt int userId) { - synchronized (mLock) { - Slog.i(TAG, "resetting temporary service for user " + userId + " from " - + Arrays.toString(mTemporaryServiceNamesList.get(userId))); - mTemporaryServiceNamesList.remove(userId); - if (mTemporaryHandler != null) { - mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE); - mTemporaryHandler = null; - } - notifyTemporaryServiceNameChangedLocked(userId, /* newTemporaryName= */ null, - /* isTemporary= */ false); - } - } - - @Override - public boolean setDefaultServiceEnabled(int userId, boolean enabled) { - synchronized (mLock) { - final boolean currentlyEnabled = isDefaultServiceEnabledLocked(userId); - if (currentlyEnabled == enabled) { - Slog.i(TAG, "setDefaultServiceEnabled(" + userId + "): already " + enabled); - return false; - } - if (enabled) { - Slog.i(TAG, "disabling default service for user " + userId); - mDefaultServicesDisabled.removeAt(userId); - } else { - Slog.i(TAG, "enabling default service for user " + userId); - mDefaultServicesDisabled.put(userId, true); - } - } - return true; - } - - @Override - public boolean isDefaultServiceEnabled(int userId) { - synchronized (mLock) { - return isDefaultServiceEnabledLocked(userId); - } - } - - private boolean isDefaultServiceEnabledLocked(int userId) { - return !mDefaultServicesDisabled.get(userId); - } - - @Override - public String toString() { - synchronized (mLock) { - return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNamesList + "]"; - } - } // TODO(b/117779333): support proto @Override @@ -314,31 +78,4 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR pw.print(mDefaultServicesDisabled.size()); } } - - // TODO(b/117779333): support proto - @Override - public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) { - synchronized (mLock) { - final String[] temporaryNames = mTemporaryServiceNamesList.get(userId); - if (temporaryNames != null) { - pw.print("tmpName="); - pw.print(Arrays.toString(temporaryNames)); - final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime(); - pw.print(" (expires in "); - TimeUtils.formatDuration(ttl, pw); - pw.print("), "); - } - pw.print("defaultName="); - pw.print(getDefaultServiceName(userId)); - final boolean disabled = mDefaultServicesDisabled.get(userId); - pw.println(disabled ? " (disabled)" : " (enabled)"); - } - } - - private void notifyTemporaryServiceNameChangedLocked(@UserIdInt int userId, - @Nullable String newTemporaryName, boolean isTemporary) { - if (mOnSetCallback != null) { - mOnSetCallback.onNameResolved(userId, newTemporaryName, isTemporary); - } - } } diff --git a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java index cac7f53aad664..17d75e600c366 100644 --- a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java +++ b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java @@ -19,8 +19,11 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.content.Context; import android.provider.Settings; +import android.text.TextUtils; +import android.util.ArraySet; import java.io.PrintWriter; +import java.util.Set; /** * Gets the service name using a property from the {@link android.provider.Settings.Secure} @@ -28,21 +31,34 @@ import java.io.PrintWriter; * * @hide */ -public final class SecureSettingsServiceNameResolver implements ServiceNameResolver { +public final class SecureSettingsServiceNameResolver extends ServiceNameBaseResolver { + /** + * The delimiter to be used to parse the secure settings string. Services must make sure + * that this delimiter is used while adding component names to their secure setting property. + */ + private static final char COMPONENT_NAME_SEPARATOR = ':'; - private final @NonNull Context mContext; + private final TextUtils.SimpleStringSplitter mStringColonSplitter = + new TextUtils.SimpleStringSplitter(COMPONENT_NAME_SEPARATOR); @NonNull private final String mProperty; public SecureSettingsServiceNameResolver(@NonNull Context context, @NonNull String property) { - mContext = context; - mProperty = property; + this(context, property, /*isMultiple*/false); } - @Override - public String getDefaultServiceName(@UserIdInt int userId) { - return Settings.Secure.getStringForUser(mContext.getContentResolver(), mProperty, userId); + /** + * + * @param context the context required to retrieve the secure setting value + * @param property name of the secure setting key + * @param isMultiple true if the system service using this resolver needs to connect to + * multiple remote services, false otherwise + */ + public SecureSettingsServiceNameResolver(@NonNull Context context, @NonNull String property, + boolean isMultiple) { + super(context, isMultiple); + mProperty = property; } // TODO(b/117779333): support proto @@ -61,4 +77,34 @@ public final class SecureSettingsServiceNameResolver implements ServiceNameResol public String toString() { return "SecureSettingsServiceNameResolver[" + mProperty + "]"; } + + @Override + public String[] readServiceNameList(int userId) { + return parseColonDelimitedServiceNames( + Settings.Secure.getStringForUser( + mContext.getContentResolver(), mProperty, userId)); + } + + @Override + public String readServiceName(int userId) { + return Settings.Secure.getStringForUser( + mContext.getContentResolver(), mProperty, userId); + } + + private String[] parseColonDelimitedServiceNames(String serviceNames) { + final Set delimitedServices = new ArraySet<>(); + if (!TextUtils.isEmpty(serviceNames)) { + final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; + splitter.setString(serviceNames); + while (splitter.hasNext()) { + final String str = splitter.next(); + if (TextUtils.isEmpty(str)) { + continue; + } + delimitedServices.add(str); + } + } + String[] delimitedServicesArray = new String[delimitedServices.size()]; + return delimitedServices.toArray(delimitedServicesArray); + } } diff --git a/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java b/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java new file mode 100644 index 0000000000000..76ea05e361415 --- /dev/null +++ b/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.infra; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.AppGlobals; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.SystemClock; +import android.text.TextUtils; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseBooleanArray; +import android.util.TimeUtils; + +import com.android.internal.annotations.GuardedBy; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Gets the service name using a framework resources, temporarily changing the service if necessary + * (typically during CTS tests or service development). + * + * @hide + */ +public abstract class ServiceNameBaseResolver implements ServiceNameResolver { + + private static final String TAG = ServiceNameBaseResolver.class.getSimpleName(); + + /** Handler message to {@link #resetTemporaryService(int)} */ + private static final int MSG_RESET_TEMPORARY_SERVICE = 0; + + @NonNull + protected final Context mContext; + @NonNull + protected final Object mLock = new Object(); + + protected final boolean mIsMultiple; + /** + * Map of temporary service name list set by {@link #setTemporaryServices(int, String[], int)}, + * keyed by {@code userId}. + * + *

Typically used by Shell command and/or CTS tests to configure temporary services if + * mIsMultiple is true. + */ + @GuardedBy("mLock") + protected final SparseArray mTemporaryServiceNamesList = new SparseArray<>(); + /** + * Map of default services that have been disabled by + * {@link #setDefaultServiceEnabled(int, boolean)},keyed by {@code userId}. + * + *

Typically used by Shell command and/or CTS tests. + */ + @GuardedBy("mLock") + protected final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray(); + @Nullable + private NameResolverListener mOnSetCallback; + /** + * When the temporary service will expire (and reset back to the default). + */ + @GuardedBy("mLock") + private long mTemporaryServiceExpiration; + + /** + * Handler used to reset the temporary service name. + */ + @GuardedBy("mLock") + private Handler mTemporaryHandler; + + protected ServiceNameBaseResolver(Context context, boolean isMultiple) { + mContext = context; + mIsMultiple = isMultiple; + } + + @Override + public void setOnTemporaryServiceNameChangedCallback(@NonNull NameResolverListener callback) { + synchronized (mLock) { + this.mOnSetCallback = callback; + } + } + + @Override + public String getServiceName(@UserIdInt int userId) { + String[] serviceNames = getServiceNameList(userId); + return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0]; + } + + @Override + public String getDefaultServiceName(@UserIdInt int userId) { + String[] serviceNames = getDefaultServiceNameList(userId); + return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0]; + } + + /** + * Gets the default list of the service names for the given user. + * + *

Typically implemented by services which want to provide multiple backends. + */ + @Override + public String[] getServiceNameList(int userId) { + synchronized (mLock) { + String[] temporaryNames = mTemporaryServiceNamesList.get(userId); + if (temporaryNames != null) { + // Always log it, as it should only be used on CTS or during development + Slog.w(TAG, "getServiceName(): using temporary name " + + Arrays.toString(temporaryNames) + " for user " + userId); + return temporaryNames; + } + final boolean disabled = mDefaultServicesDisabled.get(userId); + if (disabled) { + // Always log it, as it should only be used on CTS or during development + Slog.w(TAG, "getServiceName(): temporary name not set and default disabled for " + + "user " + userId); + return null; + } + return getDefaultServiceNameList(userId); + + } + } + + /** + * Base classes must override this to read from the desired config e.g. framework resource, + * secure settings etc. + */ + @Nullable + public abstract String[] readServiceNameList(int userId); + + /** + * Base classes must override this to read from the desired config e.g. framework resource, + * secure settings etc. + */ + @Nullable + public abstract String readServiceName(int userId); + + /** + * Gets the default list of the service names for the given user. + * + *

Typically implemented by services which want to provide multiple backends. + */ + @Override + public String[] getDefaultServiceNameList(int userId) { + synchronized (mLock) { + if (mIsMultiple) { + String[] serviceNameList = readServiceNameList(userId); + // Filter out unimplemented services + // Initialize the validated array as null because we do not know the final size. + List validatedServiceNameList = new ArrayList<>(); + try { + for (int i = 0; i < serviceNameList.length; i++) { + if (TextUtils.isEmpty(serviceNameList[i])) { + continue; + } + ComponentName serviceComponent = ComponentName.unflattenFromString( + serviceNameList[i]); + ServiceInfo serviceInfo = AppGlobals.getPackageManager().getServiceInfo( + serviceComponent, + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); + if (serviceInfo != null) { + validatedServiceNameList.add(serviceNameList[i]); + } + } + } catch (Exception e) { + Slog.e(TAG, "Could not validate provided services.", e); + } + String[] validatedServiceNameArray = new String[validatedServiceNameList.size()]; + return validatedServiceNameList.toArray(validatedServiceNameArray); + } else { + final String name = readServiceName(userId); + return TextUtils.isEmpty(name) ? new String[0] : new String[]{name}; + } + } + } + + @Override + public boolean isConfiguredInMultipleMode() { + return mIsMultiple; + } + + @Override + public boolean isTemporary(@UserIdInt int userId) { + synchronized (mLock) { + return mTemporaryServiceNamesList.get(userId) != null; + } + } + + @Override + public void setTemporaryService(@UserIdInt int userId, @NonNull String componentName, + int durationMs) { + setTemporaryServices(userId, new String[]{componentName}, durationMs); + } + + @Override + public void setTemporaryServices(int userId, @NonNull String[] componentNames, int durationMs) { + synchronized (mLock) { + mTemporaryServiceNamesList.put(userId, componentNames); + + if (mTemporaryHandler == null) { + mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_RESET_TEMPORARY_SERVICE) { + synchronized (mLock) { + resetTemporaryService(userId); + } + } else { + Slog.wtf(TAG, "invalid handler msg: " + msg); + } + } + }; + } else { + mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE); + } + mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs; + mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs); + for (int i = 0; i < componentNames.length; i++) { + notifyTemporaryServiceNameChangedLocked(userId, componentNames[i], + /* isTemporary= */ true); + } + } + } + + @Override + public void resetTemporaryService(@UserIdInt int userId) { + synchronized (mLock) { + Slog.i(TAG, "resetting temporary service for user " + userId + " from " + + Arrays.toString(mTemporaryServiceNamesList.get(userId))); + mTemporaryServiceNamesList.remove(userId); + if (mTemporaryHandler != null) { + mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE); + mTemporaryHandler = null; + } + notifyTemporaryServiceNameChangedLocked(userId, /* newTemporaryName= */ null, + /* isTemporary= */ false); + } + } + + @Override + public boolean setDefaultServiceEnabled(int userId, boolean enabled) { + synchronized (mLock) { + final boolean currentlyEnabled = isDefaultServiceEnabledLocked(userId); + if (currentlyEnabled == enabled) { + Slog.i(TAG, "setDefaultServiceEnabled(" + userId + "): already " + enabled); + return false; + } + if (enabled) { + Slog.i(TAG, "disabling default service for user " + userId); + mDefaultServicesDisabled.removeAt(userId); + } else { + Slog.i(TAG, "enabling default service for user " + userId); + mDefaultServicesDisabled.put(userId, true); + } + } + return true; + } + + @Override + public boolean isDefaultServiceEnabled(int userId) { + synchronized (mLock) { + return isDefaultServiceEnabledLocked(userId); + } + } + + @GuardedBy("mLock") + private boolean isDefaultServiceEnabledLocked(int userId) { + return !mDefaultServicesDisabled.get(userId); + } + + @Override + public String toString() { + synchronized (mLock) { + return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNamesList + "]"; + } + } + + // TODO(b/117779333): support proto + @Override + public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) { + synchronized (mLock) { + final String[] temporaryNames = mTemporaryServiceNamesList.get(userId); + if (temporaryNames != null) { + pw.print("tmpName="); + pw.print(Arrays.toString(temporaryNames)); + final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime(); + pw.print(" (expires in "); + TimeUtils.formatDuration(ttl, pw); + pw.print("), "); + } + pw.print("defaultName="); + pw.print(getDefaultServiceName(userId)); + final boolean disabled = mDefaultServicesDisabled.get(userId); + pw.println(disabled ? " (disabled)" : " (enabled)"); + } + } + + private void notifyTemporaryServiceNameChangedLocked(@UserIdInt int userId, + @Nullable String newTemporaryName, boolean isTemporary) { + if (mOnSetCallback != null) { + mOnSetCallback.onNameResolved(userId, newTemporaryName, isTemporary); + } + } +} diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index 91f5c69994270..352a257e29151 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -49,7 +49,8 @@ public final class CredentialManagerService extends public CredentialManagerService(@NonNull Context context) { super(context, - new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE), + new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE, + /*isMultiple=*/true), null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER); }