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. 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. If no service with the specified name exists on the device, cloudsearch will be disabled.
Example: "com.android.intelligence/.CloudSearchService" 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. <!-- 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. 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_status" />
<java-symbol type="string" name="notification_channel_network_alerts" /> <java-symbol type="string" name="notification_channel_network_alerts" />
<java-symbol type="string" name="notification_channel_network_available" /> <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_vpn" />
<java-symbol type="string" name="notification_channel_device_admin" /> <java-symbol type="string" name="notification_channel_device_admin" />
<java-symbol type="string" name="notification_channel_alerts" /> <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 com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@@ -62,7 +64,7 @@ public class CloudSearchManagerService extends
public CloudSearchManagerService(Context context) { public CloudSearchManagerService(Context context) {
super(context, new FrameworkResourcesServiceNameResolver(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); PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mContext = context; mContext = context;
@@ -70,7 +72,25 @@ public class CloudSearchManagerService extends
@Override @Override
protected CloudSearchPerUserService newServiceLocked(int resolvedUserId, boolean disabled) { 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 @Override
@@ -111,19 +131,28 @@ public class CloudSearchManagerService extends
@NonNull ICloudSearchManagerCallback callBack) { @NonNull ICloudSearchManagerCallback callBack) {
searchRequest.setSource( searchRequest.setSource(
mContext.getPackageManager().getNameForUid(Binder.getCallingUid())); mContext.getPackageManager().getNameForUid(Binder.getCallingUid()));
runForUserLocked("search", searchRequest.getRequestId(), (service) -> runForUser("search", (service) -> {
service.onSearchLocked(searchRequest, callBack)); synchronized (service.mLock) {
service.onSearchLocked(searchRequest, callBack);
}
});
} }
@Override @Override
public void returnResults(IBinder token, String requestId, SearchResponse response) { public void returnResults(IBinder token, String requestId, SearchResponse response) {
runForUserLocked("returnResults", requestId, (service) -> runForUser("returnResults", (service) -> {
service.onReturnResultsLocked(token, requestId, response)); synchronized (service.mLock) {
service.onReturnResultsLocked(token, requestId, response);
}
});
} }
public void destroy(@NonNull SearchRequest searchRequest) { public void destroy(@NonNull SearchRequest searchRequest) {
runForUserLocked("destroyCloudSearchSession", searchRequest.getRequestId(), runForUser("destroyCloudSearchSession", (service) -> {
(service) -> service.onDestroyLocked(searchRequest.getRequestId())); synchronized (service.mLock) {
service.onDestroyLocked(searchRequest.getRequestId());
}
});
} }
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, 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); .exec(this, in, out, err, args, callback, resultReceiver);
} }
private void runForUserLocked(@NonNull final String func, private void runForUser(@NonNull final String func,
@NonNull final String requestId,
@NonNull final Consumer<CloudSearchPerUserService> c) { @NonNull final Consumer<CloudSearchPerUserService> c) {
ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class); ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
@@ -143,7 +171,7 @@ public class CloudSearchManagerService extends
null, null); null, null);
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid() Slog.d(TAG, "runForUser:" + func + " from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()); + ", uid=" + Binder.getCallingUid());
} }
Context ctx = getContext(); Context ctx = getContext();
@@ -160,8 +188,11 @@ public class CloudSearchManagerService extends
final long origId = Binder.clearCallingIdentity(); final long origId = Binder.clearCallingIdentity();
try { try {
synchronized (mLock) { synchronized (mLock) {
final CloudSearchPerUserService service = getServiceForUserLocked(userId); final List<CloudSearchPerUserService> services =
c.accept(service); getServiceListForUserLocked(userId);
for (int i = 0; i < services.size(); i++) {
c.accept(services.get(i));
}
} }
} finally { } finally {
Binder.restoreCallingIdentity(origId); Binder.restoreCallingIdentity(origId);

View File

@@ -54,7 +54,12 @@ public class CloudSearchManagerServiceShellCommand extends ShellCommand {
return 0; return 0;
} }
final int duration = Integer.parseInt(getNextArgRequired()); 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 pw.println("CloudSearchService temporarily set to " + serviceName
+ " for " + duration + "ms"); + " for " + duration + "ms");
break; break;

