diff options
5 files changed, 129 insertions, 12 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ad15ea90c45c..3136cb6cea35 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4429,8 +4429,16 @@ public class ActivityManagerService extends IActivityManager.Stub final boolean clearPendingIntentsForStoppedApp = (android.content.pm.Flags.stayStopped() && packageStateStopped); if (packageName == null || uninstalling || clearPendingIntentsForStoppedApp) { + final int cancelReason; + if (packageName == null) { + cancelReason = PendingIntentRecord.CANCEL_REASON_USER_STOPPED; + } else if (uninstalling) { + cancelReason = PendingIntentRecord.CANCEL_REASON_OWNER_UNINSTALLED; + } else { + cancelReason = PendingIntentRecord.CANCEL_REASON_OWNER_FORCE_STOPPED; + } didSomething |= mPendingIntentController.removePendingIntentsForPackage( - packageName, userId, appId, doit); + packageName, userId, appId, doit, cancelReason); } if (doit) { diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java index fb0d6957129a..f3361203c44a 100644 --- a/services/core/java/com/android/server/am/PendingIntentController.java +++ b/services/core/java/com/android/server/am/PendingIntentController.java @@ -22,6 +22,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_OWNER_CANCELED; +import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_SUPERSEDED; import android.annotation.Nullable; import android.app.Activity; @@ -54,6 +56,7 @@ import com.android.internal.util.RingBuffer; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.AlarmManagerInternal; import com.android.server.LocalServices; +import com.android.server.am.PendingIntentRecord.CancellationReason; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.SafeActivityOptions; @@ -191,7 +194,7 @@ public class PendingIntentController { } return rec; } - makeIntentSenderCanceled(rec); + makeIntentSenderCanceled(rec, CANCEL_REASON_SUPERSEDED); mIntentSenderRecords.remove(key); decrementUidStatLocked(rec); } @@ -206,7 +209,7 @@ public class PendingIntentController { } boolean removePendingIntentsForPackage(String packageName, int userId, int appId, - boolean doIt) { + boolean doIt, @CancellationReason int cancelReason) { boolean didSomething = false; synchronized (mLock) { @@ -256,7 +259,7 @@ public class PendingIntentController { } didSomething = true; it.remove(); - makeIntentSenderCanceled(pir); + makeIntentSenderCanceled(pir, cancelReason); decrementUidStatLocked(pir); if (pir.key.activity != null) { final Message m = PooledLambda.obtainMessage( @@ -289,13 +292,14 @@ public class PendingIntentController { } catch (RemoteException e) { throw new SecurityException(e); } - cancelIntentSender(rec, true); + cancelIntentSender(rec, true, CANCEL_REASON_OWNER_CANCELED); } } - public void cancelIntentSender(PendingIntentRecord rec, boolean cleanActivity) { + public void cancelIntentSender(PendingIntentRecord rec, boolean cleanActivity, + @CancellationReason int cancelReason) { synchronized (mLock) { - makeIntentSenderCanceled(rec); + makeIntentSenderCanceled(rec, cancelReason); mIntentSenderRecords.remove(rec.key); decrementUidStatLocked(rec); if (cleanActivity && rec.key.activity != null) { @@ -359,8 +363,10 @@ public class PendingIntentController { } } - private void makeIntentSenderCanceled(PendingIntentRecord rec) { + private void makeIntentSenderCanceled(PendingIntentRecord rec, + @CancellationReason int cancelReason) { rec.canceled = true; + rec.cancelReason = cancelReason; final RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked(); if (callbacks != null) { final Message m = PooledLambda.obtainMessage( diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 95e130ed1194..71cca7c7e12f 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -21,6 +21,7 @@ import static android.app.ActivityManager.START_SUCCESS; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.app.ActivityManager; @@ -51,11 +52,14 @@ import android.util.ArraySet; import android.util.Slog; import android.util.TimeUtils; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.wm.SafeActivityOptions; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.Objects; @@ -71,12 +75,35 @@ public final class PendingIntentRecord extends IIntentSender.Stub { public static final int FLAG_BROADCAST_SENDER = 1 << 1; public static final int FLAG_SERVICE_SENDER = 1 << 2; + public static final int CANCEL_REASON_NULL = 0; + public static final int CANCEL_REASON_USER_STOPPED = 1 << 0; + public static final int CANCEL_REASON_OWNER_UNINSTALLED = 1 << 1; + public static final int CANCEL_REASON_OWNER_FORCE_STOPPED = 1 << 2; + public static final int CANCEL_REASON_OWNER_CANCELED = 1 << 3; + public static final int CANCEL_REASON_HOSTING_ACTIVITY_DESTROYED = 1 << 4; + public static final int CANCEL_REASON_SUPERSEDED = 1 << 5; + public static final int CANCEL_REASON_ONE_SHOT_SENT = 1 << 6; + + @IntDef({ + CANCEL_REASON_NULL, + CANCEL_REASON_USER_STOPPED, + CANCEL_REASON_OWNER_UNINSTALLED, + CANCEL_REASON_OWNER_FORCE_STOPPED, + CANCEL_REASON_OWNER_CANCELED, + CANCEL_REASON_HOSTING_ACTIVITY_DESTROYED, + CANCEL_REASON_SUPERSEDED, + CANCEL_REASON_ONE_SHOT_SENT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CancellationReason {} + final PendingIntentController controller; final Key key; final int uid; public final WeakReference<PendingIntentRecord> ref; boolean sent = false; boolean canceled = false; + @CancellationReason int cancelReason = CANCEL_REASON_NULL; /** * Map IBinder to duration specified as Pair<Long, Integer>, Long is allowlist duration in * milliseconds, Integer is allowlist type defined at @@ -424,7 +451,7 @@ public final class PendingIntentRecord extends IIntentSender.Stub { sent = true; if ((key.flags & PendingIntent.FLAG_ONE_SHOT) != 0) { - controller.cancelIntentSender(this, true); + controller.cancelIntentSender(this, true, CANCEL_REASON_ONE_SHOT_SENT); } finalIntent = key.requestIntent != null ? new Intent(key.requestIntent) : new Intent(); @@ -687,6 +714,21 @@ public final class PendingIntentRecord extends IIntentSender.Stub { } } + @VisibleForTesting + static String cancelReasonToString(@CancellationReason int cancelReason) { + return switch (cancelReason) { + case CANCEL_REASON_NULL -> "NULL"; + case CANCEL_REASON_USER_STOPPED -> "USER_STOPPED"; + case CANCEL_REASON_OWNER_UNINSTALLED -> "OWNER_UNINSTALLED"; + case CANCEL_REASON_OWNER_FORCE_STOPPED -> "OWNER_FORCE_STOPPED"; + case CANCEL_REASON_OWNER_CANCELED -> "OWNER_CANCELED"; + case CANCEL_REASON_HOSTING_ACTIVITY_DESTROYED -> "HOSTING_ACTIVITY_DESTROYED"; + case CANCEL_REASON_SUPERSEDED -> "SUPERSEDED"; + case CANCEL_REASON_ONE_SHOT_SENT -> "ONE_SHOT_SENT"; + default -> "UNKNOWN"; + }; + } + public void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("uid="); pw.print(uid); pw.print(" packageName="); pw.print(key.packageName); @@ -707,7 +749,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub { } if (sent || canceled) { pw.print(prefix); pw.print("sent="); pw.print(sent); - pw.print(" canceled="); pw.println(canceled); + pw.print(" canceled="); pw.print(canceled); + pw.print(" cancelReason="); pw.println(cancelReasonToString(cancelReason)); } if (mAllowlistDuration != null) { pw.print(prefix); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 207d42b6b58d..e687f6ad1bfc 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4259,7 +4259,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A PendingIntentRecord rec = apr.get(); if (rec != null) { mAtmService.mPendingIntentController.cancelIntentSender(rec, - false /* cleanActivity */); + false /* cleanActivity */, + PendingIntentRecord.CANCEL_REASON_HOSTING_ACTIVITY_DESTROYED); } } pendingResults = null; diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java index 783971a1afe4..89b48bad2358 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java @@ -16,9 +16,18 @@ package com.android.server.am; +import static android.os.Process.INVALID_UID; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_NULL; +import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_ONE_SHOT_SENT; +import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_OWNER_CANCELED; +import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_OWNER_FORCE_STOPPED; +import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_SUPERSEDED; +import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_USER_STOPPED; +import static com.android.server.am.PendingIntentRecord.cancelReasonToString; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyInt; @@ -34,6 +43,7 @@ import android.app.PendingIntent; import android.content.Intent; import android.content.pm.IPackageManager; import android.os.Looper; +import android.os.UserHandle; import androidx.test.runner.AndroidJUnit4; @@ -54,6 +64,7 @@ public class PendingIntentControllerTest { private static final String TEST_PACKAGE_NAME = "test-package-1"; private static final String TEST_FEATURE_ID = "test-feature-1"; private static final int TEST_CALLING_UID = android.os.Process.myUid(); + private static final int TEST_USER_ID = 0; private static final Intent[] TEST_INTENTS = new Intent[]{new Intent("com.test.intent")}; @Mock @@ -92,7 +103,7 @@ public class PendingIntentControllerTest { private PendingIntentRecord createPendingIntentRecord(int flags) { return mPendingIntentController.getIntentSender(ActivityManager.INTENT_SENDER_BROADCAST, - TEST_PACKAGE_NAME, TEST_FEATURE_ID, TEST_CALLING_UID, 0, null, null, 0, + TEST_PACKAGE_NAME, TEST_FEATURE_ID, TEST_CALLING_UID, TEST_USER_ID, null, null, 0, TEST_INTENTS, null, flags, null); } @@ -126,6 +137,54 @@ public class PendingIntentControllerTest { piCaptor.getValue().getTarget()); } + @Test + public void testCancellationReason() { + { + final PendingIntentRecord pir = createPendingIntentRecord(0); + assertCancelReason(CANCEL_REASON_NULL, pir.cancelReason); + } + + { + final PendingIntentRecord pir = createPendingIntentRecord(0); + mPendingIntentController.cancelIntentSender(pir); + assertCancelReason(CANCEL_REASON_OWNER_CANCELED, pir.cancelReason); + } + + { + final PendingIntentRecord pir = createPendingIntentRecord(0); + createPendingIntentRecord(PendingIntent.FLAG_CANCEL_CURRENT); + assertCancelReason(CANCEL_REASON_SUPERSEDED, pir.cancelReason); + } + + { + final PendingIntentRecord pir = createPendingIntentRecord(PendingIntent.FLAG_ONE_SHOT); + pir.send(0, null, null, null, null, null, null); + assertCancelReason(CANCEL_REASON_ONE_SHOT_SENT, pir.cancelReason); + } + + { + final PendingIntentRecord pir = createPendingIntentRecord(0); + mPendingIntentController.removePendingIntentsForPackage(TEST_PACKAGE_NAME, + TEST_USER_ID, UserHandle.getAppId(TEST_CALLING_UID), true, + CANCEL_REASON_OWNER_FORCE_STOPPED); + assertCancelReason(CANCEL_REASON_OWNER_FORCE_STOPPED, pir.cancelReason); + } + + { + final PendingIntentRecord pir = createPendingIntentRecord(0); + mPendingIntentController.removePendingIntentsForPackage(null, + TEST_USER_ID, INVALID_UID, true, + CANCEL_REASON_USER_STOPPED); + assertCancelReason(CANCEL_REASON_USER_STOPPED, pir.cancelReason); + } + } + + private void assertCancelReason(int expectedReason, int actualReason) { + final String errMsg = "Expected: " + cancelReasonToString(expectedReason) + + "; Actual: " + cancelReasonToString(actualReason); + assertEquals(errMsg, expectedReason, actualReason); + } + @After public void tearDown() { if (mMockingSession != null) { |