diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index f3315a8dc089e..8181a74e8f95e 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1997,7 +1997,8 @@ class ContextImpl extends Context { private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, String instanceName, Handler handler, Executor executor, UserHandle user) { - // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser. + // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser and + // ActivityManagerService.LocalService.startAndBindSupplementalProcessService IServiceConnection sd; if (conn == null) { throw new IllegalArgumentException("connection is null"); @@ -2023,10 +2024,10 @@ class ContextImpl extends Context { flags |= BIND_WAIVE_PRIORITY; } service.prepareToLeaveProcess(this); - int res = ActivityManager.getService().bindIsolatedService( - mMainThread.getApplicationThread(), getActivityToken(), service, - service.resolveTypeIfNeeded(getContentResolver()), - sd, flags, instanceName, getOpPackageName(), user.getIdentifier()); + int res = ActivityManager.getService().bindServiceInstance( + mMainThread.getApplicationThread(), getActivityToken(), service, + service.resolveTypeIfNeeded(getContentResolver()), + sd, flags, instanceName, getOpPackageName(), user.getIdentifier()); if (res < 0) { throw new SecurityException( "Not allowed to bind to service " + service); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 82c419affac2b..e4ef12c250aba 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -169,7 +169,7 @@ interface IActivityManager { int bindService(in IApplicationThread caller, in IBinder token, in Intent service, in String resolvedType, in IServiceConnection connection, int flags, in String callingPackage, int userId); - int bindIsolatedService(in IApplicationThread caller, in IBinder token, in Intent service, + int bindServiceInstance(in IApplicationThread caller, in IBinder token, in Intent service, in String resolvedType, in IServiceConnection connection, int flags, in String instanceName, in String callingPackage, int userId); void updateServiceGroup(in IServiceConnection connection, int group, int importance); diff --git a/services/api/current.txt b/services/api/current.txt index 50f00524676cc..dcf7e64479dac 100644 --- a/services/api/current.txt +++ b/services/api/current.txt @@ -39,6 +39,7 @@ package com.android.server.am { public interface ActivityManagerLocal { method public boolean canStartForegroundService(int, int, @NonNull String); + method public boolean startAndBindSupplementalProcessService(@NonNull android.content.Intent, @NonNull android.content.ServiceConnection, int) throws android.os.TransactionTooLargeException; } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index b5c0a67be20fa..9353dd832bbab 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2721,7 +2721,8 @@ public final class ActiveServices { int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, final IServiceConnection connection, int flags, - String instanceName, String callingPackage, final int userId) + String instanceName, boolean isSupplementalProcessService, String callingPackage, + final int userId) throws TransactionTooLargeException { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service + " type=" + resolvedType + " conn=" + connection.asBinder() @@ -2805,10 +2806,9 @@ public final class ActiveServices { final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0; final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0; - ServiceLookupResult res = - retrieveServiceLocked(service, instanceName, resolvedType, callingPackage, - callingPid, callingUid, userId, true, - callerFg, isBindExternal, allowInstant); + ServiceLookupResult res = retrieveServiceLocked(service, instanceName, + isSupplementalProcessService, resolvedType, callingPackage, callingPid, callingUid, + userId, true, callerFg, isBindExternal, allowInstant); if (res == null) { return 0; } @@ -3228,6 +3228,20 @@ public final class ActiveServices { int callingPid, int callingUid, int userId, boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal, boolean allowInstant) { + return retrieveServiceLocked(service, instanceName, false, resolvedType, callingPackage, + callingPid, callingUid, userId, createIfNeeded, callingFromFg, isBindExternal, + allowInstant); + } + + private ServiceLookupResult retrieveServiceLocked(Intent service, + String instanceName, boolean isSupplementalProcessService, String resolvedType, + String callingPackage, int callingPid, int callingUid, int userId, + boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal, + boolean allowInstant) { + if (isSupplementalProcessService && instanceName == null) { + throw new IllegalArgumentException("No instanceName provided for supplemental process"); + } + ServiceRecord r = null; if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "retrieveServiceLocked: " + service + " type=" + resolvedType + " callingUid=" + callingUid); @@ -3249,7 +3263,6 @@ public final class ActiveServices { if (instanceName == null) { comp = service.getComponent(); } else { - // This is for isolated services final ComponentName realComp = service.getComponent(); if (realComp == null) { throw new IllegalArgumentException("Can't use custom instance name '" + instanceName @@ -3304,12 +3317,19 @@ public final class ActiveServices { return null; } if (instanceName != null - && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) { + && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0 + && !isSupplementalProcessService) { throw new IllegalArgumentException("Can't use instance name '" + instanceName - + "' with non-isolated service '" + sInfo.name + "'"); + + "' with non-isolated non-supplemental service '" + sInfo.name + "'"); } - ComponentName className = new ComponentName( - sInfo.applicationInfo.packageName, sInfo.name); + if (isSupplementalProcessService + && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) { + throw new IllegalArgumentException("Service cannot be both supplemental and " + + "isolated"); + } + + ComponentName className = new ComponentName(sInfo.applicationInfo.packageName, + sInfo.name); ComponentName name = comp != null ? comp : className; if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid, name.getPackageName(), sInfo.applicationInfo.uid)) { @@ -3392,7 +3412,8 @@ public final class ActiveServices { = new Intent.FilterComparison(service.cloneFilter()); final ServiceRestarter res = new ServiceRestarter(); r = new ServiceRecord(mAm, className, name, definingPackageName, - definingUid, filter, sInfo, callingFromFg, res); + definingUid, filter, sInfo, callingFromFg, res, + isSupplementalProcessService); res.setService(r); smap.mServicesByInstanceName.put(name, r); smap.mServicesByIntent.put(filter, r); diff --git a/services/core/java/com/android/server/am/ActivityManagerLocal.java b/services/core/java/com/android/server/am/ActivityManagerLocal.java index 9a1bfddcec07a..d9ee7d974864b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerLocal.java +++ b/services/core/java/com/android/server/am/ActivityManagerLocal.java @@ -18,6 +18,9 @@ package com.android.server.am; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.TransactionTooLargeException; /** * Interface for in-process calls into @@ -58,4 +61,24 @@ public interface ActivityManagerLocal { * @hide */ void tempAllowWhileInUsePermissionInFgs(int uid, long durationMs); + + /** + * Starts a supplemental process service and binds to it. You can through the arguments here + * have the system bring up multiple concurrent processes hosting their own instance of that + * service. The userAppUid you provide here identifies the different instances - each + * unique uid is attributed to a supplemental process. + * + * @param service Identifies the supplemental process service to connect to. The Intent must + * specify an explicit component name. This value cannot be null. + * @param conn Receives information as the service is started and stopped. + * This must be a valid ServiceConnection object; it must not be null. + * @param userAppUid Uid of the app for which the supplemental process needs to be spawned. + * @return {@code true} if the system is in the process of bringing up a + * service that your client has permission to bind to; {@code false} + * if the system couldn't find the service or if your client doesn't + * have permission to bind to it. + */ + boolean startAndBindSupplementalProcessService(@NonNull Intent service, + @NonNull ServiceConnection conn, int userAppUid) throws TransactionTooLargeException; + } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 902659c278188..d80af21acd447 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -218,6 +218,7 @@ import android.content.IIntentSender; import android.content.Intent; import android.content.IntentFilter; import android.content.LocusId; +import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ActivityPresentationInfo; import android.content.pm.ApplicationInfo; @@ -12314,13 +12315,25 @@ public class ActivityManagerService extends IActivityManager.Stub public int bindService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException { - return bindIsolatedService(caller, token, service, resolvedType, connection, flags, + return bindServiceInstance(caller, token, service, resolvedType, connection, flags, null, callingPackage, userId); } - public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service, + /** + * Binds to a service with a given instanceName, creating it if it does not already exist. + * If the instanceName field is not supplied, binding to the service occurs as usual. + */ + public int bindServiceInstance(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String instanceName, String callingPackage, int userId) throws TransactionTooLargeException { + return bindServiceInstance(caller, token, service, resolvedType, connection, flags, + instanceName, false, callingPackage, userId); + } + + private int bindServiceInstance(IApplicationThread caller, IBinder token, Intent service, + String resolvedType, IServiceConnection connection, int flags, String instanceName, + boolean isSupplementalProcessService, String callingPackage, int userId) + throws TransactionTooLargeException { enforceNotIsolatedCaller("bindService"); // Refuse possible leaked file descriptors @@ -12332,6 +12345,10 @@ public class ActivityManagerService extends IActivityManager.Stub throw new IllegalArgumentException("callingPackage cannot be null"); } + if (isSupplementalProcessService && instanceName == null) { + throw new IllegalArgumentException("No instance name provided for isolated process"); + } + // Ensure that instanceName, which is caller provided, does not contain // unusual characters. if (instanceName != null) { @@ -12345,8 +12362,8 @@ public class ActivityManagerService extends IActivityManager.Stub } synchronized(this) { - return mServices.bindServiceLocked(caller, token, service, - resolvedType, connection, flags, instanceName, callingPackage, userId); + return mServices.bindServiceLocked(caller, token, service, resolvedType, connection, + flags, instanceName, isSupplementalProcessService, callingPackage, userId); } } @@ -15822,6 +15839,34 @@ public class ActivityManagerService extends IActivityManager.Stub processName, abiOverride, uid, crashHandler); } + @Override + public boolean startAndBindSupplementalProcessService(Intent service, + ServiceConnection conn, int userAppUid) throws TransactionTooLargeException { + if (service == null) { + throw new IllegalArgumentException("intent is null"); + } + if (conn == null) { + throw new IllegalArgumentException("connection is null"); + } + if (service.getComponent() == null) { + throw new IllegalArgumentException("service must specify explicit component"); + } + if (!UserHandle.isApp(userAppUid)) { + throw new IllegalArgumentException("uid is not within application range"); + } + + Handler handler = mContext.getMainThreadHandler(); + int flags = Context.BIND_AUTO_CREATE; + + final IServiceConnection sd = mContext.getServiceDispatcher(conn, handler, flags); + service.prepareToLeaveProcess(mContext); + return ActivityManagerService.this.bindServiceInstance( + mContext.getIApplicationThread(), mContext.getActivityToken(), service, + service.resolveTypeIfNeeded(mContext.getContentResolver()), sd, flags, + Integer.toString(userAppUid), /*isSupplementalProcessService*/ true, + mContext.getOpPackageName(), UserHandle.getUserId(userAppUid)) != 0; + } + @Override public void onUserRemoved(@UserIdInt int userId) { // Clean up any ActivityTaskManager state (by telling it the user is stopped) diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 24e7ba4a32b96..da78e2d7504ef 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -570,6 +570,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN ComponentName instanceName, String definingPackageName, int definingUid, Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg, Runnable restarter) { + this(ams, name, instanceName, definingPackageName, definingUid, intent, sInfo, callerIsFg, + restarter, false); + } + + ServiceRecord(ActivityManagerService ams, ComponentName name, + ComponentName instanceName, String definingPackageName, int definingUid, + Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg, + Runnable restarter, boolean isSupplementalProcessService) { this.ams = ams; this.name = name; this.instanceName = instanceName; @@ -580,7 +588,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN serviceInfo = sInfo; appInfo = sInfo.applicationInfo; packageName = sInfo.applicationInfo.packageName; - if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) { + if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0 + || isSupplementalProcessService) { processName = sInfo.processName + ":" + instanceName.getClassName(); } else { processName = sInfo.processName;