View File

@@ -49,6 +49,8 @@ public class CloudSearchPerUserService extends
@GuardedBy("mLock") @GuardedBy("mLock")
private final CircularQueue<String, CloudSearchCallbackInfo> mCallbackQueue = private final CircularQueue<String, CloudSearchCallbackInfo> mCallbackQueue =
new CircularQueue<>(QUEUE_SIZE); new CircularQueue<>(QUEUE_SIZE);
private final String mServiceName;
private final ComponentName mRemoteComponentName;
@Nullable @Nullable
@GuardedBy("mLock") @GuardedBy("mLock")
private RemoteCloudSearchService mRemoteService; private RemoteCloudSearchService mRemoteService;
@@ -60,8 +62,10 @@ public class CloudSearchPerUserService extends
private boolean mZombie; private boolean mZombie;
protected CloudSearchPerUserService(CloudSearchManagerService master, protected CloudSearchPerUserService(CloudSearchManagerService master,
Object lock, int userId) { Object lock, int userId, String serviceName) {
super(master, lock, userId); super(master, lock, userId);
mServiceName = serviceName;
mRemoteComponentName = ComponentName.unflattenFromString(mServiceName);
} }
@Override // from PerUserSystemService @Override // from PerUserSystemService
@@ -108,7 +112,7 @@ public class CloudSearchPerUserService extends
? searchRequest.getSearchConstraints().getString( ? searchRequest.getSearchConstraints().getString(
SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER) : ""; SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER) : "";
String remoteServicePackageName = getServiceComponentName().getPackageName(); String remoteServicePackageName = mRemoteComponentName.getPackageName();
// By default, all providers are marked as wanted. // By default, all providers are marked as wanted.
boolean wantedProvider = true; boolean wantedProvider = true;
if (filterList.length() > 0) { if (filterList.length() > 0) {
@@ -150,11 +154,19 @@ public class CloudSearchPerUserService extends
/** /**
* Used to return results back to the clients. * Used to return results back to the clients.
*/ */
@GuardedBy("mLock")
public void onReturnResultsLocked(@NonNull IBinder token, public void onReturnResultsLocked(@NonNull IBinder token,
@NonNull String requestId, @NonNull String requestId,
@NonNull SearchResponse response) { @NonNull SearchResponse response) {
if (mRemoteService == null) {
return;
}
ICloudSearchService serviceInterface = mRemoteService.getServiceInterface();
if (serviceInterface == null || token != serviceInterface.asBinder()) {
return;
}
if (mCallbackQueue.containsKey(requestId)) { if (mCallbackQueue.containsKey(requestId)) {
response.setSource(mRemoteService.getComponentName().getPackageName()); response.setSource(mServiceName);
final CloudSearchCallbackInfo sessionInfo = mCallbackQueue.getElement(requestId); final CloudSearchCallbackInfo sessionInfo = mCallbackQueue.getElement(requestId);
try { try {
if (response.getStatusCode() == SearchResponse.SEARCH_STATUS_OK) { if (response.getStatusCode() == SearchResponse.SEARCH_STATUS_OK) {
@@ -163,6 +175,10 @@ public class CloudSearchPerUserService extends
sessionInfo.mCallback.onSearchFailed(response); sessionInfo.mCallback.onSearchFailed(response);
} }
} catch (RemoteException e) { } catch (RemoteException e) {
if (mMaster.debug) {
Slog.e(TAG, "Exception in posting results");
e.printStackTrace();
}
onDestroyLocked(requestId); onDestroyLocked(requestId);
} }
} }
@@ -297,7 +313,7 @@ public class CloudSearchPerUserService extends
@Nullable @Nullable
private RemoteCloudSearchService getRemoteServiceLocked() { private RemoteCloudSearchService getRemoteServiceLocked() {
if (mRemoteService == null) { if (mRemoteService == null) {
final String serviceName = getComponentNameLocked(); final String serviceName = getComponentNameForMultipleLocked(mServiceName);
if (serviceName == null) { if (serviceName == null) {
if (mMaster.verbose) { if (mMaster.verbose) {
Slog.v(TAG, "getRemoteServiceLocked(): not set"); 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.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -168,10 +169,10 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
private final SparseBooleanArray mDisabledByUserRestriction; private final SparseBooleanArray mDisabledByUserRestriction;
/** /**
* Cache of services per user id. * Cache of service list per user id.
*/ */
@GuardedBy("mLock") @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 * 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; mServiceNameResolver = serviceNameResolver;
if (mServiceNameResolver != null) { if (mServiceNameResolver != null) {
mServiceNameResolver.setOnTemporaryServiceNameChangedCallback( mServiceNameResolver.setOnTemporaryServiceNameChangedCallback(
(u, s, t) -> onServiceNameChanged(u, s, t)); this::onServiceNameChanged);
} }
if (disallowProperty == null) { if (disallowProperty == null) {
mDisabledByUserRestriction = null; mDisabledByUserRestriction = null;
@@ -308,7 +308,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@Override // from SystemService @Override // from SystemService
public void onUserStopped(@NonNull TargetUser user) { public void onUserStopped(@NonNull TargetUser user) {
synchronized (mLock) { synchronized (mLock) {
removeCachedServiceLocked(user.getUserIdentifier()); removeCachedServiceListLocked(user.getUserIdentifier());
} }
} }
@@ -386,21 +386,58 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
synchronized (mLock) { synchronized (mLock) {
final S oldService = peekServiceForUserLocked(userId); final S oldService = peekServiceForUserLocked(userId);
if (oldService != null) { if (oldService != null) {
oldService.removeSelfFromCacheLocked(); oldService.removeSelfFromCache();
} }
mServiceNameResolver.setTemporaryService(userId, componentName, durationMs); 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. * Sets whether the default service should be used.
* *
* <p>Typically used during CTS tests to make sure only the default service doesn't interfere * <p>Typically used during CTS tests to make sure only the default service doesn't interfere
* with the test results. * with the test results.
* *
* @throws SecurityException if caller is not allowed to manage this service's settings.
*
* @return whether the enabled state changed. * @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) { public final boolean setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
Slog.i(mTag, "setDefaultServiceEnabled() for userId " + userId + ": " + 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); final S oldService = peekServiceForUserLocked(userId);
if (oldService != null) { if (oldService != null) {
oldService.removeSelfFromCacheLocked(); oldService.removeSelfFromCache();
} }
// Must update the service on cache so its initialization code is triggered // Must update the service on cache so its initialization code is triggered
@@ -500,6 +537,21 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@Nullable @Nullable
protected abstract S newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled); 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 * Register the service for extra Settings changes (i.e., other than
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or * {@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 * <p><b>NOTE: </p>it doesn't need to register for
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or * {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
* {@link #getServiceSettingsProperty()}. * {@link #getServiceSettingsProperty()}.
*
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver, protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
@@ -539,18 +590,38 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@GuardedBy("mLock") @GuardedBy("mLock")
@NonNull @NonNull
protected S getServiceForUserLocked(@UserIdInt int userId) { 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(), final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null); Binder.getCallingUid(), userId, false, false, null, null);
S service = mServicesCache.get(resolvedUserId); List<S> services = mServicesCacheList.get(resolvedUserId);
if (service == null) { if (services == null || services.size() == 0) {
final boolean disabled = isDisabledLocked(userId); final boolean disabled = isDisabledLocked(userId);
service = newServiceLocked(resolvedUserId, disabled); if (mServiceNameResolver == null) {
return null;
}
if (mServiceNameResolver.isConfiguredInMultipleMode()) {
services = newServiceListLocked(resolvedUserId, disabled,
mServiceNameResolver.getServiceNameList(userId));
} else {
services = new ArrayList<>();
services.add(newServiceLocked(resolvedUserId, disabled));
}
if (!disabled) { if (!disabled) {
onServiceEnabledLocked(service, resolvedUserId); for (int i = 0; i < services.size(); i++) {
onServiceEnabledLocked(services.get(i), resolvedUserId);
} }
mServicesCache.put(userId, service);
} }
return service; mServicesCacheList.put(userId, services);
}
return services;
} }
/** /**
@@ -560,9 +631,20 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@GuardedBy("mLock") @GuardedBy("mLock")
@Nullable @Nullable
protected S peekServiceForUserLocked(@UserIdInt int userId) { 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(), final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null); Binder.getCallingUid(), userId, false, false, null, null);
return mServicesCache.get(resolvedUserId); return mServicesCacheList.get(resolvedUserId);
} }
/** /**
@@ -570,15 +652,16 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
*/ */
@GuardedBy("mLock") @GuardedBy("mLock")
protected void updateCachedServiceLocked(@UserIdInt int userId) { 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 * Checks whether the service is disabled (through {@link UserManager} restrictions) for the
* given user. * given user.
*/ */
@GuardedBy("mLock")
protected boolean isDisabledLocked(@UserIdInt int userId) { protected boolean isDisabledLocked(@UserIdInt int userId) {
return mDisabledByUserRestriction == null ? false : mDisabledByUserRestriction.get(userId); return mDisabledByUserRestriction != null && mDisabledByUserRestriction.get(userId);
} }
/** /**
@@ -591,15 +674,37 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@GuardedBy("mLock") @GuardedBy("mLock")
protected S updateCachedServiceLocked(@UserIdInt int userId, boolean disabled) { protected S updateCachedServiceLocked(@UserIdInt int userId, boolean disabled) {
final S service = getServiceForUserLocked(userId); final S service = getServiceForUserLocked(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) { if (service != null) {
synchronized (service.mLock) {
service.updateLocked(disabled); service.updateLocked(disabled);
if (!service.isEnabledLocked()) { if (!service.isEnabledLocked()) {
removeCachedServiceLocked(userId); removeCachedServiceListLocked(userId);
} else { } else {
onServiceEnabledLocked(service, userId); 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. * <p>By default doesn't do anything, but can be overridden by subclasses.
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
@GuardedBy("mLock")
protected void onServiceEnabledLocked(@NonNull S service, @UserIdInt int userId) { 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. * @return the removed service.
*/ */
@GuardedBy("mLock") @GuardedBy("mLock")
@NonNull @NonNull
protected final S removeCachedServiceLocked(@UserIdInt int userId) { protected final List<S> removeCachedServiceListLocked(@UserIdInt int userId) {
final S service = peekServiceForUserLocked(userId); final List<S> services = peekServiceListForUserLocked(userId);
if (service != null) { if (services != null) {
mServicesCache.delete(userId); mServicesCacheList.delete(userId);
onServiceRemoved(service, 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. * Called before the package that provides the service for the given user is being updated.
*/ */
@GuardedBy("mLock")
protected void onServicePackageUpdatingLocked(@UserIdInt int userId) { protected void onServicePackageUpdatingLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageUpdatingLocked(" + 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. * Called after the package that provides the service for the given user is being updated.
*/ */
@GuardedBy("mLock")
protected void onServicePackageUpdatedLocked(@UserIdInt int userId) { protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageUpdated(" + 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. * Called after the package data that provides the service for the given user is cleared.
*/ */
@GuardedBy("mLock")
protected void onServicePackageDataClearedLocked(@UserIdInt int userId) { protected void onServicePackageDataClearedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageDataCleared(" + 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. * Called after the package that provides the service for the given user is restarted.
*/ */
@GuardedBy("mLock")
protected void onServicePackageRestartedLocked(@UserIdInt int userId) { protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageRestarted(" + userId + ")"); if (verbose) Slog.v(mTag, "onServicePackageRestarted(" + userId + ")");
} }
@@ -686,7 +798,24 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
protected void onServiceNameChanged(@UserIdInt int userId, @Nullable String serviceName, protected void onServiceNameChanged(@UserIdInt int userId, @Nullable String serviceName,
boolean isTemporary) { boolean isTemporary) {
synchronized (mLock) { 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") @GuardedBy("mLock")
protected void visitServicesLocked(@NonNull Visitor<S> visitor) { protected void visitServicesLocked(@NonNull Visitor<S> visitor) {
final int size = mServicesCache.size(); final int size = mServicesCacheList.size();
for (int i = 0; i < size; i++) { 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") @GuardedBy("mLock")
protected void clearCacheLocked() { protected void clearCacheLocked() {
mServicesCache.clear(); mServicesCacheList.clear();
} }
/** /**
@@ -757,6 +889,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
} }
// TODO(b/117779333): support proto // TODO(b/117779333): support proto
@GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) { protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
boolean realDebug = debug; boolean realDebug = debug;
boolean realVerbose = verbose; boolean realVerbose = verbose;
@@ -765,40 +898,64 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
try { try {
// Temporarily turn on full logging; // Temporarily turn on full logging;
debug = verbose = true; debug = verbose = true;
final int size = mServicesCache.size(); final int size = mServicesCacheList.size();
pw.print(prefix); pw.print("Debug: "); pw.print(realDebug); pw.print(prefix);
pw.print(" Verbose: "); pw.println(realVerbose); pw.print("Debug: ");
pw.print("Package policy flags: "); pw.println(mServicePackagePolicyFlags); pw.print(realDebug);
pw.print(" Verbose: ");
pw.println(realVerbose);
pw.print("Package policy flags: ");
pw.println(mServicePackagePolicyFlags);
if (mUpdatingPackageNames != null) { if (mUpdatingPackageNames != null) {
pw.print("Packages being updated: "); pw.println(mUpdatingPackageNames); pw.print("Packages being updated: ");
pw.println(mUpdatingPackageNames);
} }
dumpSupportedUsers(pw, prefix); dumpSupportedUsers(pw, prefix);
if (mServiceNameResolver != null) { if (mServiceNameResolver != null) {
pw.print(prefix); pw.print("Name resolver: "); pw.print(prefix);
mServiceNameResolver.dumpShort(pw); pw.println(); pw.print("Name resolver: ");
mServiceNameResolver.dumpShort(pw);
pw.println();
final List<UserInfo> users = getSupportedUsers(); final List<UserInfo> users = getSupportedUsers();
for (int i = 0; i < users.size(); i++) { for (int i = 0; i < users.size(); i++) {
final int userId = users.get(i).id; final int userId = users.get(i).id;
pw.print(prefix2); pw.print(userId); pw.print(": "); pw.print(prefix2);
mServiceNameResolver.dumpShort(pw, userId); pw.println(); 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.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(); final String settingsProperty = getServiceSettingsProperty();
if (settingsProperty != null) { 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) { if (size == 0) {
pw.println("none"); pw.println("none");
} else { } else {
pw.println(size); pw.println(size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
pw.print(prefix); pw.print("Service at "); pw.print(i); pw.println(": "); pw.print(prefix);
final S service = mServicesCache.valueAt(i); 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); service.dumpLocked(prefix2, pw);
}
}
pw.println(); pw.println();
} }
} }
@@ -820,7 +977,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
final int userId = getChangingUserId(); final int userId = getChangingUserId();
synchronized (mLock) { synchronized (mLock) {
if (mUpdatingPackageNames == null) { if (mUpdatingPackageNames == null) {
mUpdatingPackageNames = new SparseArray<String>(mServicesCache.size()); mUpdatingPackageNames = new SparseArray<String>(mServicesCacheList.size());
} }
mUpdatingPackageNames.put(userId, packageName); mUpdatingPackageNames.put(userId, packageName);
onServicePackageUpdatingLocked(userId); onServicePackageUpdatingLocked(userId);
@@ -835,7 +992,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
+ " because package " + activePackageName + " because package " + activePackageName
+ " is being updated"); + " is being updated");
} }
removeCachedServiceLocked(userId); removeCachedServiceListLocked(userId);
if ((mServicePackagePolicyFlags & PACKAGE_UPDATE_POLICY_REFRESH_EAGER) if ((mServicePackagePolicyFlags & PACKAGE_UPDATE_POLICY_REFRESH_EAGER)
!= 0) { != 0) {
@@ -901,7 +1058,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) { if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
handleActiveServiceRestartedLocked(activePackageName, userId); handleActiveServiceRestartedLocked(activePackageName, userId);
} else { } else {
removeCachedServiceLocked(userId); removeCachedServiceListLocked(userId);
} }
} else { } else {
handlePackageUpdateLocked(pkg); handlePackageUpdateLocked(pkg);
@@ -930,7 +1087,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
private void handleActiveServiceRemoved(@UserIdInt int userId) { private void handleActiveServiceRemoved(@UserIdInt int userId) {
synchronized (mLock) { synchronized (mLock) {
removeCachedServiceLocked(userId); removeCachedServiceListLocked(userId);
} }
final String serviceSettingsProperty = getServiceSettingsProperty(); final String serviceSettingsProperty = getServiceSettingsProperty();
if (serviceSettingsProperty != null) { if (serviceSettingsProperty != null) {
@@ -939,6 +1096,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
} }
} }
@GuardedBy("mLock")
private void handleActiveServiceRestartedLocked(String activePackageName, private void handleActiveServiceRestartedLocked(String activePackageName,
@UserIdInt int userId) { @UserIdInt int userId) {
if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_NO_REFRESH) != 0) { if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_NO_REFRESH) != 0) {
@@ -952,7 +1110,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
+ " because package " + activePackageName + " because package " + activePackageName
+ " is being restarted"); + " is being restarted");
} }
removeCachedServiceLocked(userId); removeCachedServiceListLocked(userId);
if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_REFRESH_EAGER) != 0) { if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_REFRESH_EAGER) != 0) {
if (debug) { if (debug) {
@@ -966,6 +1124,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@Override @Override
public void onPackageModified(String packageName) { public void onPackageModified(String packageName) {
synchronized (mLock) {
if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName); if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
if (mServiceNameResolver == null) { if (mServiceNameResolver == null) {
@@ -973,7 +1132,19 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
} }
final int userId = getChangingUserId(); final int userId = getChangingUserId();
final String serviceName = mServiceNameResolver.getDefaultServiceName(userId); final String[] serviceNames = mServiceNameResolver.getDefaultServiceNameList(
userId);
if (serviceNames != null) {
for (int i = 0; i < serviceNames.length; i++) {
peekAndUpdateCachedServiceLocked(packageName, userId, serviceNames[i]);
}
}
}
}
@GuardedBy("mLock")
private void peekAndUpdateCachedServiceLocked(String packageName, int userId,
String serviceName) {
if (serviceName == null) { if (serviceName == null) {
return; return;
} }
@@ -997,6 +1168,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
} }
} }
@GuardedBy("mLock")
private String getActiveServicePackageNameLocked() { private String getActiveServicePackageNameLocked() {
final int userId = getChangingUserId(); final int userId = getChangingUserId();
final S service = peekServiceForUserLocked(userId); final S service = peekServiceForUserLocked(userId);

View File

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

View File

@@ -15,6 +15,7 @@
*/ */
package com.android.server.infra; package com.android.server.infra;
import android.annotation.ArrayRes;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.StringRes; import android.annotation.StringRes;
@@ -33,6 +34,7 @@ import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Arrays;
/** /**
* Gets the service name using a framework resources, temporarily changing the service if necessary * 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)} */ /** Handler message to {@link #resetTemporaryService(int)} */
private static final int MSG_RESET_TEMPORARY_SERVICE = 0; private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
private final @NonNull Context mContext; @NonNull private final Context mContext;
private final @NonNull Object mLock = new Object(); @NonNull private final Object mLock = new Object();
private final @StringRes int mResourceId; @StringRes private final int mStringResourceId;
private @Nullable NameResolverListener mOnSetCallback; @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}. * 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") @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 * Map of default services that have been disabled by
* {@link #setDefaultServiceEnabled(int, boolean)},keyed by {@code userId}. * {@link #setDefaultServiceEnabled(int, boolean)},keyed by {@code userId}.
@@ -69,7 +71,7 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
*/ */
@GuardedBy("mLock") @GuardedBy("mLock")
private final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray(); private final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray();
@Nullable private NameResolverListener mOnSetCallback;
/** /**
* When the temporary service will expire (and reset back to the default). * 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, public FrameworkResourcesServiceNameResolver(@NonNull Context context,
@StringRes int resourceId) { @StringRes int resourceId) {
mContext = context; 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 @Override
@@ -96,22 +113,31 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
} }
@Override @Override
public String getDefaultServiceName(@UserIdInt int userId) { public String getServiceName(@UserIdInt int userId) {
synchronized (mLock) { String[] serviceNames = getServiceNameList(userId);
final String name = mContext.getString(mResourceId); return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0];
return TextUtils.isEmpty(name) ? null : name;
}
} }
@Override @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) { synchronized (mLock) {
final String temporaryName = mTemporaryServiceNames.get(userId); String[] temporaryNames = mTemporaryServiceNamesList.get(userId);
if (temporaryName != null) { if (temporaryNames != null) {
// Always log it, as it should only be used on CTS or during development // Always log it, as it should only be used on CTS or during development
Slog.w(TAG, "getServiceName(): using temporary name " + temporaryName Slog.w(TAG, "getServiceName(): using temporary name "
+ " for user " + userId); + Arrays.toString(temporaryNames) + " for user " + userId);
return temporaryName; return temporaryNames;
} }
final boolean disabled = mDefaultServicesDisabled.get(userId); final boolean disabled = mDefaultServicesDisabled.get(userId);
if (disabled) { if (disabled) {
@@ -120,22 +146,50 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
+ "user " + userId); + "user " + userId);
return null; 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 @Override
public boolean isTemporary(@UserIdInt int userId) { public boolean isTemporary(@UserIdInt int userId) {
synchronized (mLock) { synchronized (mLock) {
return mTemporaryServiceNames.get(userId) != null; return mTemporaryServiceNamesList.get(userId) != null;
} }
} }
@Override @Override
public void setTemporaryService(@UserIdInt int userId, @NonNull String componentName, public void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
int durationMs) { int durationMs) {
setTemporaryServices(userId, new String[]{componentName}, durationMs);
}
@Override
public void setTemporaryServices(int userId, @NonNull String[] componentNames, int durationMs) {
synchronized (mLock) { synchronized (mLock) {
mTemporaryServiceNames.put(userId, componentName); mTemporaryServiceNamesList.put(userId, componentNames);
if (mTemporaryHandler == null) { if (mTemporaryHandler == null) {
mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) { mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
@@ -155,17 +209,19 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
} }
mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs; mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs); mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
notifyTemporaryServiceNameChangedLocked(userId, componentName, for (int i = 0; i < componentNames.length; i++) {
notifyTemporaryServiceNameChangedLocked(userId, componentNames[i],
/* isTemporary= */ true); /* isTemporary= */ true);
} }
} }
}
@Override @Override
public void resetTemporaryService(@UserIdInt int userId) { public void resetTemporaryService(@UserIdInt int userId) {
synchronized (mLock) { synchronized (mLock) {
Slog.i(TAG, "resetting temporary service for user " + userId + " from " Slog.i(TAG, "resetting temporary service for user " + userId + " from "
+ mTemporaryServiceNames.get(userId)); + Arrays.toString(mTemporaryServiceNamesList.get(userId)));
mTemporaryServiceNames.remove(userId); mTemporaryServiceNamesList.remove(userId);
if (mTemporaryHandler != null) { if (mTemporaryHandler != null) {
mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE); mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
mTemporaryHandler = null; mTemporaryHandler = null;
@@ -207,16 +263,21 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
@Override @Override
public String toString() { public String toString() {
return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNames + "]"; synchronized (mLock) {
return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNamesList + "]";
}
} }
// TODO(b/117779333): support proto // TODO(b/117779333): support proto
@Override @Override
public void dumpShort(@NonNull PrintWriter pw) { public void dumpShort(@NonNull PrintWriter pw) {
synchronized (mLock) { synchronized (mLock) {
pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId); pw.print("FrameworkResourcesServiceNamer: resId=");
pw.print(", numberTemps="); pw.print(mTemporaryServiceNames.size()); pw.print(mStringResourceId);
pw.print(", enabledDefaults="); pw.print(mDefaultServicesDisabled.size()); 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 @Override
public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) { public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
synchronized (mLock) { synchronized (mLock) {
final String temporaryName = mTemporaryServiceNames.get(userId); final String[] temporaryNames = mTemporaryServiceNamesList.get(userId);
if (temporaryName != null) { if (temporaryNames != null) {
pw.print("tmpName="); pw.print(temporaryName); pw.print("tmpName=");
pw.print(Arrays.toString(temporaryNames));
final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime(); 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); final boolean disabled = mDefaultServicesDisabled.get(userId);
pw.println(disabled ? " (disabled)" : " (enabled)"); pw.println(disabled ? " (disabled)" : " (enabled)");
} }

View File

@@ -34,7 +34,7 @@ public interface ServiceNameResolver {
/** /**
* Listener for name changes. * Listener for name changes.
*/ */
public interface NameResolverListener { interface NameResolverListener {
/** /**
* The name change callback. * The name change callback.
@@ -63,6 +63,30 @@ public interface ServiceNameResolver {
@Nullable @Nullable
String getDefaultServiceName(@UserIdInt int userId); 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 * Gets the current name of the service for the given user
* *
@@ -75,6 +99,18 @@ public interface ServiceNameResolver {
return getDefaultServiceName(userId); 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. * Checks whether the current service is temporary for the given user.
*/ */
@@ -87,9 +123,9 @@ public interface ServiceNameResolver {
* *
* @param userId user handle * @param userId user handle
* @param componentName name of the new component * @param componentName name of the new component
* @param durationMs how long the change will be valid (the service will be automatically reset * @param durationMs how long the change will be valid (the service will be automatically
* reset
* to the default component after this timeout expires). * to the default component after this timeout expires).
*
* @throws UnsupportedOperationException if not implemented. * @throws UnsupportedOperationException if not implemented.
*/ */
default void setTemporaryService(@UserIdInt int userId, @NonNull String componentName, default void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
@@ -97,11 +133,25 @@ public interface ServiceNameResolver {
throw new UnsupportedOperationException("temporary user not supported"); 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. * Resets the temporary service implementation to the default component for the given user.
* *
* @param userId user handle * @param userId user handle
*
* @throws UnsupportedOperationException if not implemented. * @throws UnsupportedOperationException if not implemented.
*/ */
default void resetTemporaryService(@UserIdInt int userId) { default void resetTemporaryService(@UserIdInt int userId) {
@@ -116,9 +166,9 @@ public interface ServiceNameResolver {
* *
* @param userId user handle * @param userId user handle
* @param enabled whether the default service should be used when the temporary service is not * @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 * set. If the service enabled state is already that value, the command is
* ignored and this
* method return {@code false}. * method return {@code false}.
*
* @return whether the enabled state changed. * @return whether the enabled state changed.
* @throws UnsupportedOperationException if not implemented. * @throws UnsupportedOperationException if not implemented.
*/ */
@@ -133,7 +183,6 @@ public interface ServiceNameResolver {
* with the test results. * with the test results.
* *
* @param userId user handle * @param userId user handle
*
* @throws UnsupportedOperationException if not implemented. * @throws UnsupportedOperationException if not implemented.
*/ */
default boolean isDefaultServiceEnabled(@UserIdInt int userId) { default boolean isDefaultServiceEnabled(@UserIdInt int userId) {