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

Commit f6b5dfae authored by Sudheer Shanka's avatar Sudheer Shanka Committed by Android (Google) Code Review
Browse files

Merge changes from topic "log-count-pi-from-top" into main

* changes:
  Log the count of force-stop cancelled PendingIntents sent from top.
  Allow determining the reason for a PendingIntent cancellation.
parents 804f0c04 c409fda9
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -4437,8 +4437,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) {
+13 −7
Original line number Diff line number Diff line
@@ -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(
+57 −2
Original line number Diff line number Diff line
@@ -16,11 +16,13 @@

package com.android.server.am;

import static android.app.ActivityManager.PROCESS_STATE_TOP;
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 +53,15 @@ 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.modules.expresslog.Counter;
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 +77,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
@@ -419,12 +448,22 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
        SafeActivityOptions mergedOptions = null;
        synchronized (controller.mLock) {
            if (canceled) {
                if (cancelReason == CANCEL_REASON_OWNER_FORCE_STOPPED
                        && controller.mAmInternal.getUidProcessState(callingUid)
                                == PROCESS_STATE_TOP) {
                    Counter.logIncrementWithUid(
                            "app.value_force_stop_cancelled_pi_sent_from_top_per_caller",
                            callingUid);
                    Counter.logIncrementWithUid(
                            "app.value_force_stop_cancelled_pi_sent_from_top_per_owner",
                            uid);
                }
                return ActivityManager.START_CANCELED;
            }

            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 +726,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 +761,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);
+2 −1
Original line number Diff line number Diff line
@@ -4260,7 +4260,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;
+60 −1
Original line number Diff line number Diff line
@@ -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) {