Add support to start and stop SDK sandbox services
Starting the sandbox service instead of just binding gives us better
control over the sandbox:
* There is no longer a dependency on using BIND_AUTO_CREATE to create a
sandbox process. Sandbox restarts can therefore be disabled if needed.
* The sandbox can be killed by stopping the service instead of using
killUid(). Therefore, the sandbox can be informed when it is about to
die. Killing multiple sandboxes sharing a uid can also be avoided.
Bug: 239562510
Test: atest SdkSandboxLifecycleHostTest
Change-Id: I5c2302a58beb71b08c4d435625e791006bacd3fb
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index c628ec4..d859f3f 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -451,6 +451,15 @@
*/
public static final int SUBREASON_SDK_SANDBOX_DIED = 27;
+ /**
+ * The process was killed because it was an SDK sandbox process that was either not usable or
+ * was no longer being used; this would be set only when the reason is {@link #REASON_OTHER}.
+ *
+ * For internal use only.
+ * @hide
+ */
+ public static final int SUBREASON_SDK_SANDBOX_NOT_NEEDED = 28;
+
// If there is any OEM code which involves additional app kill reasons, it should
// be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000.
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index a203ae2..1c0be68 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1923,6 +1923,7 @@
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
+ // Keep this in sync with ActivityManagerLocal.startSdkSandboxService
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
@@ -1964,6 +1965,7 @@
}
private boolean stopServiceCommon(Intent service, UserHandle user) {
+ // // Keep this in sync with ActivityManagerLocal.stopSdkSandboxService
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
diff --git a/services/api/current.txt b/services/api/current.txt
index 329dbdf..b55166c 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -43,6 +43,8 @@
method @Deprecated public boolean bindSdkSandboxService(@NonNull android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull String, @NonNull String, int) throws android.os.RemoteException;
method public boolean canStartForegroundService(int, int, @NonNull String);
method public void killSdkSandboxClientAppProcess(@NonNull android.os.IBinder);
+ method @Nullable public android.content.ComponentName startSdkSandboxService(@NonNull android.content.Intent, int, @NonNull String, @NonNull String) throws android.os.RemoteException;
+ method public boolean stopSdkSandboxService(@NonNull android.content.Intent, int, @NonNull String, @NonNull String);
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 3a93cb3..f45dc13 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -746,10 +746,13 @@
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage,
- @Nullable String callingFeatureId, final int userId)
+ @Nullable String callingFeatureId, final int userId, boolean isSdkSandboxService,
+ int sdkSandboxClientAppUid, String sdkSandboxClientAppPackage, String instanceName)
throws TransactionTooLargeException {
return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
- callingPackage, callingFeatureId, userId, BackgroundStartPrivileges.NONE);
+ callingPackage, callingFeatureId, userId, BackgroundStartPrivileges.NONE,
+ isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage,
+ instanceName);
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
@@ -757,6 +760,17 @@
String callingPackage, @Nullable String callingFeatureId, final int userId,
BackgroundStartPrivileges backgroundStartPrivileges)
throws TransactionTooLargeException {
+ return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
+ callingPackage, callingFeatureId, userId, backgroundStartPrivileges,
+ false /* isSdkSandboxService */, INVALID_UID, null, null);
+ }
+
+ ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
+ int callingPid, int callingUid, boolean fgRequired,
+ String callingPackage, @Nullable String callingFeatureId, final int userId,
+ BackgroundStartPrivileges backgroundStartPrivileges, boolean isSdkSandboxService,
+ int sdkSandboxClientAppUid, String sdkSandboxClientAppPackage, String instanceName)
+ throws TransactionTooLargeException {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
+ " type=" + resolvedType + " args=" + service.getExtras());
@@ -774,9 +788,9 @@
callerFg = true;
}
- ServiceLookupResult res =
- retrieveServiceLocked(service, null, resolvedType, callingPackage,
- callingPid, callingUid, userId, true, callerFg, false, false, false);
+ ServiceLookupResult res = retrieveServiceLocked(service, instanceName, isSdkSandboxService,
+ sdkSandboxClientAppUid, sdkSandboxClientAppPackage, resolvedType, callingPackage,
+ callingPid, callingUid, userId, true, callerFg, false, false, null, false);
if (res == null) {
return null;
}
@@ -800,16 +814,30 @@
return null;
}
+ // For the SDK sandbox, we start the service on behalf of the client app.
+ final int appUid = isSdkSandboxService ? sdkSandboxClientAppUid : r.appInfo.uid;
+ final String appPackageName =
+ isSdkSandboxService ? sdkSandboxClientAppPackage : r.packageName;
+ int appTargetSdkVersion = r.appInfo.targetSdkVersion;
+ if (isSdkSandboxService) {
+ try {
+ appTargetSdkVersion = AppGlobals.getPackageManager().getApplicationInfo(
+ appPackageName, ActivityManagerService.STOCK_PM_FLAGS,
+ userId).targetSdkVersion;
+ } catch (RemoteException ignored) {
+ }
+ }
+
// If we're starting indirectly (e.g. from PendingIntent), figure out whether
// we're launching into an app in a background state. This keys off of the same
// idleness state tracking as e.g. O+ background service start policy.
- final boolean bgLaunch = !mAm.isUidActiveLOSP(r.appInfo.uid);
+ final boolean bgLaunch = !mAm.isUidActiveLOSP(appUid);
// If the app has strict background restrictions, we treat any bg service
// start analogously to the legacy-app forced-restrictions case, regardless
// of its target SDK version.
boolean forcedStandby = false;
- if (bgLaunch && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
+ if (bgLaunch && appRestrictedAnyInBackground(appUid, appPackageName)) {
if (DEBUG_FOREGROUND_SERVICE) {
Slog.d(TAG, "Forcing bg-only service start only for " + r.shortInstanceName
+ " : bgLaunch=" + bgLaunch + " callerFg=" + callerFg);
@@ -839,7 +867,7 @@
boolean forceSilentAbort = false;
if (fgRequired) {
final int mode = mAm.getAppOpsManager().checkOpNoThrow(
- AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
+ AppOpsManager.OP_START_FOREGROUND, appUid, appPackageName);
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
case AppOpsManager.MODE_DEFAULT:
@@ -861,12 +889,12 @@
}
// If this isn't a direct-to-foreground start, check our ability to kick off an
- // arbitrary service
+ // arbitrary service.
if (forcedStandby || (!r.startRequested && !fgRequired)) {
// Before going further -- if this app is not allowed to start services in the
// background, then at this point we aren't going to let it period.
- final int allowed = mAm.getAppStartModeLOSP(r.appInfo.uid, r.packageName,
- r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
+ final int allowed = mAm.getAppStartModeLOSP(appUid, appPackageName, appTargetSdkVersion,
+ callingPid, false, false, forcedStandby);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
Slog.w(TAG, "Background start not allowed: service "
+ service + " to " + r.shortInstanceName
@@ -890,7 +918,7 @@
}
// This app knows it is in the new model where this operation is not
// allowed, so tell it what has happened.
- UidRecord uidRec = mAm.mProcessList.getUidRecordLOSP(r.appInfo.uid);
+ UidRecord uidRec = mAm.mProcessList.getUidRecordLOSP(appUid);
return new ComponentName("?", "app is in background uid " + uidRec);
}
}
@@ -899,10 +927,10 @@
// an ordinary startService() or a startForegroundService(). Now, only require that
// the app follow through on the startForegroundService() -> startForeground()
// contract if it actually targets O+.
- if (r.appInfo.targetSdkVersion < Build.VERSION_CODES.O && fgRequired) {
+ if (appTargetSdkVersion < Build.VERSION_CODES.O && fgRequired) {
if (DEBUG_BACKGROUND_CHECK || DEBUG_FOREGROUND_SERVICE) {
Slog.i(TAG, "startForegroundService() but host targets "
- + r.appInfo.targetSdkVersion + " - not requiring startForeground()");
+ + appTargetSdkVersion + " - not requiring startForeground()");
}
fgRequired = false;
}
@@ -1376,7 +1404,8 @@
}
int stopServiceLocked(IApplicationThread caller, Intent service,
- String resolvedType, int userId) {
+ String resolvedType, int userId, boolean isSdkSandboxService,
+ int sdkSandboxClientAppUid, String sdkSandboxClientAppPackage, String instanceName) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "stopService: " + service
+ " type=" + resolvedType);
@@ -1389,9 +1418,10 @@
}
// If this service is active, make sure it is stopped.
- ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, null,
+ ServiceLookupResult r = retrieveServiceLocked(service, instanceName, isSdkSandboxService,
+ sdkSandboxClientAppUid, sdkSandboxClientAppPackage, resolvedType, null,
Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, false, false,
- false);
+ null, false);
if (r != null) {
if (r.record != null) {
final long origId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/am/ActivityManagerLocal.java b/services/core/java/com/android/server/am/ActivityManagerLocal.java
index 3f06990..928af3f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerLocal.java
+++ b/services/core/java/com/android/server/am/ActivityManagerLocal.java
@@ -17,8 +17,10 @@
package com.android.server.am;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Context.BindServiceFlags;
import android.content.Context.BindServiceFlagsBits;
@@ -68,9 +70,53 @@
void tempAllowWhileInUsePermissionInFgs(int uid, long durationMs);
/**
- * Binds to a sdk sandbox service, creating it if needed. You can through the arguments
- * here have the system bring up multiple concurrent processes hosting their own instance of
- * that service. The {@code processName} you provide here identifies the different instances.
+ * Requests that an SDK sandbox service be started. If this service is not already running,
+ * it will be instantiated and started (creating a process for it if needed). You can through
+ * the arguments here have the system bring up multiple concurrent processes hosting their own
+ * instance of that service. Each instance is identified by the {@code processName} provided
+ * here.
+ *
+ * @param service Identifies the sdk sandbox process service to connect to. The Intent must
+ * specify an explicit component name. This value cannot be null.
+ * @param clientAppUid Uid of the app for which the sdk sandbox process needs to be spawned.
+ * @param clientAppPackage Package of the app for which the sdk sandbox process needs to
+ * be spawned. This package must belong to the clientAppUid.
+ * @param processName Unique identifier for the service instance. Each unique name here will
+ * result in a different service instance being created. Identifiers must only contain
+ * ASCII letters, digits, underscores, and periods.
+ *
+ * @throws RemoteException If the service could not be started.
+ * @return If the service is being started or is already running, the {@link ComponentName} of
+ * the actual service that was started is returned; else if the service does not exist null is
+ * returned.
+ */
+ @Nullable
+ @SuppressLint("RethrowRemoteException")
+ ComponentName startSdkSandboxService(@NonNull Intent service, int clientAppUid,
+ @NonNull String clientAppPackage, @NonNull String processName)
+ throws RemoteException;
+
+ // TODO(b/269592470): What if the sandbox is stopped while there is an active binding to it?
+ /**
+ * Requests that an SDK sandbox service with a given {@code processName} be stopped.
+ *
+ * @param service Identifies the sdk sandbox process service to connect to. The Intent must
+ * specify an explicit component name. This value cannot be null.
+ * @param clientAppUid Uid of the app for which the sdk sandbox process needs to be stopped.
+ * @param clientAppPackage Package of the app for which the sdk sandbox process needs to
+ * be stopped. This package must belong to the clientAppUid.
+ * @param processName Unique identifier for the service instance. Each unique name here will
+ * result in a different service instance being created. Identifiers must only contain
+ * ASCII letters, digits, underscores, and periods.
+ *
+ * @return If there is a service matching the given Intent that is already running, then it is
+ * stopped and true is returned; else false is returned.
+ */
+ boolean stopSdkSandboxService(@NonNull Intent service, int clientAppUid,
+ @NonNull String clientAppPackage, @NonNull String processName);
+
+ /**
+ * Binds to an SDK sandbox service for a given client application.
*
* @param service Identifies the sdk sandbox process service to connect to. The Intent must
* specify an explicit component name. This value cannot be null.
@@ -90,7 +136,7 @@
* 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.
- * @throws RemoteException If the service could not be brought up.
+ * @throws RemoteException If the service could not be bound to.
* @see Context#bindService(Intent, ServiceConnection, int)
*/
@SuppressLint("RethrowRemoteException")
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6aa49d2..eab5b2c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -215,6 +215,7 @@
import android.app.PendingIntentStats;
import android.app.ProcessMemoryState;
import android.app.ProfilerInfo;
+import android.app.ServiceStartNotAllowedException;
import android.app.SyncNotedAppOp;
import android.app.WaitResult;
import android.app.assist.ActivityId;
@@ -13172,6 +13173,15 @@
String resolvedType, boolean requireForeground, String callingPackage,
String callingFeatureId, int userId)
throws TransactionTooLargeException {
+ return startService(caller, service, resolvedType, requireForeground, callingPackage,
+ callingFeatureId, userId, false /* isSdkSandboxService */, INVALID_UID, null, null);
+ }
+
+ private ComponentName startService(IApplicationThread caller, Intent service,
+ String resolvedType, boolean requireForeground, String callingPackage,
+ String callingFeatureId, int userId, boolean isSdkSandboxService,
+ int sdkSandboxClientAppUid, String sdkSandboxClientAppPackage, String instanceName)
+ throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
enforceAllowedToStartOrBindServiceIfSdkSandbox(service);
// Refuse possible leaked file descriptors
@@ -13183,6 +13193,11 @@
throw new IllegalArgumentException("callingPackage cannot be null");
}
+ if (isSdkSandboxService && instanceName == null) {
+ throw new IllegalArgumentException("No instance name provided for SDK sandbox process");
+ }
+ validateServiceInstanceName(instanceName);
+
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
final int callingPid = Binder.getCallingPid();
@@ -13198,7 +13213,9 @@
synchronized (this) {
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
- requireForeground, callingPackage, callingFeatureId, userId);
+ requireForeground, callingPackage, callingFeatureId, userId,
+ isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage,
+ instanceName);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -13207,9 +13224,26 @@
return res;
}
+ private void validateServiceInstanceName(String instanceName) {
+ // Ensure that instanceName, which is caller provided, does not contain
+ // unusual characters.
+ if (instanceName != null) {
+ if (!instanceName.matches("[a-zA-Z0-9_.]+")) {
+ throw new IllegalArgumentException("Illegal instanceName");
+ }
+ }
+ }
+
@Override
public int stopService(IApplicationThread caller, Intent service,
String resolvedType, int userId) {
+ return stopService(caller, service, resolvedType, userId, false /* isSdkSandboxService */,
+ INVALID_UID, null, null);
+ }
+
+ private int stopService(IApplicationThread caller, Intent service, String resolvedType,
+ int userId, boolean isSdkSandboxService,
+ int sdkSandboxClientAppUid, String sdkSandboxClientAppPackage, String instanceName) {
enforceNotIsolatedCaller("stopService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
@@ -13221,7 +13255,9 @@
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "stopService: " + service);
}
synchronized (this) {
- return mServices.stopServiceLocked(caller, service, resolvedType, userId);
+ return mServices.stopServiceLocked(caller, service, resolvedType, userId,
+ isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage,
+ instanceName);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -13380,17 +13416,7 @@
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) {
- for (int i = 0; i < instanceName.length(); ++i) {
- char c = instanceName.charAt(i);
- if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
- || (c >= '0' && c <= '9') || c == '_' || c == '.')) {
- throw new IllegalArgumentException("Illegal instanceName");
- }
- }
- }
+ validateServiceInstanceName(instanceName);
try {
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
@@ -17290,6 +17316,53 @@
}
@Override
+ public ComponentName startSdkSandboxService(Intent service, int clientAppUid,
+ String clientAppPackage, String processName) throws RemoteException {
+ validateSdkSandboxParams(service, clientAppUid, clientAppPackage, processName);
+ // TODO(b/269598719): Is passing the application thread of the system_server alright?
+ // e.g. the sandbox getting privileged access due to this.
+ ComponentName cn = ActivityManagerService.this.startService(
+ mContext.getIApplicationThread(), service,
+ service.resolveTypeIfNeeded(mContext.getContentResolver()), false,
+ mContext.getOpPackageName(), mContext.getAttributionTag(),
+ UserHandle.getUserId(clientAppUid), true, clientAppUid, clientAppPackage,
+ processName);
+ if (cn != null) {
+ if (cn.getPackageName().equals("!")) {
+ throw new SecurityException(
+ "Not allowed to start service " + service
+ + " without permission " + cn.getClassName());
+ } else if (cn.getPackageName().equals("!!")) {
+ throw new SecurityException(
+ "Unable to start service " + service
+ + ": " + cn.getClassName());
+ } else if (cn.getPackageName().equals("?")) {
+ throw ServiceStartNotAllowedException.newInstance(false,
+ "Not allowed to start service " + service + ": "
+ + cn.getClassName());
+ }
+ }
+
+ return cn;
+ }
+
+ @Override
+ public boolean stopSdkSandboxService(Intent service, int clientAppUid,
+ String clientAppPackage, String processName) {
+ validateSdkSandboxParams(service, clientAppUid, clientAppPackage, processName);
+ int res = ActivityManagerService.this.stopService(
+ mContext.getIApplicationThread(), service,
+ service.resolveTypeIfNeeded(mContext.getContentResolver()),
+ UserHandle.getUserId(clientAppUid), true, clientAppUid, clientAppPackage,
+ processName);
+ if (res < 0) {
+ throw new SecurityException(
+ "Not allowed to stop service " + service);
+ }
+ return res != 0;
+ }
+
+ @Override
public boolean bindSdkSandboxService(Intent service, ServiceConnection conn,
int clientAppUid, IBinder clientApplicationThread, String clientAppPackage,
String processName, int flags)
@@ -17311,27 +17384,10 @@
int clientAppUid, IBinder clientApplicationThread, String clientAppPackage,
String processName, long flags)
throws RemoteException {
- if (service == null) {
- throw new IllegalArgumentException("intent is null");
- }
+ validateSdkSandboxParams(service, clientAppUid, clientAppPackage, processName);
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
- if (clientAppPackage == null) {
- throw new IllegalArgumentException("clientAppPackage is null");
- }
- if (processName == null) {
- throw new IllegalArgumentException("processName is null");
- }
- if (service.getComponent() == null) {
- throw new IllegalArgumentException("service must specify explicit component");
- }
- if (!UserHandle.isApp(clientAppUid)) {
- throw new IllegalArgumentException("uid is not within application range");
- }
- if (mAppOpsService.checkPackage(clientAppUid, clientAppPackage) != MODE_ALLOWED) {
- throw new IllegalArgumentException("uid does not belong to provided package");
- }
Handler handler = mContext.getMainThreadHandler();
IApplicationThread clientApplicationThreadVerified = null;
@@ -17364,6 +17420,28 @@
UserHandle.getUserId(clientAppUid)) != 0;
}
+ private void validateSdkSandboxParams(Intent service, int clientAppUid,
+ String clientAppPackage, String processName) {
+ if (service == null) {
+ throw new IllegalArgumentException("intent is null");
+ }
+ if (clientAppPackage == null) {
+ throw new IllegalArgumentException("clientAppPackage is null");
+ }
+ if (processName == null) {
+ throw new IllegalArgumentException("processName is null");
+ }
+ if (service.getComponent() == null) {
+ throw new IllegalArgumentException("service must specify explicit component");
+ }
+ if (!UserHandle.isApp(clientAppUid)) {
+ throw new IllegalArgumentException("uid is not within application range");
+ }
+ if (mAppOpsService.checkPackage(clientAppUid, clientAppPackage) != MODE_ALLOWED) {
+ throw new IllegalArgumentException("uid does not belong to provided package");
+ }
+ }
+
@Override
public boolean bindSdkSandboxService(Intent service, ServiceConnection conn,
int clientAppUid, String clientAppPackage, String processName, int flags)
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 32db33d..cfe0ee8 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1318,6 +1318,13 @@
// left sitting around after no longer needed.
app.killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true);
+ } else if (app.isSdkSandbox && psr.numberOfRunningServices() <= 0
+ && app.getActiveInstrumentation() == null) {
+ // If this is an SDK sandbox process and there are no services running it, we
+ // aggressively kill the sandbox as we usually don't want to re-use the same
+ // sandbox again.
+ app.killLocked("sandbox not needed", ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_SDK_SANDBOX_NOT_NEEDED, true);
} else {
// Keeping this process, update its uid.
updateAppUidRecLSP(app);