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",