Merge "Adding multiple provider support in AbstractMasterSystemService" into tm-dev

This commit is contained in:
Shashwat Razdan
2022-03-01 06:37:35 +00:00
committed by Android (Google) Code Review
9 changed files with 570 additions and 166 deletions

View File

@@ -4130,9 +4130,9 @@
This service must be trusted, as it can be activated without explicit consent of the user.
If no service with the specified name exists on the device, cloudsearch will be disabled.
Example: "com.android.intelligence/.CloudSearchService"
config_defaultCloudSearchService is for the single provider case.
config_defaultCloudSearchServices is for the multiple provider case.
-->
<string name="config_defaultCloudSearchService" translatable="false"></string>
<string-array name="config_defaultCloudSearchServices"></string-array>
<!-- The package name for the system's translation service.
This service must be trusted, as it can be activated without explicit consent of the user.

View File

@@ -3675,7 +3675,7 @@
<java-symbol type="string" name="notification_channel_network_status" />
<java-symbol type="string" name="notification_channel_network_alerts" />
<java-symbol type="string" name="notification_channel_network_available" />
<java-symbol type="string" name="config_defaultCloudSearchService" />
<java-symbol type="array" name="config_defaultCloudSearchServices" />
<java-symbol type="string" name="notification_channel_vpn" />
<java-symbol type="string" name="notification_channel_device_admin" />
<java-symbol type="string" name="notification_channel_alerts" />

View File

