Merge changes from topic "sharedisolated"

* changes:
  Add allowedSharedIsolatedProcess attribute.
  Support shared isolated processes.
This commit is contained in:
Martijn Coenen
2022-12-20 17:34:15 +00:00
committed by Android (Google) Code Review
11 changed files with 218 additions and 43 deletions

View File

@@ -363,6 +363,7 @@ package android {
field public static final int allowGameFpsOverride = 16844378; // 0x101065a
field public static final int allowNativeHeapPointerTagging = 16844306; // 0x1010612
field public static final int allowParallelSyncs = 16843570; // 0x1010332
field public static final int allowSharedIsolatedProcess;
field public static final int allowSingleTap = 16843353; // 0x1010259
field public static final int allowTaskReparenting = 16843268; // 0x1010204
field public static final int allowUndo = 16843999; // 0x10104df
@@ -9957,6 +9958,7 @@ package android.content {
field public static final int BIND_INCLUDE_CAPABILITIES = 4096; // 0x1000
field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
field public static final int BIND_NOT_PERCEPTIBLE = 256; // 0x100
field public static final int BIND_SHARED_ISOLATED_PROCESS = 8192; // 0x2000
field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20
field public static final String BIOMETRIC_SERVICE = "biometric";
field public static final String BLOB_STORE_SERVICE = "blob_store";
@@ -12533,6 +12535,7 @@ package android.content.pm {
method public void dump(android.util.Printer, String);
method public int getForegroundServiceType();
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ServiceInfo> CREATOR;
field public static final int FLAG_ALLOW_SHARED_ISOLATED_PROCESS = 16; // 0x10
field public static final int FLAG_EXTERNAL_SERVICE = 4; // 0x4
field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000

View File

@@ -277,7 +277,8 @@ public abstract class Context {
BIND_IMPORTANT,
BIND_ADJUST_WITH_ACTIVITY,
BIND_NOT_PERCEPTIBLE,
BIND_INCLUDE_CAPABILITIES
BIND_INCLUDE_CAPABILITIES,
BIND_SHARED_ISOLATED_PROCESS
})
@Retention(RetentionPolicy.SOURCE)
public @interface BindServiceFlags {}
@@ -394,6 +395,22 @@ public abstract class Context {
*/
public static final int BIND_INCLUDE_CAPABILITIES = 0x000001000;
/**
* Flag for {@link #bindIsolatedService}: Bind the service into a shared isolated process.
* Specifying this flag allows multiple isolated services to be running in a single shared
* isolated process.
*
* The shared isolated process instance is identified by the <var>instanceName</var>
* parameter in {@link #bindIsolatedService(Intent, int, String, Executor, ServiceConnection)}.
*
* Subsequent calls to {@link #bindIsolatedService} with the same <var>instanceName</var>
* will cause the isolated service to be co-located in the same shared isolated process.
*
* Note that the shared isolated process is scoped to the calling app; once created, only
* the calling app can bind additional isolated services into the shared process.
*/
public static final int BIND_SHARED_ISOLATED_PROCESS = 0x00002000;
/*********** Public flags above this line ***********/
/*********** Hidden flags below this line ***********/

View File

@@ -78,6 +78,20 @@ public class ServiceInfo extends ComponentInfo
*/
public static final int FLAG_USE_APP_ZYGOTE = 0x0008;
/**
* Bit in {@link #flags}: If set, and this is an {@link android.R.attr#isolatedProcess}
* service, the service is allowed to be bound in a shared isolated process with other
* isolated services. Note that these other isolated services can also belong to other
* apps from different vendors.
*
* Shared isolated processes are created when using the
* {@link android.content.Context#BIND_SHARED_ISOLATED_PROCESS) during service binding.
*
* Note that when this flag is used, the {@link android.R.attr#process} attribute is
* ignored when the process is bound into a shared isolated process by a client.
*/
public static final int FLAG_ALLOW_SHARED_ISOLATED_PROCESS = 0x0010;
/**
* Bit in {@link #flags} indicating if the service is visible to ephemeral applications.
* @hide

View File

@@ -2963,6 +2963,18 @@
Context.createAttributionContext() using the first attribution tag
contained here. -->
<attr name="attributionTags" />
<!-- If true, and this is an {@link android.R.attr#isolatedProcess} service, the service
is allowed to be bound in a shared isolated process with other isolated services.
Note that these other isolated services can also belong to other apps from different
vendors.
<p>
Shared isolated processes are created when using the
{@link android.content.Context#BIND_SHARED_ISOLATED_PROCESS) during service binding.
<p>
Note that when this flag is used, the {@link android.R.attr#process} attribute is
ignored when the process is bound into a shared isolated process by a client.
-->
<attr name="allowSharedIsolatedProcess" format="boolean" />
</declare-styleable>
<!-- @hide The <code>apex-system-service</code> tag declares an apex system service

View File

@@ -121,6 +121,7 @@
<public name="visualQueryDetectionService" />
<public name="physicalKeyboardHintLanguageTag" />
<public name="physicalKeyboardHintLayoutType" />
<public name="allowSharedIsolatedProcess" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01cd0000">

View File

@@ -704,6 +704,26 @@ public final class ActiveServices {
}
}
static String getProcessNameForService(ServiceInfo sInfo, ComponentName name,
String callingPackage, String instanceName, boolean isSdkSandbox,
boolean inSharedIsolatedProcess) {
if (isSdkSandbox) {
// For SDK sandbox, the process name is passed in as the instanceName
return instanceName;
}
if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
// For regular processes, just the name in sInfo
return sInfo.processName;
}
// Isolated processes remain.
if (inSharedIsolatedProcess) {
// Shared isolated processes are scoped to the calling package
return callingPackage + ":ishared:" + instanceName;
} else {
return sInfo.processName + ":" + name.getClassName();
}
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage,
@Nullable String callingFeatureId, final int userId)
@@ -736,7 +756,7 @@ public final class ActiveServices {
ServiceLookupResult res =
retrieveServiceLocked(service, null, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false, false);
callingPid, callingUid, userId, true, callerFg, false, false, false);
if (res == null) {
return null;
}
@@ -1338,7 +1358,8 @@ public final class ActiveServices {
// If this service is active, make sure it is stopped.
ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, null,
Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, false, false);
Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, false, false,
false);
if (r != null) {
if (r.record != null) {
final long origId = Binder.clearCallingIdentity();
@@ -1430,7 +1451,7 @@ public final class ActiveServices {
IBinder peekServiceLocked(Intent service, String resolvedType, String callingPackage) {
ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(),
UserHandle.getCallingUserId(), false, false, false, false);
UserHandle.getCallingUserId(), false, false, false, false, false);
IBinder ret = null;
if (r != null) {
@@ -3237,11 +3258,13 @@ public final class ActiveServices {
!= ProcessList.SCHED_GROUP_BACKGROUND;
final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
final boolean inSharedIsolatedProcess = (flags & Context.BIND_SHARED_ISOLATED_PROCESS) != 0;
ServiceLookupResult res = retrieveServiceLocked(service, instanceName,
isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage,
resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg,
isBindExternal, allowInstant, null /* fgsDelegateOptions */);
isBindExternal, allowInstant, null /* fgsDelegateOptions */,
inSharedIsolatedProcess);
if (res == null) {
return 0;
}
@@ -3697,10 +3720,11 @@ public final class ActiveServices {
String instanceName, String resolvedType, String callingPackage,
int callingPid, int callingUid, int userId,
boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
boolean allowInstant) {
return retrieveServiceLocked(service, instanceName, false, 0, null, resolvedType,
boolean allowInstant, boolean inSharedIsolatedProcess) {
return retrieveServiceLocked(service, instanceName, false, INVALID_UID, null, resolvedType,
callingPackage, callingPid, callingUid, userId, createIfNeeded, callingFromFg,
isBindExternal, allowInstant, null /* fgsDelegateOptions */);
isBindExternal, allowInstant, null /* fgsDelegateOptions */,
inSharedIsolatedProcess);
}
private ServiceLookupResult retrieveServiceLocked(Intent service,
@@ -3708,7 +3732,8 @@ public final class ActiveServices {
String sdkSandboxClientAppPackage, String resolvedType,
String callingPackage, int callingPid, int callingUid, int userId,
boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
boolean allowInstant, ForegroundServiceDelegationOptions fgsDelegateOptions) {
boolean allowInstant, ForegroundServiceDelegationOptions fgsDelegateOptions,
boolean inSharedIsolatedProcess) {
if (isSdkSandboxService && instanceName == null) {
throw new IllegalArgumentException("No instanceName provided for sdk sandbox process");
}
@@ -3803,11 +3828,15 @@ public final class ActiveServices {
final Intent.FilterComparison filter =
new Intent.FilterComparison(service.cloneFilter());
final ServiceRestarter res = new ServiceRestarter();
final String processName = getProcessNameForService(sInfo, cn, callingPackage,
null /* instanceName */, false /* isSdkSandbox */,
false /* inSharedIsolatedProcess */);
r = new ServiceRecord(mAm, cn /* name */, cn /* instanceName */,
sInfo.applicationInfo.packageName, sInfo.applicationInfo.uid, filter, sInfo,
callingFromFg, res, null /* sdkSandboxProcessName */,
callingFromFg, res, processName,
INVALID_UID /* sdkSandboxClientAppUid */,
null /* sdkSandboxClientAppPackage */);
null /* sdkSandboxClientAppPackage */,
false /* inSharedIsolatedProcess */);
res.setService(r);
smap.mServicesByInstanceName.put(cn, r);
smap.mServicesByIntent.put(filter, r);
@@ -3901,6 +3930,21 @@ public final class ActiveServices {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
" is not an externalService");
}
if (inSharedIsolatedProcess) {
if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
throw new SecurityException("BIND_SHARED_ISOLATED_PROCESS failed, "
+ className + " is not an isolatedProcess");
}
if ((sInfo.flags & ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS) == 0) {
throw new SecurityException("BIND_SHARED_ISOLATED_PROCESS failed, "
+ className + " has not set the allowSharedIsolatedProcess "
+ " attribute.");
}
if (instanceName == null) {
throw new IllegalArgumentException("instanceName must be provided for "
+ "binding a service into a shared isolated process.");
}
}
if (userId > 0) {
if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
sInfo.name, sInfo.flags)
@@ -3934,12 +3978,12 @@ public final class ActiveServices {
final Intent.FilterComparison filter
= new Intent.FilterComparison(service.cloneFilter());
final ServiceRestarter res = new ServiceRestarter();
String sdkSandboxProcessName = isSdkSandboxService ? instanceName
: null;
String processName = getProcessNameForService(sInfo, name, callingPackage,
instanceName, false, inSharedIsolatedProcess);
r = new ServiceRecord(mAm, className, name, definingPackageName,
definingUid, filter, sInfo, callingFromFg, res,
sdkSandboxProcessName, sdkSandboxClientAppUid,
sdkSandboxClientAppPackage);
processName, sdkSandboxClientAppUid,
sdkSandboxClientAppPackage, inSharedIsolatedProcess);
res.setService(r);
smap.mServicesByInstanceName.put(name, r);
smap.mServicesByIntent.put(filter, r);
@@ -4728,21 +4772,52 @@ public final class ActiveServices {
}
}
} else {
// If this service runs in an isolated process, then each time
// we call startProcessLocked() we will get a new isolated
// process, starting another process if we are currently waiting
// for a previous process to come up. To deal with this, we store
// in the service any current isolated process it is running in or
// waiting to have come up.
app = r.isolationHostProc;
if (WebViewZygote.isMultiprocessEnabled()
&& r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
hostingRecord = HostingRecord.byWebviewZygote(r.instanceName, r.definingPackageName,
r.definingUid, r.serviceInfo.processName);
}
if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) {
hostingRecord = HostingRecord.byAppZygote(r.instanceName, r.definingPackageName,
r.definingUid, r.serviceInfo.processName);
if (r.inSharedIsolatedProcess) {
app = mAm.mProcessList.getSharedIsolatedProcess(procName, r.appInfo.uid,
r.appInfo.packageName);
if (app != null) {
final IApplicationThread thread = app.getThread();
final int pid = app.getPid();
final UidRecord uidRecord = app.getUidRecord();
if (thread != null) {
try {
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"realStartServiceLocked: " + r.shortInstanceName);
}
realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg,
enqueueOomAdj);
return null;
} catch (TransactionTooLargeException e) {
throw e;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting service " + r.shortInstanceName,
e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
}
} else {
// If this service runs in an isolated process, then each time
// we call startProcessLocked() we will get a new isolated
// process, starting another process if we are currently waiting
// for a previous process to come up. To deal with this, we store
// in the service any current isolated process it is running in or
// waiting to have come up.
app = r.isolationHostProc;
if (WebViewZygote.isMultiprocessEnabled()
&& r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
hostingRecord = HostingRecord.byWebviewZygote(r.instanceName,
r.definingPackageName,
r.definingUid, r.serviceInfo.processName);
}
if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) {
hostingRecord = HostingRecord.byAppZygote(r.instanceName, r.definingPackageName,
r.definingUid, r.serviceInfo.processName);
}
}
}
@@ -7649,7 +7724,7 @@ public final class ActiveServices {
null /* sdkSandboxClientAppPackage */, null /* resolvedType */, callingPackage,
callingPid, callingUid, userId, true /* createIfNeeded */,
false /* callingFromFg */, false /* isBindExternal */, false /* allowInstant */ ,
options);
options, false /* inSharedIsolatedProcess */);
if (res == null || res.record == null) {
Slog.d(TAG,
"startForegroundServiceDelegateLocked retrieveServiceLocked returns null");

View File

@@ -13054,7 +13054,7 @@ public class ActivityManagerService extends IActivityManager.Stub
String resolvedType, IServiceConnection connection, int flags, String instanceName,
String callingPackage, int userId) throws TransactionTooLargeException {
return bindServiceInstance(caller, token, service, resolvedType, connection, flags,
instanceName, false, 0, null, callingPackage, userId);
instanceName, false, INVALID_UID, null, callingPackage, userId);
}
private int bindServiceInstance(IApplicationThread caller, IBinder token, Intent service,

View File

@@ -3004,6 +3004,16 @@ public final class ProcessList {
}
}
ProcessRecord getSharedIsolatedProcess(String processName, int uid, String packageName) {
for (int i = 0, size = mIsolatedProcesses.size(); i < size; i++) {
final ProcessRecord app = mIsolatedProcesses.valueAt(i);
if (app.info.uid == uid && app.info.packageName.equals(packageName)
&& app.processName.equals(processName)) {
return app;
}
}
return null;
}
@Nullable
@GuardedBy("mService")
List<Integer> getIsolatedProcessesLocked(int uid) {

View File

@@ -19,6 +19,7 @@ package com.android.server.am;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.os.PowerExemptionManager.REASON_DENIED;
import static android.os.Process.INVALID_UID;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -118,6 +119,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
boolean fgWaiting; // is a timeout for going foreground already scheduled?
boolean isNotAppComponentUsage; // is service binding not considered component/package usage?
boolean isForeground; // is service currently in foreground mode?
boolean inSharedIsolatedProcess; // is the service in a shared isolated process
int foregroundId; // Notification ID of last foreground req.
Notification foregroundNoti; // Notification record of foreground state.
long fgDisplayTime; // time at which the FGS notification should become visible
@@ -723,6 +725,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
isSdkSandbox = false;
sdkSandboxClientAppUid = 0;
sdkSandboxClientAppPackage = null;
inSharedIsolatedProcess = false;
}
public static ServiceRecord newEmptyInstanceForTest(ActivityManagerService ams) {
@@ -734,14 +737,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
Runnable restarter) {
this(ams, name, instanceName, definingPackageName, definingUid, intent, sInfo, callerIsFg,
restarter, null, 0, null);
restarter, sInfo.processName, INVALID_UID, null, false);
}
ServiceRecord(ActivityManagerService ams, ComponentName name,
ComponentName instanceName, String definingPackageName, int definingUid,
Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
Runnable restarter, String sdkSandboxProcessName, int sdkSandboxClientAppUid,
String sdkSandboxClientAppPackage) {
Runnable restarter, String processName, int sdkSandboxClientAppUid,
String sdkSandboxClientAppPackage, boolean inSharedIsolatedProcess) {
this.ams = ams;
this.name = name;
this.instanceName = instanceName;
@@ -752,16 +755,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
serviceInfo = sInfo;
appInfo = sInfo.applicationInfo;
packageName = sInfo.applicationInfo.packageName;
this.isSdkSandbox = sdkSandboxProcessName != null;
this.isSdkSandbox = sdkSandboxClientAppUid != INVALID_UID;
this.sdkSandboxClientAppUid = sdkSandboxClientAppUid;
this.sdkSandboxClientAppPackage = sdkSandboxClientAppPackage;
if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
processName = sInfo.processName + ":" + instanceName.getClassName();
} else if (sdkSandboxProcessName != null) {
processName = sdkSandboxProcessName;
} else {
processName = sInfo.processName;
}
this.inSharedIsolatedProcess = inSharedIsolatedProcess;
this.processName = processName;
permission = sInfo.permission;
exported = sInfo.exported;
this.restarter = restarter;

