Merge "Merge SP1A.210812.016" into stage-aosp-master
diff --git a/api/Android.bp b/api/Android.bp
index 96f9990..2d7b8bd 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -24,9 +24,8 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-python_binary_host {
-    name: "api_versions_trimmer",
-    srcs: ["api_versions_trimmer.py"],
+python_defaults {
+    name: "python3_version_defaults",
     version: {
         py2: {
             enabled: false,
@@ -38,6 +37,12 @@
     },
 }
 
+python_binary_host {
+    name: "api_versions_trimmer",
+    srcs: ["api_versions_trimmer.py"],
+    defaults: ["python3_version_defaults"],
+}
+
 python_test_host {
     name: "api_versions_trimmer_unittests",
     main: "api_versions_trimmer_unittests.py",
@@ -45,17 +50,28 @@
         "api_versions_trimmer_unittests.py",
         "api_versions_trimmer.py",
     ],
+    defaults: ["python3_version_defaults"],
     test_options: {
         unit_test: true,
     },
-    version: {
-        py2: {
-            enabled: false,
-        },
-        py3: {
-            enabled: true,
-            embedded_launcher: false,
-        },
+}
+
+python_binary_host {
+    name: "merge_annotation_zips",
+    srcs: ["merge_annotation_zips.py"],
+    defaults: ["python3_version_defaults"],
+}
+
+python_test_host {
+    name: "merge_annotation_zips_test",
+    main: "merge_annotation_zips_test.py",
+    srcs: [
+        "merge_annotation_zips.py",
+        "merge_annotation_zips_test.py",
+    ],
+    defaults: ["python3_version_defaults"],
+    test_options: {
+        unit_test: true,
     },
 }
 
diff --git a/api/merge_annotation_zips.py b/api/merge_annotation_zips.py
new file mode 100755
index 0000000..9c67d7b
--- /dev/null
+++ b/api/merge_annotation_zips.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+"""Script to merge annotation XML files (created by e.g. metalava)."""
+
+from pathlib import Path
+import sys
+import xml.etree.ElementTree as ET
+import zipfile
+
+
+def validate_xml_assumptions(root):
+  """Verify the format of the annotations XML matches expectations"""
+  prevName = ""
+  assert root.tag == 'root'
+  for child in root:
+    assert child.tag == 'item', 'unexpected tag: %s' % child.tag
+    assert list(child.attrib.keys()) == ['name'], 'unexpected attribs: %s' % child.attrib.keys()
+    assert prevName < child.get('name'), 'items unexpectedly not strictly sorted (possibly duplicate entries)'
+    prevName = child.get('name')
+
+
+def merge_xml(a, b):
+  """Merge two annotation xml files"""
+  for xml in [a, b]:
+    validate_xml_assumptions(xml)
+  a.extend(b[:])
+  a[:] = sorted(a[:], key=lambda x: x.get('name'))
+  validate_xml_assumptions(a)
+
+
+def merge_zip_file(out_dir, zip_file):
+  """Merge the content of the zip_file into out_dir"""
+  for filename in zip_file.namelist():
+    path = Path(out_dir, filename)
+    if path.exists():
+      existing_xml = ET.parse(path)
+      with zip_file.open(filename) as other_file:
+        other_xml = ET.parse(other_file)
+      merge_xml(existing_xml.getroot(), other_xml.getroot())
+      existing_xml.write(path, encoding='UTF-8', xml_declaration=True)
+    else:
+      zip_file.extract(filename, out_dir)
+
+
+def main():
+  out_dir = Path(sys.argv[1])
+  zip_filenames = sys.argv[2:]
+
+  assert not out_dir.exists()
+  out_dir.mkdir()
+  for zip_filename in zip_filenames:
+    with zipfile.ZipFile(zip_filename) as zip_file:
+      merge_zip_file(out_dir, zip_file)
+
+
+if __name__ == "__main__":
+  main()
diff --git a/api/merge_annotation_zips_test.py b/api/merge_annotation_zips_test.py
new file mode 100644
index 0000000..26795c47
--- /dev/null
+++ b/api/merge_annotation_zips_test.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+import io
+from pathlib import Path
+import tempfile
+import unittest
+import zipfile
+
+import merge_annotation_zips
+
+
+zip_a = {
+  'android/provider/annotations.xml':
+  """<?xml version="1.0" encoding="UTF-8"?>
+<root>
+  <item name="android.provider.BlockedNumberContract boolean isBlocked(android.content.Context, java.lang.String)">
+    <annotation name="androidx.annotation.WorkerThread"/>
+  </item>
+  <item name="android.provider.SimPhonebookContract.SimRecords android.net.Uri getItemUri(int, int, int) 2">
+    <annotation name="androidx.annotation.IntRange">
+      <val name="from" val="1" />
+    </annotation>
+  </item>
+</root>""",
+  'android/os/annotations.xml':
+  """<?xml version="1.0" encoding="UTF-8"?>
+<root>
+  <item name="android.app.ActionBar void setCustomView(int) 0">
+    <annotation name="androidx.annotation.LayoutRes"/>
+  </item>
+</root>
+"""
+}
+
+zip_b = {
+  'android/provider/annotations.xml':
+  """<?xml version="1.0" encoding="UTF-8"?>
+<root>
+  <item name="android.provider.MediaStore QUERY_ARG_MATCH_FAVORITE">
+    <annotation name="androidx.annotation.IntDef">
+      <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" />
+      <val name="flag" val="true" />
+    </annotation>
+  </item>
+  <item name="android.provider.MediaStore QUERY_ARG_MATCH_PENDING">
+    <annotation name="androidx.annotation.IntDef">
+      <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" />
+      <val name="flag" val="true" />
+    </annotation>
+  </item>
+</root>"""
+}
+
+zip_c = {
+  'android/app/annotations.xml':
+  """<?xml version="1.0" encoding="UTF-8"?>
+<root>
+  <item name="android.app.ActionBar void setCustomView(int) 0">
+    <annotation name="androidx.annotation.LayoutRes"/>
+  </item>
+</root>"""
+}
+
+merged_provider = """<?xml version='1.0' encoding='UTF-8'?>
+<root>
+  <item name="android.provider.BlockedNumberContract boolean isBlocked(android.content.Context, java.lang.String)">
+    <annotation name="androidx.annotation.WorkerThread" />
+  </item>
+  <item name="android.provider.MediaStore QUERY_ARG_MATCH_FAVORITE">
+    <annotation name="androidx.annotation.IntDef">
+      <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" />
+      <val name="flag" val="true" />
+    </annotation>
+  </item>
+  <item name="android.provider.MediaStore QUERY_ARG_MATCH_PENDING">
+    <annotation name="androidx.annotation.IntDef">
+      <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" />
+      <val name="flag" val="true" />
+    </annotation>
+  </item>
+<item name="android.provider.SimPhonebookContract.SimRecords android.net.Uri getItemUri(int, int, int) 2">
+    <annotation name="androidx.annotation.IntRange">
+      <val name="from" val="1" />
+    </annotation>
+  </item>
+</root>"""
+
+
+
+class MergeAnnotationZipsTest(unittest.TestCase):
+
+  def test_merge_zips(self):
+    with tempfile.TemporaryDirectory() as out_dir:
+      for zip_content in [zip_a, zip_b, zip_c]:
+        f = io.BytesIO()
+        with zipfile.ZipFile(f, "w") as zip_file:
+          for filename, content in zip_content.items():
+            zip_file.writestr(filename, content)
+          merge_annotation_zips.merge_zip_file(out_dir, zip_file)
+
+      # Unchanged
+      self.assertEqual(zip_a['android/os/annotations.xml'], Path(out_dir, 'android/os/annotations.xml').read_text())
+      self.assertEqual(zip_c['android/app/annotations.xml'], Path(out_dir, 'android/app/annotations.xml').read_text())
+
+      # Merged
+      self.assertEqual(merged_provider, Path(out_dir, 'android/provider/annotations.xml').read_text())
+
+
+if __name__ == "__main__":
+  unittest.main(verbosity=2)
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 9c4d53a..f72c3a7 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -35,8 +35,14 @@
   }
 
   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 {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d8ced6f..6a57c24 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -9057,6 +9057,10 @@
     field public static final int ERROR_UNKNOWN = 0; // 0x0
   }
 
+  public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
+    field @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public static final android.net.Uri ENTERPRISE_CONTENT_URI;
+  }
+
   @Deprecated public static final class ContactsContract.MetadataSync implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncColumns {
     field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
     field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ea6d0ce..9505aad 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -313,11 +313,17 @@
   }
 
   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();
