Add support for spawning multiple service processes

Modify activity manager code to allow for multiple processes to be
spawned for the same service (non-isolated) having different instance
names. A new API is introduced similar to bindService where the app UID
associated with the supplemental process service is used as the instance
name.

The main changes involve distinguishing it from isolated service
processes with instance name, removing a check that allows only isolated
services to have instance names and using distinguishing process names
in the ServiceRecord for the different processes running that service.

Bug: 203670795
Bug: 209058402
Test: Manual, installing two Supplemental client apps and attempting to
load code from each and examining new processes created. Automated tests
are WIP.

Change-Id: I2e349412338e6f0cc9cabaefb77fe2413f5850e0
This commit is contained in:
Sanjana Sunil
2021-12-17 07:32:22 +00:00
committed by Palak Chaudhary
parent 87b22d6d8f
commit e5cc81ce07
7 changed files with 122 additions and 22 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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 <var>userAppUid</var> 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;
}

View File

@@ -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)

View File

@@ -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;