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

Commit b4f626d1 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topic "expose-pending-intent" into stage-aosp-master

* changes:
  Reduce PendingIntent memory allocation
  Expose PendingIntent.addCancelListener
parents 10284429 41b8d147
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -35,8 +35,14 @@ package android.app {
  }

  public final class PendingIntent implements android.os.Parcelable {
    method public boolean addCancelListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.PendingIntent.CancelListener);
    method @RequiresPermission(android.Manifest.permission.GET_INTENT_SENDER_INTENT) public boolean intentFilterEquals(@Nullable android.app.PendingIntent);
    method @NonNull @RequiresPermission(android.Manifest.permission.GET_INTENT_SENDER_INTENT) public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
    method public void removeCancelListener(@NonNull android.app.PendingIntent.CancelListener);
  }

  public static interface PendingIntent.CancelListener {
    method public void onCancelled(@NonNull android.app.PendingIntent);
  }

  public class StatusBarManager {
+6 −0
Original line number Diff line number Diff line
@@ -313,11 +313,17 @@ package android.app {
  }

  public final class PendingIntent implements android.os.Parcelable {
    method public boolean addCancelListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.PendingIntent.CancelListener);
    method @RequiresPermission("android.permission.GET_INTENT_SENDER_INTENT") public boolean intentFilterEquals(@Nullable android.app.PendingIntent);
    method @NonNull @RequiresPermission("android.permission.GET_INTENT_SENDER_INTENT") public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
    method public void removeCancelListener(@NonNull android.app.PendingIntent.CancelListener);
    field @Deprecated public static final int FLAG_MUTABLE_UNAUDITED = 33554432; // 0x2000000
  }

  public static interface PendingIntent.CancelListener {
    method public void onCancelled(@NonNull android.app.PendingIntent);
  }

  public final class PictureInPictureParams implements android.os.Parcelable {
    method public java.util.List<android.app.RemoteAction> getActions();
    method public float getAspectRatio();
+8 −1
Original line number Diff line number Diff line
@@ -250,7 +250,14 @@ interface IActivityManager {
            in String[] resolvedTypes, int flags, in Bundle options, int userId);
    void cancelIntentSender(in IIntentSender sender);
    ActivityManager.PendingIntentInfo getInfoForIntentSender(in IIntentSender sender);
    void registerIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
    /**
      This method used to be called registerIntentSenderCancelListener(), was void, and
      would call `receiver` if the PI has already been canceled.
      Now it returns false if the PI is cancelled, without calling `receiver`.
      The method was renamed to catch calls to the original method.
     */
    boolean registerIntentSenderCancelListenerEx(in IIntentSender sender,
        in IResultReceiver receiver);
    void unregisterIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
    void enterSafeMode();
    void noteWakeupAlarm(in IIntentSender sender, in WorkSource workSource, int sourceUid,
+100 −33
Original line number Diff line number Diff line
@@ -53,8 +53,10 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.AndroidException;
import android.util.ArraySet;
import android.util.Pair;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;

import java.lang.annotation.Retention;
@@ -62,6 +64,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;

/**
 * A description of an Intent and target action to perform with it.  Instances
@@ -124,14 +127,37 @@ import java.util.Objects;
 */
public final class PendingIntent implements Parcelable {
    private static final String TAG = "PendingIntent";
    @NonNull
    private final IIntentSender mTarget;
    private IResultReceiver mCancelReceiver;
    private IBinder mWhitelistToken;
    private ArraySet<CancelListener> mCancelListeners;

    // cached pending intent information
    private @Nullable PendingIntentInfo mCachedInfo;

    /**
     * Structure to store information related to {@link #addCancelListener}, which is rarely used,
     * so we lazily allocate it to keep the PendingIntent class size small.
     */
    private final class CancelListerInfo extends IResultReceiver.Stub {
        private final ArraySet<Pair<Executor, CancelListener>> mCancelListeners = new ArraySet<>();

        /**
         * Whether the PI is canceled or not. Note this is essentially a "cache" that's updated
         * only when the client uses {@link #addCancelListener}. Even if this is false, that
         * still doesn't know the PI is *not* canceled, but if it's true, this PI is definitely
         * canceled.
         */
        private boolean mCanceled;

        @Override
        public void send(int resultCode, Bundle resultData) throws RemoteException {
            notifyCancelListeners();
        }
    }

    @GuardedBy("mTarget")
    private @Nullable CancelListerInfo mCancelListerInfo;

    /**
     * It is now required to specify either {@link #FLAG_IMMUTABLE}
     * or {@link #FLAG_MUTABLE} when creating a PendingIntent.
@@ -1048,66 +1074,105 @@ public final class PendingIntent implements Parcelable {
    }

    /**
     * Register a listener to when this pendingIntent is cancelled. There are no guarantees on which
     * thread a listener will be called and it's up to the caller to synchronize. This may
     * trigger a synchronous binder call so should therefore usually be called on a background
     * thread.
     *
     * @hide
     * @deprecated use {@link #addCancelListener(Executor, CancelListener)} instead.
     */
    public void registerCancelListener(CancelListener cancelListener) {
        synchronized (this) {
            if (mCancelReceiver == null) {
                mCancelReceiver = new IResultReceiver.Stub() {
                    @Override
                    public void send(int resultCode, Bundle resultData) throws RemoteException {
                        notifyCancelListeners();
    @Deprecated
    public void registerCancelListener(@NonNull CancelListener cancelListener) {
        if (!addCancelListener(Runnable::run, cancelListener)) {
            // Call the callback right away synchronously, if the PI has been canceled already.
            cancelListener.onCancelled(this);
        }
                };
    }
            if (mCancelListeners == null) {
                mCancelListeners = new ArraySet<>();

    /**
     * Register a listener to when this pendingIntent is cancelled.
     *
     * @return true if the listener has been set successfully. false if the {@link PendingIntent}
     * has already been canceled.
     *
     * @hide
     */
    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
    @TestApi
    public boolean addCancelListener(@NonNull Executor executor,
            @NonNull CancelListener cancelListener) {
        synchronized (mTarget) {
            if (mCancelListerInfo != null && mCancelListerInfo.mCanceled) {
                return false;
            }
            if (mCancelListerInfo == null) {
                mCancelListerInfo = new CancelListerInfo();
            }
            boolean wasEmpty = mCancelListeners.isEmpty();
            mCancelListeners.add(cancelListener);
            final CancelListerInfo cli = mCancelListerInfo;

            boolean wasEmpty = cli.mCancelListeners.isEmpty();
            cli.mCancelListeners.add(Pair.create(executor, cancelListener));
            if (wasEmpty) {
                boolean success;
                try {
                    ActivityManager.getService().registerIntentSenderCancelListener(mTarget,
                            mCancelReceiver);
                    success = ActivityManager.getService().registerIntentSenderCancelListenerEx(
                            mTarget, cli);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
                if (!success) {
                    cli.mCanceled = true;
                }
                return success;
            } else {
                return !cli.mCanceled;
            }
        }
    }

    private void notifyCancelListeners() {
        ArraySet<CancelListener> cancelListeners;
        synchronized (this) {
            cancelListeners = new ArraySet<>(mCancelListeners);
        ArraySet<Pair<Executor, CancelListener>> cancelListeners;
        synchronized (mTarget) {
            // When notifyCancelListeners() is called, mCancelListerInfo must always be non-null.
            final CancelListerInfo cli = mCancelListerInfo;
            cli.mCanceled = true;
            cancelListeners = new ArraySet<>(cli.mCancelListeners);
            cli.mCancelListeners.clear();
        }
        int size = cancelListeners.size();
        for (int i = 0; i < size; i++) {
            cancelListeners.valueAt(i).onCancelled(this);
            final Pair<Executor, CancelListener> pair = cancelListeners.valueAt(i);
            pair.first.execute(() -> pair.second.onCancelled(this));
        }
    }

    /**
     * @hide
     * @deprecated use {@link #removeCancelListener(CancelListener)} instead.
     */
    @Deprecated
    public void unregisterCancelListener(CancelListener cancelListener) {
        removeCancelListener(cancelListener);
    }

    /**
     * Un-register a listener to when this pendingIntent is cancelled.
     *
     * @hide
     */
    public void unregisterCancelListener(CancelListener cancelListener) {
        synchronized (this) {
            if (mCancelListeners == null) {
    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
    @TestApi
    public void removeCancelListener(@NonNull CancelListener cancelListener) {
        synchronized (mTarget) {
            final CancelListerInfo cli = mCancelListerInfo;
            if (cli == null || cli.mCancelListeners.size() == 0) {
                return;
            }
            boolean wasEmpty = mCancelListeners.isEmpty();
            mCancelListeners.remove(cancelListener);
            if (mCancelListeners.isEmpty() && !wasEmpty) {
            for (int i = cli.mCancelListeners.size() - 1; i >= 0; i--) {
                if (cli.mCancelListeners.valueAt(i).second == cancelListener) {
                    cli.mCancelListeners.removeAt(i);
                }
            }
            if (cli.mCancelListeners.isEmpty()) {
                try {
                    ActivityManager.getService().unregisterIntentSenderCancelListener(mTarget,
                            mCancelReceiver);
                            cli);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
@@ -1401,13 +1466,15 @@ public final class PendingIntent implements Parcelable {
     *
     * @hide
     */
    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
    @TestApi
    public interface CancelListener {
        /**
         * Called when a Pending Intent is cancelled.
         *
         * @param intent The intent that was cancelled.
         */
        void onCancelled(PendingIntent intent);
        void onCancelled(@NonNull PendingIntent intent);
    }

    private PendingIntentInfo getCachedInfo() {
+3 −2
Original line number Diff line number Diff line
@@ -4990,8 +4990,9 @@ public class ActivityManagerService extends IActivityManager.Stub
    }
    @Override
    public void registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
        mPendingIntentController.registerIntentSenderCancelListener(sender, receiver);
    public boolean registerIntentSenderCancelListenerEx(
            IIntentSender sender, IResultReceiver receiver) {
        return mPendingIntentController.registerIntentSenderCancelListener(sender, receiver);
    }
    @Override
Loading