View File

@@ -100,6 +100,8 @@ public class ParsedServiceUtils {
R.styleable.AndroidManifestService_externalService, sa)
| flag(ServiceInfo.FLAG_USE_APP_ZYGOTE,
R.styleable.AndroidManifestService_useAppZygote, sa)
| flag(ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS,
R.styleable.AndroidManifestService_allowSharedIsolatedProcess, sa)
| flag(ServiceInfo.FLAG_SINGLE_USER,
R.styleable.AndroidManifestService_singleUser, sa)));

View File

@@ -32,6 +32,7 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import android.app.compat.CompatChanges;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
import android.os.SystemClock;
@@ -40,6 +41,7 @@ import android.util.ArraySet;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -194,6 +196,47 @@ public class ActiveServicesTest {
rd2 + btwn + extra, rd2 + (btwn + extra) * 2});
}
@Test
public void testGetProcessNameForService() throws Exception {
// Regular service
final ServiceInfo regularService = new ServiceInfo();
regularService.processName = "com.foo";
String processName = ActiveServices.getProcessNameForService(regularService, null, null,
null, false, false);
assertEquals("com.foo", processName);
// Isolated service
final ServiceInfo isolatedService = new ServiceInfo();
isolatedService.processName = "com.foo";
isolatedService.flags = ServiceInfo.FLAG_ISOLATED_PROCESS;
final ComponentName component = new ComponentName("com.foo", "barService");
processName = ActiveServices.getProcessNameForService(isolatedService, component,
null, null, false, false);
assertEquals("com.foo:barService", processName);
// Isolated service in shared isolated process
final ServiceInfo isolatedServiceShared1 = new ServiceInfo();
isolatedServiceShared1.flags = ServiceInfo.FLAG_ISOLATED_PROCESS;
final String instanceName = "pool";
final String callingPackage = "com.foo";
final String sharedIsolatedProcessName1 = ActiveServices.getProcessNameForService(
isolatedServiceShared1, null, callingPackage, instanceName, false, true);
assertEquals("com.foo:ishared:pool", sharedIsolatedProcessName1);
// Bind another one in the same isolated process
final ServiceInfo isolatedServiceShared2 = new ServiceInfo(isolatedServiceShared1);
final String sharedIsolatedProcessName2 = ActiveServices.getProcessNameForService(
isolatedServiceShared2, null, callingPackage, instanceName, false, true);
assertEquals(sharedIsolatedProcessName1, sharedIsolatedProcessName2);
// Simulate another app trying to do the bind
final ServiceInfo isolatedServiceShared3 = new ServiceInfo(isolatedServiceShared1);
final String otherCallingPackage = "com.bar";
final String sharedIsolatedProcessName3 = ActiveServices.getProcessNameForService(
isolatedServiceShared3, null, otherCallingPackage, instanceName, false, true);
Assert.assertNotEquals(sharedIsolatedProcessName2, sharedIsolatedProcessName3);
}
private void prepareTestRescheduleServiceRestarts() {
mService = mock(ActivityManagerService.class);
mService.mConstants = mock(ActivityManagerConstants.class);