@@ -43,6 +43,8 @@ import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
@@ -62,7 +64,7 @@ public class CloudSearchManagerService extends
public CloudSearchManagerService(Context context) {
super(context, new FrameworkResourcesServiceNameResolver(context,
R.string.config_defaultCloudSearchService), null,
R.array.config_defaultCloudSearchServices, true), null,
PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mContext = context;
@@ -70,7 +72,25 @@ public class CloudSearchManagerService extends
@Override
protected CloudSearchPerUserService newServiceLocked(int resolvedUserId, boolean disabled) {
return new CloudSearchPerUserService(this, mLock, resolvedUserId);
return new CloudSearchPerUserService(this, mLock, resolvedUserId, "");
}
@Override
protected List<CloudSearchPerUserService> newServiceListLocked(int resolvedUserId,
boolean disabled, String[] serviceNames) {
if (serviceNames == null) {
return new ArrayList<>();
}
List<CloudSearchPerUserService> serviceList =
new ArrayList<>(serviceNames.length);
for (int i = 0; i < serviceNames.length; i++) {
if (serviceNames[i] == null) {
continue;
}
serviceList.add(new CloudSearchPerUserService(this, mLock, resolvedUserId,
serviceNames[i]));
}
return serviceList;
}
@Override
@@ -111,19 +131,28 @@ public class CloudSearchManagerService extends
@NonNull ICloudSearchManagerCallback callBack) {
searchRequest.setSource(
mContext.getPackageManager().getNameForUid(Binder.getCallingUid()));
runForUserLocked("search", searchRequest.getRequestId(), (service) ->
service.onSearchLocked(searchRequest, callBack));
runForUser("search", (service) -> {
synchronized (service.mLock) {
service.onSearchLocked(searchRequest, callBack);
}
});
}
@Override
public void returnResults(IBinder token, String requestId, SearchResponse response) {
runForUserLocked("returnResults", requestId, (service) ->
service.onReturnResultsLocked(token, requestId, response));
runForUser("returnResults", (service) -> {
synchronized (service.mLock) {
service.onReturnResultsLocked(token, requestId, response);
}
});
}
public void destroy(@NonNull SearchRequest searchRequest) {
runForUserLocked("destroyCloudSearchSession", searchRequest.getRequestId(),
(service) -> service.onDestroyLocked(searchRequest.getRequestId()));
runForUser("destroyCloudSearchSession", (service) -> {
synchronized (service.mLock) {
service.onDestroyLocked(searchRequest.getRequestId());
}
});
}
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@@ -134,8 +163,7 @@ public class CloudSearchManagerService extends
.exec(this, in, out, err, args, callback, resultReceiver);
}
private void runForUserLocked(@NonNull final String func,
@NonNull final String requestId,
private void runForUser(@NonNull final String func,
@NonNull final Consumer<CloudSearchPerUserService> c) {
ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
@@ -143,7 +171,7 @@ public class CloudSearchManagerService extends
null, null);
if (DEBUG) {
Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
Slog.d(TAG, "runForUser:" + func + " from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
}
Context ctx = getContext();
@@ -160,8 +188,11 @@ public class CloudSearchManagerService extends
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
final CloudSearchPerUserService service = getServiceForUserLocked(userId);
c.accept(service);
final List<CloudSearchPerUserService> services =
getServiceListForUserLocked(userId);
for (int i = 0; i < services.size(); i++) {
c.accept(services.get(i));
}
}
} finally {
Binder.restoreCallingIdentity(origId);

View File

@@ -54,7 +54,12 @@ public class CloudSearchManagerServiceShellCommand extends ShellCommand {
return 0;
}
final int duration = Integer.parseInt(getNextArgRequired());
mService.setTemporaryService(userId, serviceName, duration);
String[] services = serviceName.split(";");
if (services.length == 0) {
return 0;
} else {
mService.setTemporaryServices(userId, services, duration);
}
pw.println("CloudSearchService temporarily set to " + serviceName
+ " for " + duration + "ms");
break;

View File

@@ -49,6 +49,8 @@ public class CloudSearchPerUserService extends
@GuardedBy("mLock")
private final CircularQueue<String, CloudSearchCallbackInfo> mCallbackQueue =
new CircularQueue<>(QUEUE_SIZE);
private final String mServiceName;
private final ComponentName mRemoteComponentName;
@Nullable
@GuardedBy("mLock")
private RemoteCloudSearchService mRemoteService;
@@ -60,8 +62,10 @@ public class CloudSearchPerUserService extends
private boolean mZombie;
protected CloudSearchPerUserService(CloudSearchManagerService master,
Object lock, int userId) {
Object lock, int userId, String serviceName) {
super(master, lock, userId);
mServiceName = serviceName;
mRemoteComponentName = ComponentName.unflattenFromString(mServiceName);
}
@Override // from PerUserSystemService
@@ -108,7 +112,7 @@ public class CloudSearchPerUserService extends
? searchRequest.getSearchConstraints().getString(
SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER) : "";
String remoteServicePackageName = getServiceComponentName().getPackageName();
String remoteServicePackageName = mRemoteComponentName.getPackageName();
// By default, all providers are marked as wanted.
boolean wantedProvider = true;
if (filterList.length() > 0) {
@@ -150,11 +154,19 @@ public class CloudSearchPerUserService extends
/**
* Used to return results back to the clients.
*/
@GuardedBy("mLock")
public void onReturnResultsLocked(@NonNull IBinder token,
@NonNull String requestId,
@NonNull SearchResponse response) {
if (mRemoteService == null) {
return;
}
ICloudSearchService serviceInterface = mRemoteService.getServiceInterface();
if (serviceInterface == null || token != serviceInterface.asBinder()) {
return;
}
if (mCallbackQueue.containsKey(requestId)) {
response.setSource(mRemoteService.getComponentName().getPackageName());
response.setSource(mServiceName);
final CloudSearchCallbackInfo sessionInfo = mCallbackQueue.getElement(requestId);
try {
if (response.getStatusCode() == SearchResponse.SEARCH_STATUS_OK) {
@@ -163,6 +175,10 @@ public class CloudSearchPerUserService extends
sessionInfo.mCallback.onSearchFailed(response);
}
} catch (RemoteException e) {
if (mMaster.debug) {
Slog.e(TAG, "Exception in posting results");
e.printStackTrace();
}
onDestroyLocked(requestId);
}
}
@@ -297,7 +313,7 @@ public class CloudSearchPerUserService extends
@Nullable
private RemoteCloudSearchService getRemoteServiceLocked() {
if (mRemoteService == null) {
final String serviceName = getComponentNameLocked();
final String serviceName = getComponentNameForMultipleLocked(mServiceName);
if (serviceName == null) {
if (mMaster.verbose) {
Slog.v(TAG, "getRemoteServiceLocked(): not set");

View File

@@ -48,6 +48,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -168,10 +169,10 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
private final SparseBooleanArray mDisabledByUserRestriction;
/**
* Cache of services per user id.
* Cache of service list per user id.
*/
@GuardedBy("mLock")
private final SparseArray<S> mServicesCache = new SparseArray<>();
private final SparseArray<List<S>> mServicesCacheList = new SparseArray<>();
/**
* Value that determines whether the per-user service should be removed from the cache when its
@@ -252,8 +253,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
mServiceNameResolver = serviceNameResolver;
if (mServiceNameResolver != null) {
mServiceNameResolver.setOnTemporaryServiceNameChangedCallback(
(u, s, t) -> onServiceNameChanged(u, s, t));
this::onServiceNameChanged);
}
if (disallowProperty == null) {
mDisabledByUserRestriction = null;
@@ -308,7 +308,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@Override // from SystemService
public void onUserStopped(@NonNull TargetUser user) {
synchronized (mLock) {
removeCachedServiceLocked(user.getUserIdentifier());
removeCachedServiceListLocked(user.getUserIdentifier());
}
}
@@ -386,21 +386,58 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
synchronized (mLock) {
final S oldService = peekServiceForUserLocked(userId);
if (oldService != null) {
oldService.removeSelfFromCacheLocked();
oldService.removeSelfFromCache();
}
mServiceNameResolver.setTemporaryService(userId, componentName, durationMs);
}
}
/**
* Temporarily sets the service implementation.
*
* <p>Typically used by Shell command and/or CTS tests.
*
* @param componentNames list of the names of the new component
* @param durationMs how long the change will be valid (the service will be automatically
* reset
* to the default component after this timeout expires).
* @throws SecurityException if caller is not allowed to manage this service's settings.
* @throws IllegalArgumentException if value of {@code durationMs} is higher than
* {@link #getMaximumTemporaryServiceDurationMs()}.
*/
public final void setTemporaryServices(@UserIdInt int userId, @NonNull String[] componentNames,
int durationMs) {
Slog.i(mTag, "setTemporaryService(" + userId + ") to " + Arrays.toString(componentNames)
+ " for " + durationMs + "ms");
if (mServiceNameResolver == null) {
return;
}
enforceCallingPermissionForManagement();
Objects.requireNonNull(componentNames);
final int maxDurationMs = getMaximumTemporaryServiceDurationMs();
if (durationMs > maxDurationMs) {
throw new IllegalArgumentException(
"Max duration is " + maxDurationMs + " (called with " + durationMs + ")");
}
synchronized (mLock) {
final S oldService = peekServiceForUserLocked(userId);
if (oldService != null) {
oldService.removeSelfFromCache();
}
mServiceNameResolver.setTemporaryServices(userId, componentNames, durationMs);
}
}
/**
* Sets whether the default service should be used.
*
* <p>Typically used during CTS tests to make sure only the default service doesn't interfere
* with the test results.
*
* @throws SecurityException if caller is not allowed to manage this service's settings.
*
* @return whether the enabled state changed.
* @throws SecurityException if caller is not allowed to manage this service's settings.
*/
public final boolean setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
Slog.i(mTag, "setDefaultServiceEnabled() for userId " + userId + ": " + enabled);
@@ -420,7 +457,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
final S oldService = peekServiceForUserLocked(userId);
if (oldService != null) {
oldService.removeSelfFromCacheLocked();
oldService.removeSelfFromCache();
}
// Must update the service on cache so its initialization code is triggered
@@ -500,6 +537,21 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@Nullable
protected abstract S newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled);
/**
* Creates a new service list that will be added to the cache.
*
* @param resolvedUserId the resolved user id for the service.
* @param disabled whether the service is currently disabled (due to {@link UserManager}
* restrictions).
* @return a new instance.
*/
@Nullable
@GuardedBy("mLock")
protected List<S> newServiceListLocked(@UserIdInt int resolvedUserId, boolean disabled,
String[] serviceNames) {
throw new UnsupportedOperationException("newServiceListLocked not implemented. ");
}
/**
* Register the service for extra Settings changes (i.e., other than
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
@@ -516,7 +568,6 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
* <p><b>NOTE: </p>it doesn't need to register for
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
* {@link #getServiceSettingsProperty()}.
*
*/
@SuppressWarnings("unused")
protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
@@ -527,7 +578,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
* Callback for Settings changes that were registered though
* {@link #registerForExtraSettingsChanges(ContentResolver, ContentObserver)}.
*
* @param userId user associated with the change
* @param userId user associated with the change
* @param property Settings property changed.
*/
protected void onSettingsChanged(@UserIdInt int userId, @NonNull String property) {
@@ -539,18 +590,38 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@GuardedBy("mLock")
@NonNull
protected S getServiceForUserLocked(@UserIdInt int userId) {
List<S> services = getServiceListForUserLocked(userId);
return services == null || services.size() == 0 ? null : services.get(0);
}
/**
* Gets the service instance list for a user, creating instances if not present in the cache.
*/
@GuardedBy("mLock")
protected List<S> getServiceListForUserLocked(@UserIdInt int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
S service = mServicesCache.get(resolvedUserId);
if (service == null) {
List<S> services = mServicesCacheList.get(resolvedUserId);
if (services == null || services.size() == 0) {
final boolean disabled = isDisabledLocked(userId);
service = newServiceLocked(resolvedUserId, disabled);
if (!disabled) {
onServiceEnabledLocked(service, resolvedUserId);
if (mServiceNameResolver == null) {
return null;
}
mServicesCache.put(userId, service);
if (mServiceNameResolver.isConfiguredInMultipleMode()) {
services = newServiceListLocked(resolvedUserId, disabled,
mServiceNameResolver.getServiceNameList(userId));
} else {
services = new ArrayList<>();
services.add(newServiceLocked(resolvedUserId, disabled));
}
if (!disabled) {
for (int i = 0; i < services.size(); i++) {
onServiceEnabledLocked(services.get(i), resolvedUserId);
}
}
mServicesCacheList.put(userId, services);
}
return service;
return services;
}
/**
@@ -560,9 +631,20 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@GuardedBy("mLock")
@Nullable
protected S peekServiceForUserLocked(@UserIdInt int userId) {
List<S> serviceList = peekServiceListForUserLocked(userId);
return serviceList == null || serviceList.size() == 0 ? null : serviceList.get(0);
}
/**
* Gets the <b>existing</b> service instance for a user, returning {@code null} if not already
* present in the cache.
*/
@GuardedBy("mLock")
@Nullable
protected List<S> peekServiceListForUserLocked(@UserIdInt int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
return mServicesCache.get(resolvedUserId);
return mServicesCacheList.get(resolvedUserId);
}
/**
@@ -570,36 +652,59 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
*/
@GuardedBy("mLock")
protected void updateCachedServiceLocked(@UserIdInt int userId) {
updateCachedServiceLocked(userId, isDisabledLocked(userId));
updateCachedServiceListLocked(userId, isDisabledLocked(userId));
}
/**
* Checks whether the service is disabled (through {@link UserManager} restrictions) for the
* given user.
*/
@GuardedBy("mLock")
protected boolean isDisabledLocked(@UserIdInt int userId) {
return mDisabledByUserRestriction == null ? false : mDisabledByUserRestriction.get(userId);
return mDisabledByUserRestriction != null && mDisabledByUserRestriction.get(userId);
}
/**
* Updates a cached service for a given user.
*
* @param userId user handle.
* @param userId user handle.
* @param disabled whether the user is disabled.
* @return service for the user.
*/
@GuardedBy("mLock")
protected S updateCachedServiceLocked(@UserIdInt int userId, boolean disabled) {
final S service = getServiceForUserLocked(userId);
if (service != null) {
service.updateLocked(disabled);
if (!service.isEnabledLocked()) {
removeCachedServiceLocked(userId);
} else {
onServiceEnabledLocked(service, userId);
updateCachedServiceListLocked(userId, disabled);
return service;
}
/**
* Updates a cached service for a given user.
*
* @param userId user handle.
* @param disabled whether the user is disabled.
* @return service for the user.
*/
@GuardedBy("mLock")
protected List<S> updateCachedServiceListLocked(@UserIdInt int userId, boolean disabled) {
final List<S> services = getServiceListForUserLocked(userId);
if (services == null) {
return null;
}
for (int i = 0; i < services.size(); i++) {
S service = services.get(i);
if (service != null) {
synchronized (service.mLock) {
service.updateLocked(disabled);
if (!service.isEnabledLocked()) {
removeCachedServiceListLocked(userId);
} else {
onServiceEnabledLocked(services.get(i), userId);
}
}
}
}
return service;
return services;
}
/**
@@ -619,28 +724,32 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
* <p>By default doesn't do anything, but can be overridden by subclasses.
*/
@SuppressWarnings("unused")
@GuardedBy("mLock")
protected void onServiceEnabledLocked(@NonNull S service, @UserIdInt int userId) {
}
/**
* Removes a cached service for a given user.
* Removes a cached service list for a given user.
*
* @return the removed service.
*/
@GuardedBy("mLock")
@NonNull
protected final S removeCachedServiceLocked(@UserIdInt int userId) {
final S service = peekServiceForUserLocked(userId);
if (service != null) {
mServicesCache.delete(userId);
onServiceRemoved(service, userId);
protected final List<S> removeCachedServiceListLocked(@UserIdInt int userId) {
final List<S> services = peekServiceListForUserLocked(userId);
if (services != null) {
mServicesCacheList.delete(userId);
for (int i = 0; i < services.size(); i++) {
onServiceRemoved(services.get(i), userId);
}
}
return service;
return services;
}
/**
* Called before the package that provides the service for the given user is being updated.
*/
@GuardedBy("mLock")
protected void onServicePackageUpdatingLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageUpdatingLocked(" + userId + ")");
}
@@ -648,6 +757,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
/**
* Called after the package that provides the service for the given user is being updated.
*/
@GuardedBy("mLock")
protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageUpdated(" + userId + ")");
}
@@ -655,6 +765,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
/**
* Called after the package data that provides the service for the given user is cleared.
*/
@GuardedBy("mLock")
protected void onServicePackageDataClearedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageDataCleared(" + userId + ")");
}
@@ -662,6 +773,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
/**
* Called after the package that provides the service for the given user is restarted.
*/
@GuardedBy("mLock")
protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageRestarted(" + userId + ")");
}
@@ -679,14 +791,31 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
* <p>By default, it calls {@link #updateCachedServiceLocked(int)}; subclasses must either call
* that same method, or {@code super.onServiceNameChanged()}.
*
* @param userId user handle.
* @param userId user handle.
* @param serviceName the new service name.
* @param isTemporary whether the new service is temporary.
*/
protected void onServiceNameChanged(@UserIdInt int userId, @Nullable String serviceName,
boolean isTemporary) {
synchronized (mLock) {
updateCachedServiceLocked(userId);
updateCachedServiceListLocked(userId, isDisabledLocked(userId));
}
}
/**
* Called when the service name list has changed (typically when using temporary services).
*
* <p>By default, it calls {@link #updateCachedServiceLocked(int)}; subclasses must either call
* that same method, or {@code super.onServiceNameChanged()}.
*
* @param userId user handle.
* @param serviceNames the new service name list.
* @param isTemporary whether the new service is temporary.
*/
protected void onServiceNameListChanged(@UserIdInt int userId, @Nullable String[] serviceNames,
boolean isTemporary) {
synchronized (mLock) {
updateCachedServiceListLocked(userId, isDisabledLocked(userId));
}
}
@@ -695,9 +824,12 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
*/
@GuardedBy("mLock")
protected void visitServicesLocked(@NonNull Visitor<S> visitor) {
final int size = mServicesCache.size();
final int size = mServicesCacheList.size();
for (int i = 0; i < size; i++) {
visitor.visit(mServicesCache.valueAt(i));
List<S> services = mServicesCacheList.valueAt(i);
for (int j = 0; j < services.size(); j++) {
visitor.visit(services.get(j));
}
}
}
@@ -706,7 +838,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
*/
@GuardedBy("mLock")
protected void clearCacheLocked() {
mServicesCache.clear();
mServicesCacheList.clear();
}
/**
@@ -757,6 +889,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
}
// TODO(b/117779333): support proto
@GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
boolean realDebug = debug;
boolean realVerbose = verbose;
@@ -765,40 +898,64 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
try {
// Temporarily turn on full logging;
debug = verbose = true;
final int size = mServicesCache.size();
pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
pw.print(" Verbose: "); pw.println(realVerbose);
pw.print("Package policy flags: "); pw.println(mServicePackagePolicyFlags);
final int size = mServicesCacheList.size();
pw.print(prefix);
pw.print("Debug: ");
pw.print(realDebug);
pw.print(" Verbose: ");
pw.println(realVerbose);
pw.print("Package policy flags: ");
pw.println(mServicePackagePolicyFlags);
if (mUpdatingPackageNames != null) {
pw.print("Packages being updated: "); pw.println(mUpdatingPackageNames);
pw.print("Packages being updated: ");
pw.println(mUpdatingPackageNames);
}
dumpSupportedUsers(pw, prefix);
if (mServiceNameResolver != null) {
pw.print(prefix); pw.print("Name resolver: ");
mServiceNameResolver.dumpShort(pw); pw.println();
pw.print(prefix);
pw.print("Name resolver: ");
mServiceNameResolver.dumpShort(pw);
pw.println();
final List<UserInfo> users = getSupportedUsers();
for (int i = 0; i < users.size(); i++) {
final int userId = users.get(i).id;
pw.print(prefix2); pw.print(userId); pw.print(": ");
mServiceNameResolver.dumpShort(pw, userId); pw.println();
pw.print(prefix2);
pw.print(userId);
pw.print(": ");
mServiceNameResolver.dumpShort(pw, userId);
pw.println();
}
}
pw.print(prefix); pw.print("Users disabled by restriction: ");
pw.print(prefix);
pw.print("Users disabled by restriction: ");
pw.println(mDisabledByUserRestriction);
pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
pw.print(prefix);
pw.print("Allow instant service: ");
pw.println(mAllowInstantService);
final String settingsProperty = getServiceSettingsProperty();
if (settingsProperty != null) {
pw.print(prefix); pw.print("Settings property: "); pw.println(settingsProperty);
pw.print(prefix);
pw.print("Settings property: ");
pw.println(settingsProperty);
}
pw.print(prefix); pw.print("Cached services: ");
pw.print(prefix);
pw.print("Cached services: ");
if (size == 0) {
pw.println("none");
} else {
pw.println(size);
for (int i = 0; i < size; i++) {
pw.print(prefix); pw.print("Service at "); pw.print(i); pw.println(": ");
final S service = mServicesCache.valueAt(i);
service.dumpLocked(prefix2, pw);
pw.print(prefix);
pw.print("Service at ");
pw.print(i);
pw.println(": ");
final List<S> services = mServicesCacheList.valueAt(i);
for (int j = 0; j < services.size(); j++) {
S service = services.get(i);
synchronized (service.mLock) {
service.dumpLocked(prefix2, pw);
}
}
pw.println();
}
}
@@ -820,7 +977,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
final int userId = getChangingUserId();
synchronized (mLock) {
if (mUpdatingPackageNames == null) {
mUpdatingPackageNames = new SparseArray<String>(mServicesCache.size());
mUpdatingPackageNames = new SparseArray<String>(mServicesCacheList.size());
}
mUpdatingPackageNames.put(userId, packageName);
onServicePackageUpdatingLocked(userId);
@@ -835,7 +992,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
+ " because package " + activePackageName
+ " is being updated");
}
removeCachedServiceLocked(userId);
removeCachedServiceListLocked(userId);
if ((mServicePackagePolicyFlags & PACKAGE_UPDATE_POLICY_REFRESH_EAGER)
!= 0) {
@@ -901,7 +1058,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
handleActiveServiceRestartedLocked(activePackageName, userId);
} else {
removeCachedServiceLocked(userId);
removeCachedServiceListLocked(userId);
}
} else {
handlePackageUpdateLocked(pkg);
@@ -930,7 +1087,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
private void handleActiveServiceRemoved(@UserIdInt int userId) {
synchronized (mLock) {
removeCachedServiceLocked(userId);
removeCachedServiceListLocked(userId);
}
final String serviceSettingsProperty = getServiceSettingsProperty();
if (serviceSettingsProperty != null) {
@@ -939,6 +1096,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
}
}
@GuardedBy("mLock")
private void handleActiveServiceRestartedLocked(String activePackageName,
@UserIdInt int userId) {
if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_NO_REFRESH) != 0) {
@@ -952,7 +1110,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
+ " because package " + activePackageName
+ " is being restarted");
}
removeCachedServiceLocked(userId);
removeCachedServiceListLocked(userId);
if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_REFRESH_EAGER) != 0) {
if (debug) {
@@ -966,14 +1124,27 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@Override
public void onPackageModified(String packageName) {
if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
synchronized (mLock) {
if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
if (mServiceNameResolver == null) {
return;
if (mServiceNameResolver == null) {
return;
}
final int userId = getChangingUserId();
final String[] serviceNames = mServiceNameResolver.getDefaultServiceNameList(
userId);
if (serviceNames != null) {
for (int i = 0; i < serviceNames.length; i++) {
peekAndUpdateCachedServiceLocked(packageName, userId, serviceNames[i]);
}
}
}
}
final int userId = getChangingUserId();
final String serviceName = mServiceNameResolver.getDefaultServiceName(userId);
@GuardedBy("mLock")
private void peekAndUpdateCachedServiceLocked(String packageName, int userId,
String serviceName) {
if (serviceName == null) {
return;
}
@@ -997,6 +1168,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
}
}
@GuardedBy("mLock")
private String getActiveServicePackageNameLocked() {
final int userId = getChangingUserId();
final S service = peekServiceForUserLocked(userId);
@@ -1017,7 +1189,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
};
// package changes
monitor.register(getContext(), null, UserHandle.ALL, true);
monitor.register(getContext(), null, UserHandle.ALL, true);
}
/**

View File

@@ -43,14 +43,13 @@ import java.io.PrintWriter;
*
* @param <M> "main" service class.
* @param <S> "real" service class.
*
* @hide
*/
public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S, M>,
M extends AbstractMasterSystemService<M, S>> {
protected final @UserIdInt int mUserId;
protected final Object mLock;
@UserIdInt protected final int mUserId;
public final Object mLock;
protected final String mTag = getClass().getSimpleName();
protected final M mMaster;
@@ -91,14 +90,14 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
* <p><b>MUST</b> be overridden by subclasses that bind to an
* {@link com.android.internal.infra.AbstractRemoteService}.
*
* @throws NameNotFoundException if the service does not exist.
* @throws SecurityException if the service does not have the proper permissions to be bound to.
* @throws UnsupportedOperationException if subclass binds to a remote service but does not
* overrides it.
*
* @return new {@link ServiceInfo},
* @throws NameNotFoundException if the service does not exist.
* @throws SecurityException if the service does not have the proper permissions to
* be bound to.
* @throws UnsupportedOperationException if subclass binds to a remote service but does not
* overrides it.
*/
protected @NonNull ServiceInfo newServiceInfoLocked(
@NonNull protected ServiceInfo newServiceInfoLocked(
@SuppressWarnings("unused") @NonNull ComponentName serviceComponent)
throws NameNotFoundException {
throw new UnsupportedOperationException("not overridden");
@@ -137,7 +136,6 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
* previous state.
*
* @param disabled whether the service is disabled (due to {@link UserManager} restrictions).
*
* @return whether the disabled state changed.
*/
@GuardedBy("mLock")
@@ -154,18 +152,48 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
updateIsSetupComplete(mUserId);
mDisabled = disabled;
updateServiceInfoLocked();
if (mMaster.mServiceNameResolver.isConfiguredInMultipleMode()) {
updateServiceInfoListLocked();
} else {
updateServiceInfoLocked();
}
return wasEnabled != isEnabledLocked();
}
/**
* Updates the internal reference to the service info, and returns the service's component.
*/
@GuardedBy("mLock")
protected final ComponentName updateServiceInfoLocked() {
ComponentName serviceComponent = null;
if (mMaster.mServiceNameResolver != null) {
ServiceInfo serviceInfo = null;
ComponentName[] componentNames = updateServiceInfoListLocked();
return componentNames == null || componentNames.length == 0 ? null : componentNames[0];
}
/**
* Updates the internal reference to the service info, and returns the service's component.
*/
@GuardedBy("mLock")
protected final ComponentName[] updateServiceInfoListLocked() {
if (mMaster.mServiceNameResolver == null) {
return null;
}
if (!mMaster.mServiceNameResolver.isConfiguredInMultipleMode()) {
final String componentName = getComponentNameLocked();
return new ComponentName[] { getServiceComponent(componentName) };
}
final String[] componentNames = mMaster.mServiceNameResolver.getServiceNameList(
mUserId);
ComponentName[] serviceComponents = new ComponentName[componentNames.length];
for (int i = 0; i < componentNames.length; i++) {
serviceComponents[i] = getServiceComponent(componentNames[i]);
}
return serviceComponents;
}
private ComponentName getServiceComponent(String componentName) {
synchronized (mLock) {
ServiceInfo serviceInfo = null;
ComponentName serviceComponent = null;
if (!TextUtils.isEmpty(componentName)) {
try {
serviceComponent = ComponentName.unflattenFromString(componentName);
@@ -196,14 +224,14 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
Slog.e(mTag, "Bad ServiceInfo for '" + componentName + "': " + e);
mServiceInfo = null;
}
return serviceComponent;
}
return serviceComponent;
}
/**
* Gets the user associated with this service.
*/
public final @UserIdInt int getUserId() {
@UserIdInt public final int getUserId() {
return mUserId;
}
@@ -229,15 +257,34 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
/**
* Gets the current name of the service, which is either the default service or the
* {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
* {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
*/
protected final @Nullable String getComponentNameLocked() {
@Nullable
@GuardedBy("mLock")
protected final String getComponentNameLocked() {
return mMaster.mServiceNameResolver.getServiceName(mUserId);
}
/**
* Gets the current name of the service, which is either the default service or the
* {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
*/
@Nullable
@GuardedBy("mLock")
protected final String getComponentNameForMultipleLocked(String serviceName) {
String[] services = mMaster.mServiceNameResolver.getServiceNameList(mUserId);
for (int i = 0; i < services.length; i++) {
if (serviceName.equals(services[i])) {
return services[i];
}
}
return null;
}
/**
* Checks whether the current service for the user was temporarily set.
*/
@GuardedBy("mLock")
public final boolean isTemporaryServiceSetLocked() {
return mMaster.mServiceNameResolver.isTemporary(mUserId);
}
@@ -245,6 +292,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
/**
* Resets the temporary service implementation to the default component.
*/
@GuardedBy("mLock")
protected final void resetTemporaryServiceLocked() {
mMaster.mServiceNameResolver.resetTemporaryService(mUserId);
}
@@ -268,6 +316,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
return mServiceInfo == null ? null : mServiceInfo.getComponentName();
}
}
/**
* Gets the name of the of the app this service binds to, or {@code null} if the service is
* disabled.
@@ -303,8 +352,10 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
/**
* Removes the service from the main service's cache.
*/
protected final void removeSelfFromCacheLocked() {
mMaster.removeCachedServiceLocked(mUserId);
protected final void removeSelfFromCache() {
synchronized (mMaster.mLock) {
mMaster.removeCachedServiceListLocked(mUserId);
}
}
/**
@@ -327,6 +378,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
* Gets the target SDK level of the service this service binds to,
* or {@code 0} if the service is disabled.
*/
@GuardedBy("mLock")
public final int getTargedSdkLocked() {
return mServiceInfo == null ? 0 : mServiceInfo.applicationInfo.targetSdkVersion;
}
@@ -334,6 +386,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
/**
* Gets whether the device already finished setup.
*/
@GuardedBy("mLock")
protected final boolean isSetupCompletedLocked() {
return mSetupComplete;
}
@@ -348,19 +401,32 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
// TODO(b/117779333): support proto
@GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("User: "); pw.println(mUserId);
pw.print(prefix);
pw.print("User: ");
pw.println(mUserId);
if (mServiceInfo != null) {
pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabelLocked());
pw.print(prefix); pw.print("Target SDK: "); pw.println(getTargedSdkLocked());
pw.print(prefix);
pw.print("Service Label: ");
pw.println(getServiceLabelLocked());
pw.print(prefix);
pw.print("Target SDK: ");
pw.println(getTargedSdkLocked());
}
if (mMaster.mServiceNameResolver != null) {
pw.print(prefix); pw.print("Name resolver: ");
mMaster.mServiceNameResolver.dumpShort(pw, mUserId); pw.println();
pw.print(prefix);
pw.print("Name resolver: ");
mMaster.mServiceNameResolver.dumpShort(pw, mUserId);
pw.println();
}
pw.print(prefix); pw.print("Disabled by UserManager: "); pw.println(mDisabled);
pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
pw.print(prefix);
pw.print("Disabled by UserManager: ");
pw.println(mDisabled);
pw.print(prefix);
pw.print("Setup complete: ");
pw.println(mSetupComplete);
if (mServiceInfo != null) {
pw.print(prefix); pw.print("Service UID: ");
pw.print(prefix);
pw.print("Service UID: ");
pw.println(mServiceInfo.applicationInfo.uid);
}
pw.println();

View File

@@ -15,6 +15,7 @@
*/
package com.android.server.infra;
import android.annotation.ArrayRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
@@ -33,6 +34,7 @@ import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
import java.util.Arrays;
/**
* Gets the service name using a framework resources, temporarily changing the service if necessary
@@ -47,20 +49,20 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
/** Handler message to {@link #resetTemporaryService(int)} */
private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
private final @NonNull Context mContext;
private final @NonNull Object mLock = new Object();
private final @StringRes int mResourceId;
private @Nullable NameResolverListener mOnSetCallback;
@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 set by {@link #setTemporaryService(int, String, int)},
* Map of temporary service name list set by {@link #setTemporaryServices(int, String[], int)},
* keyed by {@code userId}.
*
* <p>Typically used by Shell command and/or CTS tests.
* <p>Typically used by Shell command and/or CTS tests to configure temporary services if
* mIsMultiple is true.
*/
@GuardedBy("mLock")
private final SparseArray<String> mTemporaryServiceNames = new SparseArray<>();
private final SparseArray<String[]> mTemporaryServiceNamesList = new SparseArray<>();
/**
* Map of default services that have been disabled by
* {@link #setDefaultServiceEnabled(int, boolean)},keyed by {@code userId}.
@@ -69,7 +71,7 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
*/
@GuardedBy("mLock")
private final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray();
@Nullable private NameResolverListener mOnSetCallback;
/**
* When the temporary service will expire (and reset back to the default).
*/
@@ -85,7 +87,22 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
public FrameworkResourcesServiceNameResolver(@NonNull Context context,
@StringRes int resourceId) {
mContext = context;
mResourceId = resourceId;
mStringResourceId = resourceId;
mArrayResourceId = -1;
mIsMultiple = false;
}
public FrameworkResourcesServiceNameResolver(@NonNull Context context,
@ArrayRes int resourceId, boolean 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
@@ -96,22 +113,31 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
}
@Override
public String getDefaultServiceName(@UserIdInt int userId) {
synchronized (mLock) {
final String name = mContext.getString(mResourceId);
return TextUtils.isEmpty(name) ? null : name;
}
public String getServiceName(@UserIdInt int userId) {
String[] serviceNames = getServiceNameList(userId);
return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0];
}
@Override
public String getServiceName(@UserIdInt int userId) {
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.
*
* <p>Typically implemented by services which want to provide multiple backends.
*/
@Override
public String[] getServiceNameList(int userId) {
synchronized (mLock) {
final String temporaryName = mTemporaryServiceNames.get(userId);
if (temporaryName != null) {
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 " + temporaryName
+ " for user " + userId);
return temporaryName;
Slog.w(TAG, "getServiceName(): using temporary name "
+ Arrays.toString(temporaryNames) + " for user " + userId);
return temporaryNames;
}
final boolean disabled = mDefaultServicesDisabled.get(userId);
if (disabled) {
@@ -120,22 +146,50 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
+ "user " + userId);
return null;
}
return getDefaultServiceName(userId);
return getDefaultServiceNameList(userId);
}
}
/**
* Gets the default list of the service names for the given user.
*
* <p>Typically implemented by services which want to provide multiple backends.
*/
@Override
public String[] getDefaultServiceNameList(int userId) {
synchronized (mLock) {
if (mIsMultiple) {
return mContext.getResources().getStringArray(mArrayResourceId);
} 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 mTemporaryServiceNames.get(userId) != null;
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) {
mTemporaryServiceNames.put(userId, componentName);
mTemporaryServiceNamesList.put(userId, componentNames);
if (mTemporaryHandler == null) {
mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
@@ -155,8 +209,10 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
}
mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
notifyTemporaryServiceNameChangedLocked(userId, componentName,
/* isTemporary= */ true);
for (int i = 0; i < componentNames.length; i++) {
notifyTemporaryServiceNameChangedLocked(userId, componentNames[i],
/* isTemporary= */ true);
}
}
}
@@ -164,8 +220,8 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
public void resetTemporaryService(@UserIdInt int userId) {
synchronized (mLock) {
Slog.i(TAG, "resetting temporary service for user " + userId + " from "
+ mTemporaryServiceNames.get(userId));
mTemporaryServiceNames.remove(userId);
+ Arrays.toString(mTemporaryServiceNamesList.get(userId)));
mTemporaryServiceNamesList.remove(userId);
if (mTemporaryHandler != null) {
mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
mTemporaryHandler = null;
@@ -207,16 +263,21 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
@Override
public String toString() {
return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNames + "]";
synchronized (mLock) {
return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNamesList + "]";
}
}
// TODO(b/117779333): support proto
@Override
public void dumpShort(@NonNull PrintWriter pw) {
synchronized (mLock) {
pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId);
pw.print(", numberTemps="); pw.print(mTemporaryServiceNames.size());
pw.print(", enabledDefaults="); pw.print(mDefaultServicesDisabled.size());
pw.print("FrameworkResourcesServiceNamer: resId=");
pw.print(mStringResourceId);
pw.print(", numberTemps=");
pw.print(mTemporaryServiceNamesList.size());
pw.print(", enabledDefaults=");
pw.print(mDefaultServicesDisabled.size());
}
}
@@ -224,13 +285,17 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
@Override
public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
synchronized (mLock) {
final String temporaryName = mTemporaryServiceNames.get(userId);
if (temporaryName != null) {
pw.print("tmpName="); pw.print(temporaryName);
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(" (expires in ");
TimeUtils.formatDuration(ttl, pw);
pw.print("), ");
}
pw.print("defaultName="); pw.print(getDefaultServiceName(userId));
pw.print("defaultName=");
pw.print(getDefaultServiceName(userId));
final boolean disabled = mDefaultServicesDisabled.get(userId);
pw.println(disabled ? " (disabled)" : " (enabled)");
}

View File

@@ -34,7 +34,7 @@ public interface ServiceNameResolver {
/**
* Listener for name changes.
*/
public interface NameResolverListener {
interface NameResolverListener {
/**
* The name change callback.
@@ -63,6 +63,30 @@ public interface ServiceNameResolver {
@Nullable
String getDefaultServiceName(@UserIdInt int userId);
/**
* Gets the default list of names of the services for the given user.
*
* <p>Typically implemented by reading a Settings property or framework resource.
*/
@Nullable
default String[] getDefaultServiceNameList(@UserIdInt int userId) {
if (isConfiguredInMultipleMode()) {
throw new UnsupportedOperationException("getting default service list not supported");
} else {
return new String[] { getDefaultServiceName(userId) };
}
}
/**
* Returns whether the resolver is configured to connect to multiple backend services.
* The default return type is false.
*
* <p>Typically implemented by reading a Settings property or framework resource.
*/
default boolean isConfiguredInMultipleMode() {
return false;
}
/**
* Gets the current name of the service for the given user
*
@@ -75,6 +99,18 @@ public interface ServiceNameResolver {
return getDefaultServiceName(userId);
}
/**
* Gets the current name of the service for the given user
*
* @return either the temporary name (set by
* {@link #setTemporaryService(int, String, int)}, or the
* {@link #getDefaultServiceName(int) default name}.
*/
@Nullable
default String[] getServiceNameList(@UserIdInt int userId) {
return getDefaultServiceNameList(userId);
}
/**
* Checks whether the current service is temporary for the given user.
*/
@@ -85,11 +121,11 @@ public interface ServiceNameResolver {
/**
* Temporarily sets the service implementation for the given user.
*
* @param userId user handle
* @param userId user handle
* @param componentName name of the new component
* @param durationMs how long the change will be valid (the service will be automatically reset
* to the default component after this timeout expires).
*
* @param durationMs how long the change will be valid (the service will be automatically
* reset
* to the default component after this timeout expires).
* @throws UnsupportedOperationException if not implemented.
*/
default void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
@@ -97,11 +133,25 @@ public interface ServiceNameResolver {
throw new UnsupportedOperationException("temporary user not supported");
}
/**
* Temporarily sets the service implementation for the given user.
*
* @param userId user handle
* @param componentNames list of the names of the new component
* @param durationMs how long the change will be valid (the service will be automatically
* reset
* to the default component after this timeout expires).
* @throws UnsupportedOperationException if not implemented.
*/
default void setTemporaryServices(@UserIdInt int userId, @NonNull String[] componentNames,
int durationMs) {
throw new UnsupportedOperationException("temporary user not supported");
}
/**
* Resets the temporary service implementation to the default component for the given user.
*
* @param userId user handle
*
* @throws UnsupportedOperationException if not implemented.
*/
default void resetTemporaryService(@UserIdInt int userId) {
@@ -114,11 +164,11 @@ public interface ServiceNameResolver {
* <p>Typically used during CTS tests to make sure only the default service doesn't interfere
* with the test results.
*
* @param userId user handle
* @param userId user handle
* @param enabled whether the default service should be used when the temporary service is not
* set. If the service enabled state is already that value, the command is ignored and this
* method return {@code false}.
*
* set. If the service enabled state is already that value, the command is
* ignored and this
* method return {@code false}.
* @return whether the enabled state changed.
* @throws UnsupportedOperationException if not implemented.
*/
@@ -133,7 +183,6 @@ public interface ServiceNameResolver {
* with the test results.
*
* @param userId user handle
*
* @throws UnsupportedOperationException if not implemented.
*/
default boolean isDefaultServiceEnabled(@UserIdInt int userId) {