diff options
10 files changed, 123 insertions, 17 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e596e7c2ab06..e50432eaa299 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -236,12 +236,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -final class RemoteServiceException extends AndroidRuntimeException { - public RemoteServiceException(String msg) { - super(msg); - } -} - /** * This manages the execution of the main thread in an * application process, scheduling and executing activities, @@ -1274,8 +1268,9 @@ public final class ActivityThread extends ClientTransactionHandler sendMessage(H.DISPATCH_PACKAGE_BROADCAST, packages, cmd); } - public void scheduleCrash(String msg) { - sendMessage(H.SCHEDULE_CRASH, msg); + @Override + public void scheduleCrash(String msg, int typeId) { + sendMessage(H.SCHEDULE_CRASH, msg, typeId); } public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken, @@ -1892,6 +1887,17 @@ public final class ActivityThread extends ClientTransactionHandler } } + private void throwRemoteServiceException(String message, int typeId) { + // Use a switch to ensure all the type IDs are unique. + switch (typeId) { + case ForegroundServiceDidNotStartInTimeException.TYPE_ID: // 1 + throw new ForegroundServiceDidNotStartInTimeException(message); + case RemoteServiceException.TYPE_ID: // 0 + default: + throw new RemoteServiceException(message); + } + } + class H extends Handler { public static final int BIND_APPLICATION = 110; @UnsupportedAppUsage @@ -2105,7 +2111,8 @@ public final class ActivityThread extends ClientTransactionHandler Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case SCHEDULE_CRASH: - throw new RemoteServiceException((String)msg.obj); + throwRemoteServiceException((String) msg.obj, msg.arg1); + break; case DUMP_HEAP: handleDumpHeap((DumpHeapData) msg.obj); break; diff --git a/core/java/android/app/ForegroundServiceDidNotStartInTimeException.java b/core/java/android/app/ForegroundServiceDidNotStartInTimeException.java new file mode 100644 index 000000000000..364291e69ad6 --- /dev/null +++ b/core/java/android/app/ForegroundServiceDidNotStartInTimeException.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +/** + * Exception used to crash an app process when it didn't call {@link Service#startForeground} + * in time after the service was started with + * {@link android.content.Context#startForegroundService}. + * + * @hide + */ +public class ForegroundServiceDidNotStartInTimeException extends RemoteServiceException { + /** The type ID passed to {@link IApplicationThread#scheduleCrash}. */ + public static final int TYPE_ID = 1; + + public ForegroundServiceDidNotStartInTimeException(String msg) { + super(msg); + } +} diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 1b8eb8add791..f9279da172a0 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -321,6 +321,8 @@ interface IActivityManager { boolean isTopActivityImmersive(); void crashApplication(int uid, int initialPid, in String packageName, int userId, in String message, boolean force); + void crashApplicationWithType(int uid, int initialPid, in String packageName, int userId, + in String message, boolean force, int exceptionTypeId); /** @deprecated -- use getProviderMimeTypeAsync */ @UnsupportedAppUsage(maxTargetSdk = 29, publicAlternatives = "Use {@link android.content.ContentResolver#getType} public API instead.") diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index b5294d5b6f4e..78e7ce8c594e 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -106,7 +106,7 @@ oneway interface IApplicationThread { void scheduleOnNewActivityOptions(IBinder token, in Bundle options); void scheduleSuicide(); void dispatchPackageBroadcast(int cmd, in String[] packages); - void scheduleCrash(in String msg); + void scheduleCrash(in String msg, int typeId); void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, in String path, in ParcelFileDescriptor fd, in RemoteCallback finishCallback); void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix, diff --git a/core/java/android/app/RemoteServiceException.java b/core/java/android/app/RemoteServiceException.java new file mode 100644 index 000000000000..4b32463e2996 --- /dev/null +++ b/core/java/android/app/RemoteServiceException.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.util.AndroidRuntimeException; + +/** + * Exception used by {@link ActivityThread} to crash an app process. + * + * @hide + */ +public class RemoteServiceException extends AndroidRuntimeException { + /** + * The type ID passed to {@link IApplicationThread#scheduleCrash}. + * + * Assign a unique ID to each subclass. See the above method for the numbers that are already + * taken. + */ + public static final int TYPE_ID = 0; + + public RemoteServiceException(String msg) { + super(msg); + } +} diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index f47fa39e7dc8..4dbdc606497e 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -531,7 +531,7 @@ public class TransactionParcelTests { } @Override - public void scheduleCrash(String s) throws RemoteException { + public void scheduleCrash(String s, int i) throws RemoteException { } @Override diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index c1ab6cc89eac..c7f2f43db131 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -78,6 +78,7 @@ import android.app.ActivityManagerInternal; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.ForegroundServiceDidNotStartInTimeException; import android.app.ForegroundServiceStartNotAllowedException; import android.app.IApplicationThread; import android.app.IServiceConnection; @@ -4988,9 +4989,10 @@ public final class ActiveServices { } void serviceForegroundCrash(ProcessRecord app, CharSequence serviceRecord) { - mAm.crashApplication(app.uid, app.getPid(), app.info.packageName, app.userId, + mAm.crashApplicationWithType(app.uid, app.getPid(), app.info.packageName, app.userId, "Context.startForegroundService() did not then call Service.startForeground(): " - + serviceRecord, false /*force*/); + + serviceRecord, false /*force*/, + ForegroundServiceDidNotStartInTimeException.TYPE_ID); } void scheduleServiceTimeoutLocked(ProcessRecord proc) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7dc39b34c88a..ac042a780a36 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -179,6 +179,7 @@ import android.app.PendingIntent; import android.app.ProcessMemoryState; import android.app.ProfilerInfo; import android.app.PropertyInvalidatedCache; +import android.app.RemoteServiceException; import android.app.SyncNotedAppOp; import android.app.WaitResult; import android.app.backup.BackupManager.OperationType; @@ -2935,6 +2936,13 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void crashApplication(int uid, int initialPid, String packageName, int userId, String message, boolean force) { + crashApplicationWithType(uid, initialPid, packageName, userId, message, force, + RemoteServiceException.TYPE_ID); + } + + @Override + public void crashApplicationWithType(int uid, int initialPid, String packageName, int userId, + String message, boolean force, int exceptionTypeId) { if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: crashApplication() from pid=" @@ -2947,7 +2955,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized(this) { mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, userId, - message, force); + message, force, exceptionTypeId); } } diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 3602f44cd785..5a59eabde6f0 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -486,7 +486,7 @@ class AppErrors { * @param message */ void scheduleAppCrashLocked(int uid, int initialPid, String packageName, int userId, - String message, boolean force) { + String message, boolean force, int exceptionTypeId) { ProcessRecord proc = null; // Figure out which process to kill. We don't trust that initialPid @@ -518,7 +518,7 @@ class AppErrors { return; } - proc.scheduleCrashLocked(message); + proc.scheduleCrashLocked(message, exceptionTypeId); if (force) { // If the app is responsive, the scheduled crash will happen as expected // and then the delayed summary kill will be a no-op. diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index ed136afcc592..9e94d4aa2c0f 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -26,6 +26,7 @@ import android.app.ApplicationExitInfo; import android.app.ApplicationExitInfo.Reason; import android.app.ApplicationExitInfo.SubReason; import android.app.IApplicationThread; +import android.app.RemoteServiceException; import android.content.pm.ApplicationInfo; import android.content.pm.ProcessInfo; import android.content.pm.VersionedPackage; @@ -949,6 +950,21 @@ class ProcessRecord implements WindowProcessListener { @GuardedBy("mService") void scheduleCrashLocked(String message) { + scheduleCrashLocked(message, RemoteServiceException.TYPE_ID); + } + + /** + * Let an app process throw an exception on a binder thread, which typically crashes the + * process, unless it has an unhandled exception handler. + * + * See {@link ActivityThread#throwRemoteServiceException}. + * + * @param message exception message + * @param exceptionTypeId ID defined in {@link android.app.RemoteServiceException} or one + * of its subclasses. + */ + @GuardedBy("mService") + void scheduleCrashLocked(String message, int exceptionTypeId) { // Checking killedbyAm should keep it from showing the crash dialog if the process // was already dead for a good / normal reason. if (!mKilledByAm) { @@ -959,7 +975,7 @@ class ProcessRecord implements WindowProcessListener { } final long ident = Binder.clearCallingIdentity(); try { - mThread.scheduleCrash(message); + mThread.scheduleCrash(message, exceptionTypeId); } catch (RemoteException e) { // If it's already dead our work is done. If it's wedged just kill it. // We won't get the crash dialog or the error reporting. |