@@ -2107,7 +2113,7 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    field public static final android.net.Uri ENTERPRISE_CONTENT_URI;
+    field @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public static final android.net.Uri ENTERPRISE_CONTENT_URI;
   }
 
   public static final class ContactsContract.PinnedPositions {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index c330ff7..5f2c456 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -371,12 +371,7 @@
 filegroup {
     name: "framework-cellbroadcast-shared-srcs",
     srcs: [
-        ":modules-utils-preconditions-srcs",
-        "android/os/HandlerExecutor.java",
         "android/util/LocalLog.java",
-        "com/android/internal/util/IState.java",
-        "com/android/internal/util/State.java",
-        "com/android/internal/util/StateMachine.java",
     ],
 }
 
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index b90b9a1..dfc4a6a 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -250,7 +250,14 @@
             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,
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 0136a35..9b9eed4 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -53,8 +53,10 @@
 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.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,15 +127,38 @@
  */
 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 @@
     }
 
     /**
-     * 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.
+     */
+    @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);
+        }
+    }
+
+    /**
+     * 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
      */
-    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();
-                    }
-                };
+    @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 (mCancelListeners == null) {
-                mCancelListeners = new ArraySet<>();
+            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 @@
      *
      * @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() {
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 376d942..0f0f123 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -6159,9 +6159,10 @@
             *
             * @hide
             */
-            @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+            @SystemApi
             @TestApi
-            public static final Uri ENTERPRISE_CONTENT_URI =
+            @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+            public static final @NonNull Uri ENTERPRISE_CONTENT_URI =
                     Uri.withAppendedPath(Data.ENTERPRISE_CONTENT_URI, "phones");
 
             /**
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f74d7ac..3f816c88 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4990,8 +4990,9 @@
     }
 
     @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
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index 534bd84..c49e696 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -271,9 +271,11 @@
         }
     }
 
-    void registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
+    boolean registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
         if (!(sender instanceof PendingIntentRecord)) {
-            return;
+            Slog.w(TAG, "registerIntentSenderCancelListener called on non-PendingIntentRecord");
+            // In this case, it's not "success", but we don't know if it's canceld either.
+            return true;
         }
         boolean isCancelled;
         synchronized (mLock) {
@@ -281,12 +283,9 @@
             isCancelled = pendingIntent.canceled;
             if (!isCancelled) {
                 pendingIntent.registerCancelListenerLocked(receiver);
-            }
-        }
-        if (isCancelled) {
-            try {
-                receiver.send(Activity.RESULT_CANCELED, null);
-            } catch (RemoteException e) {
+                return true;
+            } else {
+                return false;
             }
         }
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index fac8cfe..e5a8a17d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -634,6 +634,12 @@
         // on boot, if device is interactive, set HDMI CEC state as powered on as well
         if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
             mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+            // Start all actions that were queued because the device was in standby
+            if (mAddressAllocated) {
+                for (HdmiCecLocalDevice localDevice : getAllLocalDevices()) {
+                    localDevice.startQueuedActions();
+                }
+            }
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index d03b674..01bd04c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -33,6 +33,8 @@
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -59,6 +61,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
@@ -321,6 +324,18 @@
     }
 
     @Test
+    public void normalBoot_queuedActionsStartedAfterBoot() {
+        Mockito.clearInvocations(mAudioSystemDeviceSpy);
+        Mockito.clearInvocations(mPlaybackDeviceSpy);
+
+        mHdmiControlServiceSpy.onBootPhase(PHASE_BOOT_COMPLETED);
+        mTestLooper.dispatchAll();
+
+        verify(mAudioSystemDeviceSpy, times(1)).startQueuedActions();
+        verify(mPlaybackDeviceSpy, times(1)).startQueuedActions();
+    }
+
+    @Test
     public void initialPowerStatus_normalBoot_goToStandby_broadcastsPowerStatus_2_0() {
         mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index 558798d..7a564fc 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -55,7 +55,7 @@
     ],
     data: [
         ":com.android.apex.apkrollback.test_v1",
-        ":com.android.apex.cts.shim.v2_prebuilt",
+        ":StagedInstallTestApexV2",
         ":StagedInstallTestApexV2_WrongSha",
         ":TestAppAv1",
         ":test.rebootless_apex_v1",