Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72d234ea authored by Martijn Coenen's avatar Martijn Coenen
Browse files

Support shared isolated processes.

This introduces a new flag, Context.BIND_SHARED_ISOLATED_PROCESS,
which allows a single client to bind multiple isolated services into a
single isolated process. This allows the system to save resources when
multiple isolated services can safely be co-located into a single
isolated process.

We reuse the existing instanceName parameter to specify the name of the
isolated process. After the first bind for a client with a particular
instanceName, subsequent service binds with the same flag and
instanceName will result in the service being bound into the already
existing isolated process.

These are scoped to the client app; it is not possible for an app
to bind an isolated service into a shared isolated process that
belongs to another app.

Bug: 243554393
Test: atest ActiveServicesTest
Test: atest android.externalservice.cts.SharedIsolatedServiceTest
Change-Id: I6b88c551082be849f384849955d3a43e0aa707c1
parent 60d7db22
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -9945,6 +9945,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";
+18 −1
Original line number Diff line number Diff line
@@ -276,7 +276,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 {}
@@ -393,6 +394,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 ***********/

+100 −30
Original line number Diff line number Diff line
@@ -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,16 @@ 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 (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 +3973,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,6 +4767,35 @@ public final class ActiveServices {
                }
            }
        } else {
            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
@@ -4737,7 +4805,8 @@ public final class ActiveServices {
                app = r.isolationHostProc;
                if (WebViewZygote.isMultiprocessEnabled()
                        && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
                hostingRecord = HostingRecord.byWebviewZygote(r.instanceName, r.definingPackageName,
                    hostingRecord = HostingRecord.byWebviewZygote(r.instanceName,
                            r.definingPackageName,
                            r.definingUid, r.serviceInfo.processName);
                }
                if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) {
@@ -4745,6 +4814,7 @@ public final class ActiveServices {
                            r.definingUid, r.serviceInfo.processName);
                }
            }
        }

        // Not running -- get it started, and enqueue this service record
        // to be executed when the app comes up.
@@ -7649,7 +7719,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");
+1 −1
Original line number Diff line number Diff line
@@ -13079,7 +13079,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,
+10 −0
Original line number Diff line number Diff line
@@ -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) {
Loading