summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xapi/system-current.txt27
-rw-r--r--api/test-current.txt8
-rw-r--r--core/java/android/view/BatchedInputEventReceiver.java23
-rw-r--r--core/jni/android_media_AudioSystem.cpp83
-rw-r--r--media/java/android/media/AudioDeviceAttributes.java25
-rw-r--r--media/java/android/media/AudioDeviceInfo.java45
-rw-r--r--media/java/android/media/AudioManager.java343
-rw-r--r--media/java/android/media/AudioSystem.java129
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl13
-rw-r--r--media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl31
-rw-r--r--media/java/android/media/MediaRecorder.java26
-rw-r--r--media/java/android/media/MediaTranscodeManager.java136
-rw-r--r--media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java15
-rw-r--r--non-updatable-api/system-current.txt27
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java12
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java45
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java77
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java89
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java34
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java11
-rw-r--r--services/core/java/com/android/server/display/ColorFade.java4
-rw-r--r--services/core/java/com/android/server/telecom/OWNERS6
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java17
-rw-r--r--telecomm/OWNERS5
-rw-r--r--wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java8
45 files changed, 1437 insertions, 235 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index c3e56643a805..3ec346713792 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4199,8 +4199,10 @@ package android.media {
public class AudioManager {
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
method public void clearAudioServerStateCallback();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
@@ -4211,6 +4213,7 @@ package android.media {
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
@@ -4219,6 +4222,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
@@ -4228,6 +4232,7 @@ package android.media {
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
@@ -4257,6 +4262,10 @@ package android.media {
method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
}
+ public static interface AudioManager.OnPreferredDevicesForCapturePresetChangedListener {
+ method public void onPreferredDevicesForCapturePresetChanged(int, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
+ }
+
public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener {
method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
}
@@ -4385,6 +4394,14 @@ package android.media {
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat);
}
+ public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver {
+ ctor public MediaTranscodeManager.TranscodingRequest.MediaFormatResolver();
+ method @Nullable public android.media.MediaFormat resolveVideoFormat();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver setSourceVideoFormatHint(@NonNull android.media.MediaFormat);
+ method public boolean shouldTranscode();
+ field public static final String CAPS_SUPPORTS_HEVC = "support-hevc";
+ }
+
public class PlayerProxy {
method public void pause();
method public void setPan(float);
@@ -7873,7 +7890,7 @@ package android.net.wifi.nl80211 {
method @NonNull public java.util.List<android.net.wifi.nl80211.NativeScanResult> getScanResults(@NonNull String, int);
method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
method @Nullable public static android.net.wifi.nl80211.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
- method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
+ method @Deprecated public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SendMgmtFrameCallback);
method public void setOnServiceDeadCallback(@NonNull Runnable);
method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback);
@@ -7924,10 +7941,10 @@ package android.net.wifi.nl80211 {
field public final int txBitrateMbps;
}
- public static interface WifiNl80211Manager.SoftApCallback {
- method public void onConnectedClientsChanged(@NonNull android.net.wifi.nl80211.NativeWifiClient, boolean);
- method public void onFailure();
- method public void onSoftApChannelSwitched(int, int);
+ @Deprecated public static interface WifiNl80211Manager.SoftApCallback {
+ method @Deprecated public void onConnectedClientsChanged(@NonNull android.net.wifi.nl80211.NativeWifiClient, boolean);
+ method @Deprecated public void onFailure();
+ method @Deprecated public void onSoftApChannelSwitched(int, int);
}
public static class WifiNl80211Manager.TxPacketCounters {
diff --git a/api/test-current.txt b/api/test-current.txt
index 1aa3db6963b7..3de1d93785c1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1870,6 +1870,14 @@ package android.media {
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat);
}
+ public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver {
+ ctor public MediaTranscodeManager.TranscodingRequest.MediaFormatResolver();
+ method @Nullable public android.media.MediaFormat resolveVideoFormat();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver setSourceVideoFormatHint(@NonNull android.media.MediaFormat);
+ method public boolean shouldTranscode();
+ field public static final String CAPS_SUPPORTS_HEVC = "support-hevc";
+ }
+
public final class PlaybackParams implements android.os.Parcelable {
method public int getAudioStretchMode();
method public android.media.PlaybackParams setAudioStretchMode(int);
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
index 30e3ec135065..7023e4bd0134 100644
--- a/core/java/android/view/BatchedInputEventReceiver.java
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -24,7 +24,8 @@ import android.os.Looper;
* @hide
*/
public class BatchedInputEventReceiver extends InputEventReceiver {
- Choreographer mChoreographer;
+ private Choreographer mChoreographer;
+ private boolean mBatchingEnabled;
private boolean mBatchedInputScheduled;
@UnsupportedAppUsage
@@ -32,19 +33,37 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
InputChannel inputChannel, Looper looper, Choreographer choreographer) {
super(inputChannel, looper);
mChoreographer = choreographer;
+ mBatchingEnabled = true;
}
@Override
public void onBatchedInputEventPending(int source) {
- scheduleBatchedInput();
+ if (mBatchingEnabled) {
+ scheduleBatchedInput();
+ } else {
+ consumeBatchedInputEvents(-1);
+ }
}
@Override
public void dispose() {
unscheduleBatchedInput();
+ consumeBatchedInputEvents(-1);
super.dispose();
}
+ /**
+ * Sets whether to enable batching on this input event receiver.
+ * @hide
+ */
+ public void setBatchingEnabled(boolean batchingEnabled) {
+ mBatchingEnabled = batchingEnabled;
+ if (!batchingEnabled) {
+ unscheduleBatchedInput();
+ consumeBatchedInputEvents(-1);
+ }
+ }
+
void doConsumeBatchedInput(long frameTimeNanos) {
if (mBatchedInputScheduled) {
mBatchedInputScheduled = false;
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 3f39478ffd43..5c4c5099bf4c 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2407,6 +2407,79 @@ static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env,
return AUDIO_JAVA_SUCCESS;
}
+static jint android_media_AudioSystem_setDevicesRoleForCapturePreset(
+ JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
+ jobjectArray jDeviceAddresses) {
+ AudioDeviceTypeAddrVector nDevices;
+ jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+ if (results != NO_ERROR) {
+ return results;
+ }
+ int status = check_AudioSystem_Command(
+ AudioSystem::setDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+ (device_role_t)role, nDevices));
+ return (jint)status;
+}
+
+static jint android_media_AudioSystem_addDevicesRoleForCapturePreset(
+ JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
+ jobjectArray jDeviceAddresses) {
+ AudioDeviceTypeAddrVector nDevices;
+ jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+ if (results != NO_ERROR) {
+ return results;
+ }
+ int status = check_AudioSystem_Command(
+ AudioSystem::addDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+ (device_role_t)role, nDevices));
+ return (jint)status;
+}
+
+static jint android_media_AudioSystem_removeDevicesRoleForCapturePreset(
+ JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
+ jobjectArray jDeviceAddresses) {
+ AudioDeviceTypeAddrVector nDevices;
+ jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+ if (results != NO_ERROR) {
+ return results;
+ }
+ int status = check_AudioSystem_Command(
+ AudioSystem::removeDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+ (device_role_t)role, nDevices));
+ return (jint)status;
+}
+
+static jint android_media_AudioSystem_clearDevicesRoleForCapturePreset(JNIEnv *env, jobject thiz,
+ jint capturePreset,
+ jint role) {
+ return (jint)check_AudioSystem_Command(
+ AudioSystem::clearDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+ (device_role_t)role));
+}
+
+static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv *env, jobject thiz,
+ jint capturePreset,
+ jint role,
+ jobject jDevices) {
+ AudioDeviceTypeAddrVector nDevices;
+ status_t status = check_AudioSystem_Command(
+ AudioSystem::getDevicesForRoleAndCapturePreset((audio_source_t)capturePreset,
+ (device_role_t)role, nDevices));
+ if (status != NO_ERROR) {
+ return (jint)status;
+ }
+ for (const auto &device : nDevices) {
+ jobject jAudioDeviceAttributes = NULL;
+ jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &device);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+ env->CallBooleanMethod(jDevices, gListMethods.add, jAudioDeviceAttributes);
+ env->DeleteLocalRef(jAudioDeviceAttributes);
+ }
+ return AUDIO_JAVA_SUCCESS;
+}
+
static jint
android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz,
jobject jaa, jobjectArray jDeviceArray)
@@ -2558,6 +2631,16 @@ static const JNINativeMethod gMethods[] =
(void *)android_media_AudioSystem_removeDevicesRoleForStrategy},
{"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I",
(void *)android_media_AudioSystem_getDevicesForRoleAndStrategy},
+ {"setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_setDevicesRoleForCapturePreset},
+ {"addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_addDevicesRoleForCapturePreset},
+ {"removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_removeDevicesRoleForCapturePreset},
+ {"clearDevicesRoleForCapturePreset", "(II)I",
+ (void *)android_media_AudioSystem_clearDevicesRoleForCapturePreset},
+ {"getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I",
+ (void *)android_media_AudioSystem_getDevicesForRoleAndCapturePreset},
{"getDevicesForAttributes",
"(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I",
(void *)android_media_AudioSystem_getDevicesForAttributes},
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index 0ab62c14ab9f..6c8b50037d3d 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -72,6 +72,11 @@ public final class AudioDeviceAttributes implements Parcelable {
private final @Role int mRole;
/**
+ * The internal audio device type
+ */
+ private final int mNativeType;
+
+ /**
* @hide
* Constructor from a valid {@link AudioDeviceInfo}
* @param deviceInfo the connected audio device from which to obtain the device-identifying
@@ -83,6 +88,7 @@ public final class AudioDeviceAttributes implements Parcelable {
mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT;
mType = deviceInfo.getType();
mAddress = deviceInfo.getAddress();
+ mNativeType = deviceInfo.getInternalType();
}
/**
@@ -101,9 +107,12 @@ public final class AudioDeviceAttributes implements Parcelable {
}
if (role == ROLE_OUTPUT) {
AudioDeviceInfo.enforceValidAudioDeviceTypeOut(type);
- }
- if (role == ROLE_INPUT) {
+ mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(type);
+ } else if (role == ROLE_INPUT) {
AudioDeviceInfo.enforceValidAudioDeviceTypeIn(type);
+ mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(type);
+ } else {
+ mNativeType = AudioSystem.DEVICE_NONE;
}
mRole = role;
@@ -115,6 +124,7 @@ public final class AudioDeviceAttributes implements Parcelable {
mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT;
mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType);
mAddress = address;
+ mNativeType = nativeType;
}
/**
@@ -147,6 +157,15 @@ public final class AudioDeviceAttributes implements Parcelable {
return mAddress;
}
+ /**
+ * @hide
+ * Returns the internal device type of a device
+ * @return the internal device type
+ */
+ public int getInternalType() {
+ return mNativeType;
+ }
+
@Override
public int hashCode() {
return Objects.hash(mRole, mType, mAddress);
@@ -189,12 +208,14 @@ public final class AudioDeviceAttributes implements Parcelable {
dest.writeInt(mRole);
dest.writeInt(mType);
dest.writeString(mAddress);
+ dest.writeInt(mNativeType);
}
private AudioDeviceAttributes(@NonNull Parcel in) {
mRole = in.readInt();
mType = in.readInt();
mAddress = in.readString();
+ mNativeType = in.readInt();
}
public static final @NonNull Parcelable.Creator<AudioDeviceAttributes> CREATOR =
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index d4fb1be56890..477519c0da32 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -351,6 +351,14 @@ public final class AudioDeviceInfo {
}
/**
+ * @hide
+ * @return the internal device tyoe
+ */
+ public int getInternalType() {
+ return mPort.type();
+ }
+
+ /**
* @return The internal device ID.
*/
public int getId() {
@@ -513,10 +521,21 @@ public final class AudioDeviceInfo {
return INT_TO_EXT_DEVICE_MAPPING.get(intDevice, TYPE_UNKNOWN);
}
+ /** @hide */
+ public static int convertDeviceTypeToInternalInputDevice(int deviceType) {
+ return EXT_TO_INT_INPUT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE);
+ }
+
private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING;
private static final SparseIntArray EXT_TO_INT_DEVICE_MAPPING;
+ /**
+ * EXT_TO_INT_INPUT_DEVICE_MAPPING aims at mapping external device type to internal input device
+ * type.
+ */
+ private static final SparseIntArray EXT_TO_INT_INPUT_DEVICE_MAPPING;
+
static {
INT_TO_EXT_DEVICE_MAPPING = new SparseIntArray();
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_EARPIECE, TYPE_BUILTIN_EARPIECE);
@@ -601,6 +620,32 @@ public final class AudioDeviceInfo {
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_OUT_REMOTE_SUBMIX);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_SPEAKER, AudioSystem.DEVICE_OUT_BLE_SPEAKER);
+
+ // privileges mapping to input device
+ EXT_TO_INT_INPUT_DEVICE_MAPPING = new SparseIntArray();
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BUILTIN_MIC, AudioSystem.DEVICE_IN_BUILTIN_MIC);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+ TYPE_BLUETOOTH_SCO, AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+ TYPE_WIRED_HEADSET, AudioSystem.DEVICE_IN_WIRED_HEADSET);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_HDMI, AudioSystem.DEVICE_IN_HDMI);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_TELEPHONY, AudioSystem.DEVICE_IN_TELEPHONY_RX);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_DOCK, AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+ TYPE_USB_ACCESSORY, AudioSystem.DEVICE_IN_USB_ACCESSORY);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_USB_DEVICE, AudioSystem.DEVICE_IN_USB_DEVICE);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_USB_HEADSET, AudioSystem.DEVICE_IN_USB_HEADSET);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_FM_TUNER, AudioSystem.DEVICE_IN_FM_TUNER);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_TV_TUNER, AudioSystem.DEVICE_IN_TV_TUNER);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_LINE_ANALOG, AudioSystem.DEVICE_IN_LINE);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_LINE_DIGITAL, AudioSystem.DEVICE_IN_SPDIF);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+ TYPE_BLUETOOTH_A2DP, AudioSystem.DEVICE_IN_BLUETOOTH_A2DP);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_IP, AudioSystem.DEVICE_IN_IP);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BUS, AudioSystem.DEVICE_IN_BUS);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+ TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_IN_REMOTE_SUBMIX);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_IN_BLE_HEADSET);
}
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a16e063fe969..aa2ff17a307b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1949,6 +1949,349 @@ public class AudioManager {
}
//====================================================================
+ // Audio Capture Preset routing
+
+ /**
+ * @hide
+ * Set the preferred device for a given capture preset, i.e. the audio routing to be used by
+ * this capture preset. Note that the device may not be available at the time the preferred
+ * device is set, but it will be used once made available.
+ * <p>Use {@link #clearPreferredDevicesForCapturePreset(int)} to cancel setting this preference
+ * for this capture preset.</p>
+ * @param capturePreset the audio capture preset whose routing will be affected
+ * @param device the audio device to route to when available
+ * @return true if the operation was successful, false otherwise
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean setPreferredDeviceForCapturePreset(int capturePreset,
+ @NonNull AudioDeviceAttributes device) {
+ return setPreferredDevicesForCapturePreset(capturePreset, Arrays.asList(device));
+ }
+
+ /**
+ * @hide
+ * Remove all the preferred audio devices previously set
+ * @param capturePreset the audio capture preset whose routing will be affected
+ * @return true if the operation was successful, false otherwise (invalid capture preset, or no
+ * device set for example)
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean clearPreferredDevicesForCapturePreset(int capturePreset) {
+ if (!MediaRecorder.isValidAudioSource(capturePreset)) {
+ return false;
+ }
+ try {
+ final int status = getService().clearPreferredDevicesForCapturePreset(capturePreset);
+ return status == AudioSystem.SUCCESS;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Return the preferred devices for an audio capture preset, previously set with
+ * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)}
+ * @param capturePreset the capture preset to query
+ * @return a list that contains preferred devices for that capture preset.
+ */
+ @NonNull
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
+ if (!MediaRecorder.isValidAudioSource(capturePreset)) {
+ return new ArrayList<AudioDeviceAttributes>();
+ }
+ try {
+ return getService().getPreferredDevicesForCapturePreset(capturePreset);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private boolean setPreferredDevicesForCapturePreset(
+ int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+ Objects.requireNonNull(devices);
+ if (!MediaRecorder.isValidAudioSource(capturePreset)) {
+ return false;
+ }
+ if (devices.size() != 1) {
+ throw new IllegalArgumentException(
+ "Only support setting one preferred devices for capture preset");
+ }
+ for (AudioDeviceAttributes device : devices) {
+ Objects.requireNonNull(device);
+ }
+ try {
+ final int status =
+ getService().setPreferredDevicesForCapturePreset(capturePreset, devices);
+ return status == AudioSystem.SUCCESS;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Interface to be notified of changes in the preferred audio devices set for a given capture
+ * preset.
+ * <p>Note that this listener will only be invoked whenever
+ * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)} or
+ * {@link #clearPreferredDevicesForCapturePreset(int)} causes a change in
+ * preferred device. It will not be invoked directly after registration with
+ * {@link #addOnPreferredDevicesForCapturePresetChangedListener(
+ * Executor, OnPreferredDevicesForCapturePresetChangedListener)}
+ * to indicate which strategies had preferred devices at the time of registration.</p>
+ * @see #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
+ * @see #clearPreferredDevicesForCapturePreset(int)
+ * @see #getPreferredDevicesForCapturePreset(int)
+ */
+ @SystemApi
+ public interface OnPreferredDevicesForCapturePresetChangedListener {
+ /**
+ * Called on the listener to indicate that the preferred audio devices for the given
+ * capture preset has changed.
+ * @param capturePreset the capture preset whose preferred device changed
+ * @param devices a list of newly set preferred audio devices
+ */
+ void onPreferredDevicesForCapturePresetChanged(
+ int capturePreset, @NonNull List<AudioDeviceAttributes> devices);
+ }
+
+ /**
+ * @hide
+ * Adds a listener for being notified of changes to the capture-preset-preferred audio device.
+ * @param executor
+ * @param listener
+ * @throws SecurityException if the caller doesn't hold the required permission
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void addOnPreferredDevicesForCapturePresetChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnPreferredDevicesForCapturePresetChangedListener listener)
+ throws SecurityException {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+ int status = addOnDevRoleForCapturePresetChangedListener(
+ executor, listener, AudioSystem.DEVICE_ROLE_PREFERRED);
+ if (status == AudioSystem.ERROR) {
+ // This must not happen
+ throw new RuntimeException("Unknown error happened");
+ }
+ if (status == AudioSystem.BAD_VALUE) {
+ throw new IllegalArgumentException(
+ "attempt to call addOnPreferredDevicesForCapturePresetChangedListener() "
+ + "on a previously registered listener");
+ }
+ }
+
+ /**
+ * @hide
+ * Removes a previously added listener of changes to the capture-preset-preferred audio device.
+ * @param listener
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void removeOnPreferredDevicesForCapturePresetChangedListener(
+ @NonNull OnPreferredDevicesForCapturePresetChangedListener listener) {
+ Objects.requireNonNull(listener);
+ int status = removeOnDevRoleForCapturePresetChangedListener(
+ listener, AudioSystem.DEVICE_ROLE_PREFERRED);
+ if (status == AudioSystem.ERROR) {
+ // This must not happen
+ throw new RuntimeException("Unknown error happened");
+ }
+ if (status == AudioSystem.BAD_VALUE) {
+ throw new IllegalArgumentException(
+ "attempt to call removeOnPreferredDevicesForCapturePresetChangedListener() "
+ + "on an unregistered listener");
+ }
+ }
+
+ private <T> int addOnDevRoleForCapturePresetChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull T listener, int deviceRole) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+ DevRoleListeners<T> devRoleListeners =
+ (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
+ if (devRoleListeners == null) {
+ return AudioSystem.ERROR;
+ }
+ synchronized (devRoleListeners.mDevRoleListenersLock) {
+ if (devRoleListeners.hasDevRoleListener(listener)) {
+ return AudioSystem.BAD_VALUE;
+ }
+ // lazy initialization of the list of device role listener
+ if (devRoleListeners.mListenerInfos == null) {
+ devRoleListeners.mListenerInfos = new ArrayList<>();
+ }
+ final int oldCbCount = devRoleListeners.mListenerInfos.size();
+ devRoleListeners.mListenerInfos.add(new DevRoleListenerInfo<T>(executor, listener));
+ if (oldCbCount == 0 && devRoleListeners.mListenerInfos.size() > 0) {
+ // register binder for callbacks
+ synchronized (mDevRoleForCapturePresetListenersLock) {
+ int deviceRoleListenerStatus = mDeviceRoleListenersStatus;
+ mDeviceRoleListenersStatus |= (1 << deviceRole);
+ if (deviceRoleListenerStatus != 0) {
+ // There are already device role changed listeners active.
+ return AudioSystem.SUCCESS;
+ }
+ if (mDevicesRoleForCapturePresetDispatcherStub == null) {
+ mDevicesRoleForCapturePresetDispatcherStub =
+ new CapturePresetDevicesRoleDispatcherStub();
+ }
+ try {
+ getService().registerCapturePresetDevicesRoleDispatcher(
+ mDevicesRoleForCapturePresetDispatcherStub);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+ return AudioSystem.SUCCESS;
+ }
+
+ private <T> int removeOnDevRoleForCapturePresetChangedListener(
+ @NonNull T listener, int deviceRole) {
+ Objects.requireNonNull(listener);
+ DevRoleListeners<T> devRoleListeners =
+ (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
+ if (devRoleListeners == null) {
+ return AudioSystem.ERROR;
+ }
+ synchronized (devRoleListeners.mDevRoleListenersLock) {
+ if (!devRoleListeners.removeDevRoleListener(listener)) {
+ return AudioSystem.BAD_VALUE;
+ }
+ if (devRoleListeners.mListenerInfos.size() == 0) {
+ // unregister binder for callbacks
+ synchronized (mDevRoleForCapturePresetListenersLock) {
+ mDeviceRoleListenersStatus ^= (1 << deviceRole);
+ if (mDeviceRoleListenersStatus != 0) {
+ // There are some other device role changed listeners active.
+ return AudioSystem.SUCCESS;
+ }
+ try {
+ getService().unregisterCapturePresetDevicesRoleDispatcher(
+ mDevicesRoleForCapturePresetDispatcherStub);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+ return AudioSystem.SUCCESS;
+ }
+
+ private final Map<Integer, Object> mDevRoleForCapturePresetListeners = new HashMap<>(){{
+ put(AudioSystem.DEVICE_ROLE_PREFERRED,
+ new DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>());
+ }};
+
+ private class DevRoleListenerInfo<T> {
+ final @NonNull Executor mExecutor;
+ final @NonNull T mListener;
+ DevRoleListenerInfo(Executor executor, T listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+ }
+
+ private class DevRoleListeners<T> {
+ private final Object mDevRoleListenersLock = new Object();
+ @GuardedBy("mDevRoleListenersLock")
+ private @Nullable ArrayList<DevRoleListenerInfo<T>> mListenerInfos;
+
+ @GuardedBy("mDevRoleListenersLock")
+ private @Nullable DevRoleListenerInfo<T> getDevRoleListenerInfo(T listener) {
+ if (mListenerInfos == null) {
+ return null;
+ }
+ for (DevRoleListenerInfo<T> listenerInfo : mListenerInfos) {
+ if (listenerInfo.mListener == listener) {
+ return listenerInfo;
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy("mDevRoleListenersLock")
+ private boolean hasDevRoleListener(T listener) {
+ return getDevRoleListenerInfo(listener) != null;
+ }
+
+ @GuardedBy("mDevRoleListenersLock")
+ private boolean removeDevRoleListener(T listener) {
+ final DevRoleListenerInfo<T> infoToRemove = getDevRoleListenerInfo(listener);
+ if (infoToRemove != null) {
+ mListenerInfos.remove(infoToRemove);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private final Object mDevRoleForCapturePresetListenersLock = new Object();
+ /**
+ * Record if there is a listener added for device role change. If there is a listener added for
+ * a specified device role change, the bit at position `1 << device_role` is set.
+ */
+ @GuardedBy("mDevRoleForCapturePresetListenersLock")
+ private int mDeviceRoleListenersStatus = 0;
+ @GuardedBy("mDevRoleForCapturePresetListenersLock")
+ private CapturePresetDevicesRoleDispatcherStub mDevicesRoleForCapturePresetDispatcherStub;
+
+ private final class CapturePresetDevicesRoleDispatcherStub
+ extends ICapturePresetDevicesRoleDispatcher.Stub {
+
+ @Override
+ public void dispatchDevicesRoleChanged(
+ int capturePreset, int role, List<AudioDeviceAttributes> devices) {
+ final Object listenersObj = mDevRoleForCapturePresetListeners.get(role);
+ if (listenersObj == null) {
+ return;
+ }
+ switch (role) {
+ case AudioSystem.DEVICE_ROLE_PREFERRED: {
+ final DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>
+ listeners =
+ (DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>)
+ listenersObj;
+ final ArrayList<DevRoleListenerInfo<
+ OnPreferredDevicesForCapturePresetChangedListener>> prefDevListeners;
+ synchronized (listeners.mDevRoleListenersLock) {
+ if (listeners.mListenerInfos.isEmpty()) {
+ return;
+ }
+ prefDevListeners = (ArrayList<DevRoleListenerInfo<
+ OnPreferredDevicesForCapturePresetChangedListener>>)
+ listeners.mListenerInfos.clone();
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ for (DevRoleListenerInfo<
+ OnPreferredDevicesForCapturePresetChangedListener> info :
+ prefDevListeners) {
+ info.mExecutor.execute(() ->
+ info.mListener.onPreferredDevicesForCapturePresetChanged(
+ capturePreset, devices));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ }
+
+ //====================================================================
// Offload query
/**
* Returns whether offloaded playback of an audio format is supported on the device.
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 22f625004aaf..279ba0a55be0 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -27,6 +27,7 @@ import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix;
import android.telephony.TelephonyManager;
import android.util.Log;
+import android.util.Pair;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1755,6 +1756,134 @@ public class AudioSystem
public static native int getDevicesForRoleAndStrategy(
int strategy, int role, @NonNull List<AudioDeviceAttributes> devices);
+ // use case routing by capture preset
+
+ private static Pair<int[], String[]> populateInputDevicesTypeAndAddress(
+ @NonNull List<AudioDeviceAttributes> devices) {
+ int[] types = new int[devices.size()];
+ String[] addresses = new String[devices.size()];
+ for (int i = 0; i < devices.size(); ++i) {
+ types[i] = devices.get(i).getInternalType();
+ if (types[i] == AudioSystem.DEVICE_NONE) {
+ types[i] = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(
+ devices.get(i).getType());
+ }
+ addresses[i] = devices.get(i).getAddress();
+ }
+ return new Pair<int[], String[]>(types, addresses);
+ }
+
+ /**
+ * @hide
+ * Set devices as role for capture preset.
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param devices the list of devices to be set as role for the given capture preset
+ * @return {@link #SUCCESS} if successfully set
+ */
+ public static int setDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ if (devices.isEmpty()) {
+ return BAD_VALUE;
+ }
+ Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
+ return setDevicesRoleForCapturePreset(
+ capturePreset, role, typeAddresses.first, typeAddresses.second);
+ }
+
+ /**
+ * @hide
+ * Set devices as role for capture preset.
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param types all device types
+ * @param addresses all device addresses
+ * @return {@link #SUCCESS} if successfully set
+ */
+ private static native int setDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+ /**
+ * @hide
+ * Add devices as role for capture preset.
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param devices the list of devices to be added as role for the given capture preset
+ * @return {@link #SUCCESS} if successfully add
+ */
+ public static int addDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ if (devices.isEmpty()) {
+ return BAD_VALUE;
+ }
+ Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
+ return addDevicesRoleForCapturePreset(
+ capturePreset, role, typeAddresses.first, typeAddresses.second);
+ }
+
+ /**
+ * @hide
+ * Add devices as role for capture preset.
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param types all device types
+ * @param addresses all device addresses
+ * @return {@link #SUCCESS} if successfully set
+ */
+ private static native int addDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+ /**
+ * @hide
+ * Remove devices as role for the capture preset
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param devices the devices to be removed
+ * @return {@link #SUCCESS} if successfully removed
+ */
+ public static int removeDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ if (devices.isEmpty()) {
+ return BAD_VALUE;
+ }
+ Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
+ return removeDevicesRoleForCapturePreset(
+ capturePreset, role, typeAddresses.first, typeAddresses.second);
+ }
+
+ /**
+ * @hide
+ * Remove devices as role for capture preset.
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param types all device types
+ * @param addresses all device addresses
+ * @return {@link #SUCCESS} if successfully set
+ */
+ private static native int removeDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+ /**
+ * @hide
+ * Remove all devices as role for the capture preset
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @return {@link #SUCCESS} if successfully removed
+ */
+ public static native int clearDevicesRoleForCapturePreset(int capturePreset, int role);
+
+ /**
+ * @hide
+ * Query previously set devices as role for a capture preset
+ * @param capturePreset the capture preset to query for
+ * @param role the role of the devices
+ * @param devices a list that will contain the devices of role
+ * @return {@link #SUCCESS} if there is a preferred device and it was successfully retrieved
+ * and written to the array
+ */
+ public static native int getDevicesForRoleAndCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices);
+
// Items shared with audio service
/**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index ef8b0edb1fe5..85fb67df82d4 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -26,6 +26,7 @@ import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
import android.media.IAudioServerStateDispatcher;
+import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
@@ -307,4 +308,16 @@ interface IAudioService {
// code via IAudioManager.h need to be added to the top section.
oneway void setMultiAudioFocusEnabled(in boolean enabled);
+
+ int setPreferredDevicesForCapturePreset(
+ in int capturePreset, in List<AudioDeviceAttributes> devices);
+
+ int clearPreferredDevicesForCapturePreset(in int capturePreset);
+
+ List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(in int capturePreset);
+
+ void registerCapturePresetDevicesRoleDispatcher(ICapturePresetDevicesRoleDispatcher dispatcher);
+
+ oneway void unregisterCapturePresetDevicesRoleDispatcher(
+ ICapturePresetDevicesRoleDispatcher dispatcher);
}
diff --git a/media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl b/media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl
new file mode 100644
index 000000000000..5e03e632c4ff
--- /dev/null
+++ b/media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.media;
+
+import android.media.AudioDeviceAttributes;
+
+/**
+ * AIDL for AudioService to signal devices role for capture preset updates.
+ *
+ * {@hide}
+ */
+oneway interface ICapturePresetDevicesRoleDispatcher {
+
+ void dispatchDevicesRoleChanged(
+ int capturePreset, int role, in List<AudioDeviceAttributes> devices);
+
+}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 4198d7917932..1db02beaea1a 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -404,6 +404,32 @@ public class MediaRecorder implements AudioRouting,
}
}
+ /**
+ * @hide
+ * @param source An audio source to test
+ * @return true if the source is a valid one
+ */
+ public static boolean isValidAudioSource(int source) {
+ switch(source) {
+ case AudioSource.MIC:
+ case AudioSource.VOICE_UPLINK:
+ case AudioSource.VOICE_DOWNLINK:
+ case AudioSource.VOICE_CALL:
+ case AudioSource.CAMCORDER:
+ case AudioSource.VOICE_RECOGNITION:
+ case AudioSource.VOICE_COMMUNICATION:
+ case AudioSource.REMOTE_SUBMIX:
+ case AudioSource.UNPROCESSED:
+ case AudioSource.VOICE_PERFORMANCE:
+ case AudioSource.ECHO_REFERENCE:
+ case AudioSource.RADIO_TUNER:
+ case AudioSource.HOTWORD:
+ return true;
+ default:
+ return false;
+ }
+ }
+
/** @hide */
public static final String toLogFriendlyAudioSource(int source) {
switch(source) {
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index 1c5288b04685..451677f6a8bc 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -27,6 +27,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.net.Uri;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -814,6 +815,141 @@ public final class MediaTranscodeManager {
return new TranscodingRequest(this);
}
}
+
+ /**
+ * Helper class for deciding if transcoding is needed, and if so, the track
+ * formats to use.
+ */
+ public static class MediaFormatResolver {
+ private static final int BIT_RATE = 20000000; // 20Mbps
+
+ private MediaFormat mSrcVideoFormatHint;
+ private MediaFormat mSrcAudioFormatHint;
+ private Bundle mClientCaps;
+
+ /**
+ * A key describing whether the client supports HEVC-encoded video.
+ *
+ * The value associated with this key is a boolean. If unspecified, it's up to
+ * the MediaFormatResolver to determine the default.
+ *
+ * @see #setClientCapabilities(Bundle)
+ */
+ public static final String CAPS_SUPPORTS_HEVC = "support-hevc";
+
+ /**
+ * Sets the abilities of the client consuming the media. Must be called
+ * before {@link #shouldTranscode()} or {@link #resolveVideoFormat()}.
+ *
+ * @param clientCaps A Bundle object containing the client's capabilities, such as
+ * {@link #CAPS_SUPPORTS_HEVC}.
+ * @return the same VideoFormatResolver instance.
+ * @hide
+ */
+ @NonNull
+ public MediaFormatResolver setClientCapabilities(@NonNull Bundle clientCaps) {
+ mClientCaps = clientCaps;
+ return this;
+ }
+
+ /**
+ * Sets the video format hint about the source. Must be called before
+ * {@link #shouldTranscode()} or {@link #resolveVideoFormat()}.
+ *
+ * @param format A MediaFormat object containing information about the source's
+ * video track format that could affect the transcoding decision.
+ * Such information could include video codec types, color spaces,
+ * whether special format info (eg. slow-motion markers) are present,
+ * etc.. If a particular information is not present, it will not be
+ * used to make the decision.
+ * @return the same MediaFormatResolver instance.
+ */
+ @NonNull
+ public MediaFormatResolver setSourceVideoFormatHint(@NonNull MediaFormat format) {
+ mSrcVideoFormatHint = format;
+ return this;
+ }
+
+ /**
+ * Sets the audio format hint about the source.
+ *
+ * @param format A MediaFormat object containing information about the source's
+ * audio track format that could affect the transcoding decision.
+ * @return the same MediaFormatResolver instance.
+ * @hide
+ */
+ @NonNull
+ public MediaFormatResolver setSourceAudioFormatHint(@NonNull MediaFormat format) {
+ mSrcAudioFormatHint = format;
+ return this;
+ }
+
+ /**
+ * Returns whether the source content should be transcoded.
+ *
+ * @return true if the source should be transcoded.
+ * @throws UnsupportedOperationException if {@link #setClientCapabilities(Bundle)}
+ * or {@link #setSourceVideoFormatHint(MediaFormat)} was not called.
+ */
+ public boolean shouldTranscode() {
+ if (mClientCaps == null) {
+ throw new UnsupportedOperationException(
+ "Client caps must be set!");
+ }
+ // Video src hint must be provided, audio src hint is not used right now.
+ if (mSrcVideoFormatHint == null) {
+ throw new UnsupportedOperationException(
+ "Source video format hint must be set!");
+ }
+ boolean supportHevc = mClientCaps.getBoolean(CAPS_SUPPORTS_HEVC, false);
+ if (!supportHevc && MediaFormat.MIMETYPE_VIDEO_HEVC.equals(
+ mSrcVideoFormatHint.getString(MediaFormat.KEY_MIME))) {
+ return true;
+ }
+ // TODO: add more checks as needed below.
+ return false;
+ }
+
+ /**
+ * Retrieves the video track format to be used on
+ * {@link Builder#setVideoTrackFormat(MediaFormat)} for this configuration.
+ *
+ * @return the video track format to be used if transcoding should be performed,
+ * and null otherwise.
+ * @throws UnsupportedOperationException if {@link #setClientCapabilities(Bundle)}
+ * or {@link #setSourceVideoFormatHint(MediaFormat)} was not called.
+ */
+ @Nullable
+ public MediaFormat resolveVideoFormat() {
+ if (!shouldTranscode()) {
+ return null;
+ }
+ // TODO(hkuang): Only modified the video codec type, and use fixed bitrate for now.
+ // May switch to transcoding profile when it's available.
+ MediaFormat videoTrackFormat = new MediaFormat(mSrcVideoFormatHint);
+ videoTrackFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC);
+ videoTrackFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
+ return videoTrackFormat;
+ }
+
+ /**
+ * Retrieves the audio track format to be used for transcoding.
+ *
+ * @return the audio track format to be used if transcoding should be performed, and
+ * null otherwise.
+ * @throws UnsupportedOperationException if {@link #setClientCapabilities(Bundle)}
+ * or {@link #setSourceVideoFormatHint(MediaFormat)} was not called.
+ * @hide
+ */
+ @Nullable
+ public MediaFormat resolveAudioFormat() {
+ if (!shouldTranscode()) {
+ return null;
+ }
+ // Audio transcoding is not supported yet, always return null.
+ return null;
+ }
+ }
}
/**
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
index 1a3e3608f37f..33d6d64c7f37 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
@@ -24,6 +24,7 @@ import android.media.MediaFormat;
import android.media.MediaTranscodeManager;
import android.media.MediaTranscodeManager.TranscodingJob;
import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver;
import android.media.TranscodingTestConfig;
import android.net.Uri;
import android.os.Bundle;
@@ -414,17 +415,27 @@ public class MediaTranscodeManagerTest
Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+ mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4");
+ Bundle clientCaps = new Bundle();
+ clientCaps.putBoolean(MediaFormatResolver.CAPS_SUPPORTS_HEVC, false);
+ MediaFormatResolver resolver = new MediaFormatResolver()
+ .setSourceVideoFormatHint(MediaFormat.createVideoFormat(
+ MediaFormat.MIMETYPE_VIDEO_HEVC, WIDTH, HEIGHT))
+ .setClientCapabilities(clientCaps);
+ assertTrue(resolver.shouldTranscode());
+ MediaFormat videoTrackFormat = resolver.resolveVideoFormat();
+ assertNotNull(videoTrackFormat);
+
TranscodingRequest request =
new TranscodingRequest.Builder()
.setSourceUri(mSourceHEVCVideoUri)
.setDestinationUri(destinationUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
.setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
- .setVideoTrackFormat(createMediaFormat())
+ .setVideoTrackFormat(videoTrackFormat)
.build();
Executor listenerExecutor = Executors.newSingleThreadExecutor();
- Log.i(TAG, "transcoding to " + createMediaFormat());
+ Log.i(TAG, "transcoding to " + videoTrackFormat);
TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
transcodingJob -> {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index cc6c3dc97acf..ff3999f090e0 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -4139,8 +4139,10 @@ package android.media {
public class AudioManager {
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
method public void clearAudioServerStateCallback();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
@@ -4151,6 +4153,7 @@ package android.media {
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
@@ -4159,6 +4162,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
@@ -4168,6 +4172,7 @@ package android.media {
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
@@ -4197,6 +4202,10 @@ package android.media {
method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
}
+ public static interface AudioManager.OnPreferredDevicesForCapturePresetChangedListener {
+ method public void onPreferredDevicesForCapturePresetChanged(int, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
+ }
+
public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener {
method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
}
@@ -4325,6 +4334,14 @@ package android.media {
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat);
}
+ public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver {
+ ctor public MediaTranscodeManager.TranscodingRequest.MediaFormatResolver();
+ method @Nullable public android.media.MediaFormat resolveVideoFormat();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver setSourceVideoFormatHint(@NonNull android.media.MediaFormat);
+ method public boolean shouldTranscode();
+ field public static final String CAPS_SUPPORTS_HEVC = "support-hevc";
+ }
+
public class PlayerProxy {
method public void pause();
method public void setPan(float);
@@ -6864,7 +6881,7 @@ package android.net.wifi.nl80211 {
method @NonNull public java.util.List<android.net.wifi.nl80211.NativeScanResult> getScanResults(@NonNull String, int);
method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
method @Nullable public static android.net.wifi.nl80211.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
- method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
+ method @Deprecated public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SendMgmtFrameCallback);
method public void setOnServiceDeadCallback(@NonNull Runnable);
method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback);
@@ -6915,10 +6932,10 @@ package android.net.wifi.nl80211 {
field public final int txBitrateMbps;
}
- public static interface WifiNl80211Manager.SoftApCallback {
- method public void onConnectedClientsChanged(@NonNull android.net.wifi.nl80211.NativeWifiClient, boolean);
- method public void onFailure();
- method public void onSoftApChannelSwitched(int, int);
+ @Deprecated public static interface WifiNl80211Manager.SoftApCallback {
+ method @Deprecated public void onConnectedClientsChanged(@NonNull android.net.wifi.nl80211.NativeWifiClient, boolean);
+ method @Deprecated public void onFailure();
+ method @Deprecated public void onSoftApChannelSwitched(int, int);
}
public static class WifiNl80211Manager.TxPacketCounters {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
index 2b1fce8a4cf5..ffde84128549 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
@@ -41,16 +41,6 @@ public class InputChannelCompat {
}
/**
- * Creates a dispatcher from the extras received as part on onInitialize
- */
- public static InputEventReceiver fromBundle(Bundle params, String key,
- Looper looper, Choreographer choreographer, InputEventListener listener) {
-
- InputChannel channel = params.getParcelable(key);
- return new InputEventReceiver(channel, looper, choreographer, listener);
- }
-
- /**
* Version of addBatch method which preserves time accuracy in nanoseconds instead of
* converting the time to milliseconds.
* @param src old MotionEvent where the target should be appended
@@ -69,11 +59,9 @@ public class InputChannelCompat {
public static class InputEventReceiver {
private final BatchedInputEventReceiver mReceiver;
- private final InputChannel mInputChannel;
public InputEventReceiver(InputChannel inputChannel, Looper looper,
Choreographer choreographer, final InputEventListener listener) {
- mInputChannel = inputChannel;
mReceiver = new BatchedInputEventReceiver(inputChannel, looper, choreographer) {
@Override
@@ -85,40 +73,17 @@ public class InputChannelCompat {
}
/**
- * @see BatchedInputEventReceiver#dispose()
+ * @see BatchedInputEventReceiver#setBatchingEnabled()
*/
- public void dispose() {
- mReceiver.dispose();
- mInputChannel.dispose();
- }
- }
-
- /**
- * @see InputEventSender
- */
- public static class InputEventDispatcher {
-
- private final InputChannel mInputChannel;
- private final InputEventSender mSender;
-
- public InputEventDispatcher(InputChannel inputChannel, Looper looper) {
- mInputChannel = inputChannel;
- mSender = new InputEventSender(inputChannel, looper) { };
- }
-
- /**
- * @see InputEventSender#sendInputEvent(int, InputEvent)
- */
- public void dispatch(InputEvent ev) {
- mSender.sendInputEvent(ev.getSequenceNumber(), ev);
+ public void setBatchingEnabled(boolean batchingEnabled) {
+ mReceiver.setBatchingEnabled(batchingEnabled);
}
/**
- * @see InputEventSender#dispose()
+ * @see BatchedInputEventReceiver#dispose()
*/
public void dispose() {
- mSender.dispose();
- mInputChannel.dispose();
+ mReceiver.dispose();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 27863ba6c857..62bc425b2bd2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -694,6 +694,7 @@ class Bubble implements BubbleViewProvider {
pw.print(" showInShade: "); pw.println(showInShade());
pw.print(" showDot: "); pw.println(showDot());
pw.print(" showFlyout: "); pw.println(showFlyout());
+ pw.print(" lastActivity: "); pw.println(getLastActivity());
pw.print(" desiredHeight: "); pw.println(getDesiredHeightString());
pw.print(" suppressNotif: "); pw.println(shouldSuppressNotification());
pw.print(" autoExpand: "); pw.println(shouldAutoExpand());
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index c81b7cefbbd7..4b4e275e5cbd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -196,7 +196,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private INotificationManager mINotificationManager;
// Callback that updates BubbleOverflowActivity on data change.
- @Nullable private Runnable mOverflowCallback = null;
+ @Nullable private BubbleData.Listener mOverflowListener = null;
// Only load overflow data from disk once
private boolean mOverflowDataLoaded = false;
@@ -722,8 +722,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mInflateSynchronously = inflateSynchronously;
}
- void setOverflowCallback(Runnable updateOverflow) {
- mOverflowCallback = updateOverflow;
+ void setOverflowListener(BubbleData.Listener listener) {
+ mOverflowListener = listener;
}
/**
@@ -1327,9 +1327,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Lazy load overflow bubbles from disk
loadOverflowBubblesFromDisk();
+
// Update bubbles in overflow.
- if (mOverflowCallback != null) {
- mOverflowCallback.run();
+ if (mOverflowListener != null) {
+ mOverflowListener.applyUpdate(update);
}
// Collapsing? Do this first before remaining steps.
@@ -1438,21 +1439,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
cb.invalidateNotifications("BubbleData.Listener.applyUpdate");
}
updateStack();
-
- if (DEBUG_BUBBLE_CONTROLLER) {
- Log.d(TAG, "\n[BubbleData] bubbles:");
- Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getBubbles(),
- mBubbleData.getSelectedBubble()));
-
- if (mStackView != null) {
- Log.d(TAG, "\n[BubbleStackView]");
- Log.d(TAG, BubbleDebugConfig.formatBubblesString(mStackView.getBubblesOnScreen(),
- mStackView.getExpandedBubble()));
- }
- Log.d(TAG, "\n[BubbleData] overflow:");
- Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getOverflowBubbles(),
- null) + "\n");
- }
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 5c6d16d4bbee..a747db680b2e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -75,6 +75,8 @@ public class BubbleData {
@Nullable Bubble selectedBubble;
@Nullable Bubble addedBubble;
@Nullable Bubble updatedBubble;
+ @Nullable Bubble addedOverflowBubble;
+ @Nullable Bubble removedOverflowBubble;
// Pair with Bubble and @DismissReason Integer
final List<Pair<Bubble, Integer>> removedBubbles = new ArrayList<>();
@@ -93,10 +95,12 @@ public class BubbleData {
|| addedBubble != null
|| updatedBubble != null
|| !removedBubbles.isEmpty()
+ || addedOverflowBubble != null
+ || removedOverflowBubble != null
|| orderChanged;
}
- void bubbleRemoved(Bubble bubbleToRemove, @DismissReason int reason) {
+ void bubbleRemoved(Bubble bubbleToRemove, @DismissReason int reason) {
removedBubbles.add(new Pair<>(bubbleToRemove, reason));
}
}
@@ -486,8 +490,9 @@ public class BubbleData {
b.stopInflation();
}
mLogger.logOverflowRemove(b, reason);
- mStateChange.bubbleRemoved(b, reason);
mOverflowBubbles.remove(b);
+ mStateChange.bubbleRemoved(b, reason);
+ mStateChange.removedOverflowBubble = b;
}
return;
}
@@ -532,6 +537,7 @@ public class BubbleData {
}
mLogger.logOverflowAdd(bubble, reason);
mOverflowBubbles.add(0, bubble);
+ mStateChange.addedOverflowBubble = bubble;
bubble.stopInflation();
if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) {
// Remove oldest bubble.
@@ -542,6 +548,7 @@ public class BubbleData {
mStateChange.bubbleRemoved(oldest, BubbleController.DISMISS_OVERFLOW_MAX_REACHED);
mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_MAX_REACHED);
mOverflowBubbles.remove(oldest);
+ mStateChange.removedOverflowBubble = oldest;
}
}
@@ -821,11 +828,19 @@ public class BubbleData {
: "null");
pw.print("expanded: ");
pw.println(mExpanded);
- pw.print("count: ");
+
+ pw.print("stack bubble count: ");
pw.println(mBubbles.size());
for (Bubble bubble : mBubbles) {
bubble.dump(fd, pw, args);
}
+
+ pw.print("overflow bubble count: ");
+ pw.println(mOverflowBubbles.size());
+ for (Bubble bubble : mOverflowBubbles) {
+ bubble.dump(fd, pw, args);
+ }
+
pw.print("summaryKeys: ");
pw.println(mSuppressedGroupKeys.size());
for (String key : mSuppressedGroupKeys.keySet()) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 9926f2ef9b64..160addc405fa 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -108,14 +108,10 @@ public class BubbleOverflowActivity extends Activity {
mEmptyStateSubtitle = findViewById(R.id.bubble_overflow_empty_subtitle);
mEmptyStateImage = findViewById(R.id.bubble_overflow_empty_state_image);
- updateDimensions();
- onDataChanged(mBubbleController.getOverflowBubbles());
- mBubbleController.setOverflowCallback(() -> {
- onDataChanged(mBubbleController.getOverflowBubbles());
- });
+ updateOverflow();
}
- void updateDimensions() {
+ void updateOverflow() {
Resources res = getResources();
final int columns = res.getInteger(R.integer.bubbles_overflow_columns);
mRecyclerView.setLayoutManager(
@@ -137,6 +133,22 @@ public class BubbleOverflowActivity extends Activity {
mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles,
mBubbleController::promoteBubbleFromOverflow, viewWidth, viewHeight);
mRecyclerView.setAdapter(mAdapter);
+
+ mOverflowBubbles.clear();
+ mOverflowBubbles.addAll(mBubbleController.getOverflowBubbles());
+ mAdapter.notifyDataSetChanged();
+ updateEmptyStateVisibility();
+
+ mBubbleController.setOverflowListener(mDataListener);
+ updateTheme();
+ }
+
+ void updateEmptyStateVisibility() {
+ if (mOverflowBubbles.isEmpty()) {
+ mEmptyState.setVisibility(View.VISIBLE);
+ } else {
+ mEmptyState.setVisibility(View.GONE);
+ }
}
/**
@@ -168,22 +180,40 @@ public class BubbleOverflowActivity extends Activity {
mEmptyStateSubtitle.setTextColor(textColor);
}
- void onDataChanged(List<Bubble> bubbles) {
- mOverflowBubbles.clear();
- mOverflowBubbles.addAll(bubbles);
- mAdapter.notifyDataSetChanged();
+ private final BubbleData.Listener mDataListener = new BubbleData.Listener() {
- if (mOverflowBubbles.isEmpty()) {
- mEmptyState.setVisibility(View.VISIBLE);
- } else {
- mEmptyState.setVisibility(View.GONE);
- }
+ @Override
+ public void applyUpdate(BubbleData.Update update) {
+
+ Bubble toRemove = update.removedOverflowBubble;
+ if (toRemove != null) {
+ if (DEBUG_OVERFLOW) {
+ Log.d(TAG, "remove: " + toRemove);
+ }
+ toRemove.cleanupViews();
+ final int i = mOverflowBubbles.indexOf(toRemove);
+ mOverflowBubbles.remove(toRemove);
+ mAdapter.notifyItemRemoved(i);
+ }
+
+ Bubble toAdd = update.addedOverflowBubble;
+ if (toAdd != null) {
+ if (DEBUG_OVERFLOW) {
+ Log.d(TAG, "add: " + toAdd);
+ }
+ mOverflowBubbles.add(0, toAdd);
+ mAdapter.notifyItemInserted(0);
+ }
+
+ updateEmptyStateVisibility();
- if (DEBUG_OVERFLOW) {
- Log.d(TAG, "Updated overflow bubbles:\n" + BubbleDebugConfig.formatBubblesString(
- mOverflowBubbles, /*selected*/ null));
+ if (DEBUG_OVERFLOW) {
+ Log.d(TAG, BubbleDebugConfig.formatBubblesString(
+ mBubbleController.getOverflowBubbles(),
+ null));
+ }
}
- }
+ };
@Override
public void onStart() {
@@ -198,8 +228,7 @@ public class BubbleOverflowActivity extends Activity {
@Override
public void onResume() {
super.onResume();
- updateDimensions();
- updateTheme();
+ updateOverflow();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 64df2b99ee22..c1b68824d20f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -276,6 +276,10 @@ public class BubbleStackView extends FrameLayout
/** Description of current animation controller state. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Stack view state:");
+
+ String bubblesOnScreen = BubbleDebugConfig.formatBubblesString(
+ getBubblesOnScreen(), getExpandedBubble());
+ pw.print(" bubbles on screen: "); pw.println(bubblesOnScreen);
pw.print(" gestureInProgress: "); pw.println(mIsGestureInProgress);
pw.print(" showingDismiss: "); pw.println(mDismissView.isShowing());
pw.print(" isExpansionAnimating: "); pw.println(mIsExpansionAnimating);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 92d2f421d6ee..dfc82f120c90 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -38,11 +38,11 @@ import android.provider.DeviceConfig;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
+import android.view.Choreographer;
import android.view.ISystemGestureExclusionListener;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
-import android.view.InputEventReceiver;
import android.view.InputMonitor;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -67,6 +67,7 @@ import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -169,7 +170,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
private boolean mGestureBlockingActivityRunning;
private InputMonitor mInputMonitor;
- private InputEventReceiver mInputEventReceiver;
+ private InputChannelCompat.InputEventReceiver mInputEventReceiver;
private NavigationEdgeBackPlugin mEdgeBackPlugin;
private int mLeftInset;
@@ -383,8 +384,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
// Register input event receiver
mInputMonitor = InputManager.getInstance().monitorGestureInput(
"edge-swipe", mDisplayId);
- mInputEventReceiver = new SysUiInputEventReceiver(
- mInputMonitor.getInputChannel(), Looper.getMainLooper());
+ mInputEventReceiver = new InputChannelCompat.InputEventReceiver(
+ mInputMonitor.getInputChannel(), Looper.getMainLooper(),
+ Choreographer.getInstance(), this::onInputEvent);
// Add a nav bar panel window
setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
@@ -520,6 +522,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
if (action == MotionEvent.ACTION_DOWN) {
// Verify if this is in within the touch region and we aren't in immersive mode, and
// either the bouncer is showing or the notification panel is hidden
+ mInputEventReceiver.setBatchingEnabled(false);
mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
mLogGesture = false;
mInRejectedExclusion = false;
@@ -571,6 +574,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mThresholdCrossed = true;
// Capture inputs
mInputMonitor.pilferPointers();
+ mInputEventReceiver.setBatchingEnabled(true);
} else {
logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_FAR_FROM_EDGE);
}
@@ -672,15 +676,4 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
}
proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
}
-
- class SysUiInputEventReceiver extends InputEventReceiver {
- SysUiInputEventReceiver(InputChannel channel, Looper looper) {
- super(channel, looper);
- }
-
- public void onInputEvent(InputEvent event) {
- EdgeBackGestureHandler.this.onInputEvent(event);
- finishInputEvent(event, true);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 8f3033edecbb..7bac007ae478 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -188,7 +188,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
viewState.openedAmount = openedAmount;
viewState.clipTopAmount = 0;
viewState.alpha = 1;
- viewState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0;
+ viewState.belowSpeedBump = mHostLayoutController.getSpeedBumpIndex() == 0;
viewState.hideSensitive = false;
viewState.xTranslation = getTranslationX();
if (mNotGoneIndex != -1) {
@@ -352,7 +352,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
setBackgroundTop(backgroundTop);
setFirstElementRoundness(firstElementRoundness);
- mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex());
+ mShelfIcons.setSpeedBumpIndex(mHostLayoutController.getSpeedBumpIndex());
mShelfIcons.calculateIconTranslations();
mShelfIcons.applyIconStates();
for (int i = 0; i < mHostLayoutController.getChildCount(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 90492b5d606d..fdfd72489e93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
@@ -459,8 +460,7 @@ public class NotifCollection implements Dumpable {
+ ": has not been marked for removal"));
}
- if (isDismissedByUser(entry)) {
- // User-dismissed notifications cannot be lifetime-extended
+ if (cannotBeLifetimeExtended(entry)) {
cancelLifetimeExtension(entry);
} else {
updateLifetimeExtension(entry);
@@ -583,7 +583,7 @@ public class NotifCollection implements Dumpable {
}
private void cancelLocalDismissal(NotificationEntry entry) {
- if (isDismissedByUser(entry)) {
+ if (entry.getDismissState() != NOT_DISMISSED) {
entry.setDismissState(NOT_DISMISSED);
if (entry.getSbn().getNotification().isGroupSummary()) {
for (NotificationEntry otherEntry : mNotificationSet.values()) {
@@ -669,12 +669,16 @@ public class NotifCollection implements Dumpable {
* immediately removed from the collection, but can sometimes stick around due to lifetime
* extenders.
*/
- private static boolean isCanceled(NotificationEntry entry) {
+ private boolean isCanceled(NotificationEntry entry) {
return entry.mCancellationReason != REASON_NOT_CANCELED;
}
- private static boolean isDismissedByUser(NotificationEntry entry) {
- return entry.getDismissState() != NOT_DISMISSED;
+ private boolean cannotBeLifetimeExtended(NotificationEntry entry) {
+ final boolean locallyDismissedByUser = entry.getDismissState() != NOT_DISMISSED;
+ final boolean systemServerReportedUserCancel =
+ entry.mCancellationReason == REASON_CLICK
+ || entry.mCancellationReason == REASON_CANCEL;
+ return locallyDismissedByUser || systemServerReportedUserCancel;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
index cce8cdc64d30..610cd33383e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
@@ -26,6 +26,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -38,17 +39,20 @@ public class OnUserInteractionCallbackImplLegacy implements OnUserInteractionCal
private final HeadsUpManager mHeadsUpManager;
private final StatusBarStateController mStatusBarStateController;
private final VisualStabilityManager mVisualStabilityManager;
+ private final GroupMembershipManager mGroupMembershipManager;
public OnUserInteractionCallbackImplLegacy(
NotificationEntryManager notificationEntryManager,
HeadsUpManager headsUpManager,
StatusBarStateController statusBarStateController,
- VisualStabilityManager visualStabilityManager
+ VisualStabilityManager visualStabilityManager,
+ GroupMembershipManager groupMembershipManager
) {
mNotificationEntryManager = notificationEntryManager;
mHeadsUpManager = headsUpManager;
mStatusBarStateController = statusBarStateController;
mVisualStabilityManager = visualStabilityManager;
+ mGroupMembershipManager = groupMembershipManager;
}
/**
@@ -69,6 +73,13 @@ public class OnUserInteractionCallbackImplLegacy implements OnUserInteractionCal
dismissalSurface = NotificationStats.DISMISSAL_AOD;
}
+ if (mGroupMembershipManager.isOnlyChildInGroup(entry)) {
+ NotificationEntry groupSummary = mGroupMembershipManager.getLogicalGroupSummary(entry);
+ if (groupSummary.isClearable()) {
+ onDismiss(groupSummary, cancellationReason);
+ }
+ }
+
mNotificationEntryManager.performRemoveNotification(
entry.getSbn(),
new DismissedByUserStats(
@@ -82,6 +93,7 @@ public class OnUserInteractionCallbackImplLegacy implements OnUserInteractionCal
NotificationLogger.getNotificationLocation(entry))),
cancellationReason
);
+
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index e2aae64ce220..ea86d25389fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -205,9 +205,11 @@ public interface NotificationsModule {
Context context,
NotificationGutsManager notificationGutsManager,
NotificationEntryManager notificationEntryManager,
- MetricsLogger metricsLogger) {
+ MetricsLogger metricsLogger,
+ GroupMembershipManager groupMembershipManager) {
return new NotificationBlockingHelperManager(
- context, notificationGutsManager, notificationEntryManager, metricsLogger);
+ context, notificationGutsManager, notificationEntryManager, metricsLogger,
+ groupMembershipManager);
}
/** Provides an instance of {@link GroupMembershipManager} */
@@ -273,7 +275,8 @@ public interface NotificationsModule {
Lazy<NotifCollection> notifCollection,
Lazy<VisualStabilityCoordinator> visualStabilityCoordinator,
NotificationEntryManager entryManager,
- VisualStabilityManager visualStabilityManager) {
+ VisualStabilityManager visualStabilityManager,
+ Lazy<GroupMembershipManager> groupMembershipManagerLazy) {
return featureFlags.isNewNotifPipelineRenderingEnabled()
? new OnUserInteractionCallbackImpl(
pipeline.get(),
@@ -285,7 +288,8 @@ public interface NotificationsModule {
entryManager,
headsUpManager,
statusBarStateController,
- visualStabilityManager);
+ visualStabilityManager,
+ groupMembershipManagerLazy.get());
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 89f720535402..adda049951ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -817,13 +817,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mNotificationParent != null;
}
- /**
- * @return whether this notification is the only child in the group summary
- */
- public boolean isOnlyChildInGroup() {
- return mGroupMembershipManager.isOnlyChildInGroup(mEntry);
- }
-
public ExpandableNotificationRow getNotificationParent() {
return mNotificationParent;
}
@@ -1425,14 +1418,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public void performDismiss(boolean fromAccessibility) {
- if (isOnlyChildInGroup()) {
- NotificationEntry groupSummary = mGroupMembershipManager.getLogicalGroupSummary(mEntry);
- if (groupSummary.isClearable()) {
- // If this is the only child in the group, dismiss the group, but don't try to show
- // the blocking helper affordance!
- groupSummary.getRow().performDismiss(fromAccessibility);
- }
- }
dismiss(fromAccessibility);
if (mEntry.isClearable()) {
if (mOnUserInteractionCallback != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 921232568755..ab78d197da0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -28,6 +28,8 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
@@ -48,6 +50,7 @@ public class NotificationBlockingHelperManager {
private final NotificationGutsManager mNotificationGutsManager;
private final NotificationEntryManager mNotificationEntryManager;
private final MetricsLogger mMetricsLogger;
+ private final GroupMembershipManager mGroupMembershipManager;
/** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */
private ExpandableNotificationRow mBlockingHelperRow;
private Set<String> mNonBlockablePkgs;
@@ -65,7 +68,8 @@ public class NotificationBlockingHelperManager {
Context context,
NotificationGutsManager notificationGutsManager,
NotificationEntryManager notificationEntryManager,
- MetricsLogger metricsLogger) {
+ MetricsLogger metricsLogger,
+ GroupMembershipManager groupMembershipManager) {
mContext = context;
mNotificationGutsManager = notificationGutsManager;
mNotificationEntryManager = notificationEntryManager;
@@ -73,6 +77,7 @@ public class NotificationBlockingHelperManager {
mNonBlockablePkgs = new HashSet<>();
Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray(
com.android.internal.R.array.config_nonBlockableNotificationPackages));
+ mGroupMembershipManager = groupMembershipManager;
}
/**
@@ -92,11 +97,12 @@ public class NotificationBlockingHelperManager {
// - The row is blockable (i.e. not non-blockable)
// - The dismissed row is a valid group (>1 or 0 children from the same channel)
// or the only child in the group
- if ((row.getEntry().getUserSentiment() == USER_SENTIMENT_NEGATIVE || DEBUG)
+ final NotificationEntry entry = row.getEntry();
+ if ((entry.getUserSentiment() == USER_SENTIMENT_NEGATIVE || DEBUG)
&& mIsShadeExpanded
&& !row.getIsNonblockable()
- && ((!row.isChildInGroup() || row.isOnlyChildInGroup())
- && row.getNumUniqueChannels() <= 1)) {
+ && ((!row.isChildInGroup() || mGroupMembershipManager.isOnlyChildInGroup(entry))
+ && row.getNumUniqueChannels() <= 1)) {
// Dismiss any current blocking helper before continuing forward (only one can be shown
// at a given time).
dismissCurrentBlockingHelper();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 0302b2b450e2..8050fea562a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -30,7 +30,6 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.ArrayList;
@@ -50,7 +49,6 @@ public class AmbientState {
private ActivatableNotificationView mActivatedChild;
private float mOverScrollTopAmount;
private float mOverScrollBottomAmount;
- private int mSpeedBumpIndex = -1;
private boolean mDozing;
private boolean mHideSensitive;
private float mStackTranslation;
@@ -245,14 +243,6 @@ public class AmbientState {
return top ? mOverScrollTopAmount : mOverScrollBottomAmount;
}
- public int getSpeedBumpIndex() {
- return mSpeedBumpIndex;
- }
-
- public void setSpeedBumpIndex(int shelfIndex) {
- mSpeedBumpIndex = shelfIndex;
- }
-
public SectionProvider getSectionProvider() {
return mSectionProvider;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index c24d3fcadbf9..b33aa5739273 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -248,6 +248,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private boolean mChangePositionInProgress;
private boolean mChildTransferInProgress;
+ private int mSpeedBumpIndex = -1;
+ private boolean mSpeedBumpIndexDirty = true;
+
/**
* The raw amount of the overScroll on the top, which is not rubber-banded.
*/
@@ -444,7 +447,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private int mMaxDisplayedNotifications = -1;
private int mStatusBarHeight;
private int mMinInteractionHeight;
- private boolean mNoAmbient;
private final Rect mClipRect = new Rect();
private boolean mIsClipped;
private Rect mRequestedClipBounds;
@@ -1010,7 +1012,33 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
public int getSpeedBumpIndex() {
- return mAmbientState.getSpeedBumpIndex();
+ if (mSpeedBumpIndexDirty) {
+ mSpeedBumpIndexDirty = false;
+ int speedBumpIndex = 0;
+ int currentIndex = 0;
+ final int n = getChildCount();
+ for (int i = 0; i < n; i++) {
+ View view = getChildAt(i);
+ if (view.getVisibility() == View.GONE
+ || !(view instanceof ExpandableNotificationRow)) {
+ continue;
+ }
+ ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ currentIndex++;
+ boolean beforeSpeedBump;
+ if (mHighPriorityBeforeSpeedBump) {
+ beforeSpeedBump = row.getEntry().getBucket() < BUCKET_SILENT;
+ } else {
+ beforeSpeedBump = !row.getEntry().isAmbient();
+ }
+ if (beforeSpeedBump) {
+ speedBumpIndex = currentIndex;
+ }
+ }
+
+ mSpeedBumpIndex = speedBumpIndex;
+ }
+ return mSpeedBumpIndex;
}
@Override
@@ -1066,12 +1094,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
- @ShadeViewRefactor(RefactorComponent.ADAPTER)
- private void setSpeedBumpIndex(int newIndex, boolean noAmbient) {
- mAmbientState.setSpeedBumpIndex(newIndex);
- mNoAmbient = noAmbient;
- }
-
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setChildLocationsChangedListener(
NotificationLogger.OnChildLocationsChangedListener listener) {
@@ -1135,7 +1157,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
} else {
mAmbientState.setScrollY(mOwnScrollY);
}
- mStackScrollAlgorithm.resetViewStates(mAmbientState);
+ mStackScrollAlgorithm.resetViewStates(mAmbientState, getSpeedBumpIndex());
if (!isCurrentlyAnimating() && !mNeedsAnimation) {
applyCurrentState();
} else {
@@ -5785,32 +5807,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* @param open Should the fling open or close the overscroll view.
*/
void flingTopOverscroll(float velocity, boolean open);
- }
+ }
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateSpeedBumpIndex() {
- int speedBumpIndex = 0;
- int currentIndex = 0;
- final int N = getChildCount();
- for (int i = 0; i < N; i++) {
- View view = getChildAt(i);
- if (view.getVisibility() == View.GONE || !(view instanceof ExpandableNotificationRow)) {
- continue;
- }
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
- currentIndex++;
- boolean beforeSpeedBump;
- if (mHighPriorityBeforeSpeedBump) {
- beforeSpeedBump = row.getEntry().getBucket() < BUCKET_SILENT;
- } else {
- beforeSpeedBump = !row.getEntry().isAmbient();
- }
- if (beforeSpeedBump) {
- speedBumpIndex = currentIndex;
- }
- }
- boolean noAmbient = speedBumpIndex == N;
- setSpeedBumpIndex(speedBumpIndex, noAmbient);
+ mSpeedBumpIndexDirty = true;
}
/** Updates the indices of the boundaries between sections. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 8d792ab6aa58..81e6258ca5f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -713,6 +713,10 @@ public class NotificationStackScrollLayoutController {
return mView.getWakeUpHeight();
}
+ public int getSpeedBumpIndex() {
+ return mView.getSpeedBumpIndex();
+ }
+
public void setHideAmount(float linearAmount, float amount) {
mView.setHideAmount(linearAmount, amount);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 541c7845a5d3..95edfe37fee7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -91,7 +91,7 @@ public class StackScrollAlgorithm {
/**
* Updates the state of all children in the hostview based on this algorithm.
*/
- public void resetViewStates(AmbientState ambientState) {
+ public void resetViewStates(AmbientState ambientState, int speedBumpIndex) {
// The state of the local variables are saved in an algorithmState to easily subdivide it
// into multiple phases.
StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
@@ -110,7 +110,7 @@ public class StackScrollAlgorithm {
updateDimmedActivatedHideSensitive(ambientState, algorithmState);
updateClipping(algorithmState, ambientState);
- updateSpeedBumpState(algorithmState, ambientState);
+ updateSpeedBumpState(algorithmState, speedBumpIndex);
updateShelfState(ambientState);
getNotificationChildrenStates(algorithmState, ambientState);
}
@@ -136,9 +136,9 @@ public class StackScrollAlgorithm {
}
private void updateSpeedBumpState(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ int speedBumpIndex) {
int childCount = algorithmState.visibleChildren.size();
- int belowSpeedBump = ambientState.getSpeedBumpIndex();
+ int belowSpeedBump = speedBumpIndex;
for (int i = 0; i < childCount; i++) {
ExpandableView child = algorithmState.visibleChildren.get(i);
ExpandableViewState childViewState = child.getViewState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index f80656706f37..737cdeba797a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -281,17 +281,10 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
// TODO: Some of this code may be able to move to NotificationEntryManager.
removeHUN(row);
- NotificationEntry parentToCancel = null;
- if (shouldAutoCancel(entry.getSbn()) && mGroupMembershipManager.isOnlyChildInGroup(entry)) {
- NotificationEntry summarySbn = mGroupMembershipManager.getLogicalGroupSummary(entry);
- if (shouldAutoCancel(summarySbn.getSbn())) {
- parentToCancel = summarySbn;
- }
- }
- final NotificationEntry parentToCancelFinal = parentToCancel;
+
final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
entry, row, controller, intent,
- isActivityIntent, wasOccluded, parentToCancelFinal);
+ isActivityIntent, wasOccluded);
if (showOverLockscreen) {
mShadeController.addPostCollapseAction(runnable);
@@ -312,8 +305,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
RemoteInputController controller,
PendingIntent intent,
boolean isActivityIntent,
- boolean wasOccluded,
- NotificationEntry parentToCancelFinal) {
+ boolean wasOccluded) {
String notificationKey = entry.getKey();
mLogger.logHandleClickAfterPanelCollapsed(notificationKey);
@@ -373,22 +365,23 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
NotificationLogger.getNotificationLocation(entry);
final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
rank, count, true, location);
+
+ // NMS will officially remove notification if the notification has FLAG_AUTO_CANCEL:
mClickNotifier.onNotificationClick(notificationKey, nv);
- if (!canBubble) {
- if (parentToCancelFinal != null) {
- // TODO: (b/145659174) remove - this cancels the parent if the notification clicked
- // on will auto-cancel and is the only child in the group. This won't be
- // necessary in the new pipeline due to group pruning in ShadeListBuilder.
- removeNotification(parentToCancelFinal);
- }
+ // TODO (b/162832756): delete these notification removals when migrating to the new
+ // pipeline; this is taken care of in {@link NotifCollection#tryRemoveNotification}
+ // which cancels lifetime extenders if the notification was dismissed by the user (ie:
+ // clicked or manually dismissed)
+ if (!canBubble && !mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
if (shouldAutoCancel(entry.getSbn())
|| mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
notificationKey)) {
- // Automatically remove all notifications that we may have kept around longer
+ // manually call notification removal in order to cancel any lifetime extenders
removeNotification(row.getEntry());
}
}
+
mIsCollapsingToShowActivityOverLockscreen = false;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 359faba48f08..ce0f1220fc88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -442,7 +442,7 @@ public class NotifCollectionTest extends SysuiTestCase {
}
@Test
- public void testDismissingLifetimeExtendedSummaryDoesNotDismissChildren() {
+ public void testRetractingLifetimeExtendedSummaryDoesNotDismissChildren() {
// GIVEN A notif group with one summary and two children
mCollection.addNotificationLifetimeExtender(mExtender1);
CollectionEvent notif1 = postNotif(
@@ -460,15 +460,16 @@ public class NotifCollectionTest extends SysuiTestCase {
NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
NotificationEntry entry3 = mCollectionListener.getEntry(notif3.key);
- // GIVEN that the summary and one child are retracted, but both are lifetime-extended
+ // GIVEN that the summary and one child are retracted by the app, but both are
+ // lifetime-extended
mExtender1.shouldExtendLifetime = true;
- mNoMan.retractNotif(notif1.sbn, REASON_CANCEL);
- mNoMan.retractNotif(notif2.sbn, REASON_CANCEL);
+ mNoMan.retractNotif(notif1.sbn, REASON_APP_CANCEL);
+ mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
assertEquals(
new ArraySet<>(List.of(entry1, entry2, entry3)),
new ArraySet<>(mCollection.getAllNotifs()));
- // WHEN the summary is dismissed by the user
+ // WHEN the summary is retracted by the app
mCollection.dismissNotification(entry1, defaultStats(entry1));
// THEN the summary is removed, but both children stick around
@@ -480,6 +481,28 @@ public class NotifCollectionTest extends SysuiTestCase {
}
@Test
+ public void testNMSReportsUserDismissalAlwaysRemovesNotif() throws RemoteException {
+ // GIVEN notifications are lifetime extended
+ mExtender1.shouldExtendLifetime = true;
+ CollectionEvent notif = postNotif(buildNotif(TEST_PACKAGE, 1, "myTag"));
+ CollectionEvent notif2 = postNotif(buildNotif(TEST_PACKAGE, 2, "myTag"));
+ NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+ assertEquals(
+ new ArraySet<>(List.of(entry, entry2)),
+ new ArraySet<>(mCollection.getAllNotifs()));
+
+ // WHEN the notifications are reported to be dismissed by the user by NMS
+ mNoMan.retractNotif(notif.sbn, REASON_CANCEL);
+ mNoMan.retractNotif(notif2.sbn, REASON_CLICK);
+
+ // THEN the notifications are removed b/c they were dismissed by the user
+ assertEquals(
+ new ArraySet<>(List.of()),
+ new ArraySet<>(mCollection.getAllNotifs()));
+ }
+
+ @Test
public void testDismissNotificationCallsDismissInterceptors() throws RemoteException {
// GIVEN a collection with notifications with multiple dismiss interceptors
mInterceptor1.shouldInterceptDismissal = true;
@@ -833,13 +856,13 @@ public class NotifCollectionTest extends SysuiTestCase {
NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
- // WHEN a notification is removed
- mNoMan.retractNotif(notif2.sbn, REASON_CLICK);
+ // WHEN a notification is removed by the app
+ mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
// THEN each extender is asked whether to extend, even if earlier ones return true
- verify(mExtender1).shouldExtendLifetime(entry2, REASON_CLICK);
- verify(mExtender2).shouldExtendLifetime(entry2, REASON_CLICK);
- verify(mExtender3).shouldExtendLifetime(entry2, REASON_CLICK);
+ verify(mExtender1).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+ verify(mExtender2).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+ verify(mExtender3).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
// THEN the entry is not removed
assertTrue(mCollection.getAllNotifs().contains(entry2));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index 5aeb43fbd959..edb8776bcb02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -49,6 +49,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import org.junit.Before;
import org.junit.Test;
@@ -71,6 +72,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
@Mock private NotificationEntryManager mEntryManager;
@Mock private NotificationMenuRow mMenuRow;
@Mock private NotificationMenuRowPlugin.MenuItem mMenuItem;
+ @Mock private GroupMembershipManager mGroupMembershipManager;
@Before
public void setUp() {
@@ -89,7 +91,8 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
mBlockingHelperManager = new NotificationBlockingHelperManager(
- mContext, mGutsManager, mEntryManager, mock(MetricsLogger.class));
+ mContext, mGutsManager, mEntryManager, mock(MetricsLogger.class),
+ mGroupMembershipManager);
// By default, have the shade visible/expanded.
mBlockingHelperManager.setNotificationShadeExpanded(1f);
}
@@ -185,6 +188,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
.build();
assertFalse(childRow.getIsNonblockable());
+ when(mGroupMembershipManager.isOnlyChildInGroup(childRow.getEntry())).thenReturn(true);
assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
verify(mGutsManager).openGuts(childRow, 0, 0, mMenuItem);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index f48e6ea7941e..de59ac319bd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -451,8 +451,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
public void testAddNotificationUpdatesSpeedBumpIndex() {
- // initial state == -1
- assertEquals(-1, mStackScroller.getSpeedBumpIndex());
+ // initial state calculated == 0
+ assertEquals(0, mStackScroller.getSpeedBumpIndex());
// add notification that's before the speed bump
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
@@ -467,8 +467,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
public void testAddAmbientNotificationNoSpeedBumpUpdate() {
- // initial state == -1
- assertEquals(-1, mStackScroller.getSpeedBumpIndex());
+ // initial state calculated == 0
+ assertEquals(0, mStackScroller.getSpeedBumpIndex());
// add notification that's after the speed bump
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
@@ -483,8 +483,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
public void testRemoveNotificationUpdatesSpeedBump() {
- // initial state == -1
- assertEquals(-1, mStackScroller.getSpeedBumpIndex());
+ // initial state calculated == 0
+ assertEquals(0, mStackScroller.getSpeedBumpIndex());
// add 3 notification that are after the speed bump
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 5447605a36d1..1615998f7787 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -28,6 +28,7 @@ import android.media.AudioDeviceAttributes;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.IAudioRoutesObserver;
+import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.MediaMetrics;
import android.os.Binder;
@@ -546,6 +547,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
mDeviceInventory.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
}
+ /*package*/ int setPreferredDevicesForCapturePresetSync(int capturePreset,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return mDeviceInventory.setPreferredDevicesForCapturePresetSync(capturePreset, devices);
+ }
+
+ /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
+ return mDeviceInventory.clearPreferredDevicesForCapturePresetSync(capturePreset);
+ }
+
+ /*package*/ void registerCapturePresetDevicesRoleDispatcher(
+ @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+ mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher);
+ }
+
+ /*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
+ @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+ mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
+ }
+
//---------------------------------------------------------------------
// Communication with (to) AudioService
//TODO check whether the AudioService methods are candidates to move here
@@ -694,6 +714,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
}
+ /*package*/ void postSaveSetPreferredDevicesForCapturePreset(
+ int capturePreset, List<AudioDeviceAttributes> devices) {
+ sendILMsgNoDelay(
+ MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset, devices);
+ }
+
+ /*package*/ void postSaveClearPreferredDevicesForCapturePreset(int capturePreset) {
+ sendIMsgNoDelay(
+ MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset);
+ }
+
//---------------------------------------------------------------------
// Method forwarding between the helper classes (BtHelper, AudioDeviceInventory)
// only call from a "handle"* method or "on"* method
@@ -1098,6 +1129,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
case MSG_CHECK_MUTE_MUSIC:
checkMessagesMuteMusic(0);
break;
+ case MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET: {
+ final int capturePreset = msg.arg1;
+ final List<AudioDeviceAttributes> devices =
+ (List<AudioDeviceAttributes>) msg.obj;
+ mDeviceInventory.onSaveSetPreferredDevicesForCapturePreset(
+ capturePreset, devices);
+ } break;
+ case MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET: {
+ final int capturePreset = msg.arg1;
+ mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
+ } break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -1174,6 +1216,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int MSG_CHECK_MUTE_MUSIC = 36;
private static final int MSG_REPORT_NEW_ROUTES_A2DP = 37;
+ private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
+ private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 39;
+
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index fbf07cc591ff..33a8a30243de 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -31,6 +31,7 @@ import android.media.AudioPort;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.IAudioRoutesObserver;
+import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.MediaMetrics;
import android.os.Binder;
@@ -140,6 +141,10 @@ public class AudioDeviceInventory {
private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices =
new ArrayMap<>();
+ // List of preferred devices of capture preset
+ private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevicesForCapturePreset =
+ new ArrayMap<>();
+
// the wrapper for AudioSystem static methods, allows us to spy AudioSystem
private final @NonNull AudioSystemAdapter mAudioSystem;
@@ -154,6 +159,10 @@ public class AudioDeviceInventory {
final RemoteCallbackList<IStrategyPreferredDevicesDispatcher> mPrefDevDispatchers =
new RemoteCallbackList<IStrategyPreferredDevicesDispatcher>();
+ // Monitoring of devices for role and capture preset
+ final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers =
+ new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>();
+
/*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
mDeviceBroker = broker;
mAudioSystem = AudioSystemAdapter.getDefaultAdapter();
@@ -243,6 +252,9 @@ public class AudioDeviceInventory {
pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType)
+ " (" + AudioSystem.getDeviceName(keyType)
+ ") addr:" + valueAddress); });
+ mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
+ pw.println(" " + prefix + "capturePreset:" + capturePreset
+ + " devices:" + devices); });
}
//------------------------------------------------------------
@@ -270,6 +282,9 @@ public class AudioDeviceInventory {
mAudioSystem.setDevicesRoleForStrategy(
strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); });
}
+ synchronized (mPreferredDevicesForCapturePreset) {
+ // TODO: call audiosystem to restore
+ }
}
// only public for mocking/spying
@@ -613,6 +628,20 @@ public class AudioDeviceInventory {
dispatchPreferredDevice(strategy, new ArrayList<AudioDeviceAttributes>());
}
+ /*package*/ void onSaveSetPreferredDevicesForCapturePreset(
+ int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+ mPreferredDevicesForCapturePreset.put(capturePreset, devices);
+ dispatchDevicesRoleForCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+ }
+
+ /*package*/ void onSaveClearPreferredDevicesForCapturePreset(int capturePreset) {
+ mPreferredDevicesForCapturePreset.remove(capturePreset);
+ dispatchDevicesRoleForCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED,
+ new ArrayList<AudioDeviceAttributes>());
+ }
+
//------------------------------------------------------------
//
@@ -651,6 +680,41 @@ public class AudioDeviceInventory {
mPrefDevDispatchers.unregister(dispatcher);
}
+ /*package*/ int setPreferredDevicesForCapturePresetSync(
+ int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+ final long identity = Binder.clearCallingIdentity();
+ final int status = mAudioSystem.setDevicesRoleForCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+ Binder.restoreCallingIdentity(identity);
+
+ if (status == AudioSystem.SUCCESS) {
+ mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices);
+ }
+ return status;
+ }
+
+ /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
+ final long identity = Binder.clearCallingIdentity();
+ final int status = mAudioSystem.clearDevicesRoleForCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED);
+ Binder.restoreCallingIdentity(identity);
+
+ if (status == AudioSystem.SUCCESS) {
+ mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset);
+ }
+ return status;
+ }
+
+ /*package*/ void registerCapturePresetDevicesRoleDispatcher(
+ @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+ mDevRoleCapturePresetDispatchers.register(dispatcher);
+ }
+
+ /*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
+ @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+ mDevRoleCapturePresetDispatchers.unregister(dispatcher);
+ }
+
/**
* Implements the communication with AudioSystem to (dis)connect a device in the native layers
* @param connect true if connection
@@ -1306,6 +1370,19 @@ public class AudioDeviceInventory {
mPrefDevDispatchers.finishBroadcast();
}
+ private void dispatchDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ final int nbDispatchers = mDevRoleCapturePresetDispatchers.beginBroadcast();
+ for (int i = 0; i < nbDispatchers; ++i) {
+ try {
+ mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged(
+ capturePreset, role, devices);
+ } catch (RemoteException e) {
+ }
+ }
+ mDevRoleCapturePresetDispatchers.finishBroadcast();
+ }
+
//----------------------------------------------------------
// For tests only
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 4378490d19c5..5f6491093453 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -84,6 +84,7 @@ import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
import android.media.IAudioServerStateDispatcher;
import android.media.IAudioService;
+import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
@@ -1987,6 +1988,94 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
}
+ /**
+ * @see AudioManager#setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
+ */
+ public int setPreferredDevicesForCapturePreset(
+ int capturePreset, List<AudioDeviceAttributes> devices) {
+ if (devices == null) {
+ return AudioSystem.ERROR;
+ }
+ enforceModifyAudioRoutingPermission();
+ final String logString = String.format(
+ "setPreferredDevicesForCapturePreset u/pid:%d/%d source:%d dev:%s",
+ Binder.getCallingUid(), Binder.getCallingPid(), capturePreset,
+ devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
+ sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+ if (devices.stream().anyMatch(device ->
+ device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT)) {
+ Log.e(TAG, "Unsupported output routing in " + logString);
+ return AudioSystem.ERROR;
+ }
+
+ final int status = mDeviceBroker.setPreferredDevicesForCapturePresetSync(
+ capturePreset, devices);
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, String.format("Error %d in %s)", status, logString));
+ }
+
+ return status;
+ }
+
+ /** @see AudioManager#clearPreferredDevicesForCapturePreset(int) */
+ public int clearPreferredDevicesForCapturePreset(int capturePreset) {
+ enforceModifyAudioRoutingPermission();
+ final String logString = String.format(
+ "removePreferredDeviceForCapturePreset source:%d", capturePreset);
+ sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+
+ final int status = mDeviceBroker.clearPreferredDevicesForCapturePresetSync(capturePreset);
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, String.format("Error %d in %s", status, logString));
+ }
+ return status;
+ }
+
+ /**
+ * @see AudioManager#getPreferredDevicesForCapturePreset(int)
+ */
+ public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
+ enforceModifyAudioRoutingPermission();
+ List<AudioDeviceAttributes> devices = new ArrayList<>();
+ final long identity = Binder.clearCallingIdentity();
+ final int status = AudioSystem.getDevicesForRoleAndCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+ Binder.restoreCallingIdentity(identity);
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, String.format("Error %d in getPreferredDeviceForCapturePreset(%d)",
+ status, capturePreset));
+ return new ArrayList<AudioDeviceAttributes>();
+ } else {
+ return devices;
+ }
+ }
+
+ /**
+ * @see AudioManager#addOnPreferredDevicesForCapturePresetChangedListener(
+ * Executor, OnPreferredDevicesForCapturePresetChangedListener)
+ */
+ public void registerCapturePresetDevicesRoleDispatcher(
+ @Nullable ICapturePresetDevicesRoleDispatcher dispatcher) {
+ if (dispatcher == null) {
+ return;
+ }
+ enforceModifyAudioRoutingPermission();
+ mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(dispatcher);
+ }
+
+ /**
+ * @see AudioManager#removeOnPreferredDevicesForCapturePresetChangedListener(
+ * AudioManager.OnPreferredDevicesForCapturePresetChangedListener)
+ */
+ public void unregisterCapturePresetDevicesRoleDispatcher(
+ @Nullable ICapturePresetDevicesRoleDispatcher dispatcher) {
+ if (dispatcher == null) {
+ return;
+ }
+ enforceModifyAudioRoutingPermission();
+ mDeviceBroker.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
+ }
+
/** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index a0e1ca78a5e7..ae64990fd8d0 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -101,6 +101,40 @@ public class AudioSystemAdapter {
}
/**
+ * Same as (@link AudioSystem#setDevicesRoleForCapturePreset(int, List))
+ * @param capturePreset
+ * @param role
+ * @param devices
+ * @return
+ */
+ public int setDevicesRoleForCapturePreset(int capturePreset, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return AudioSystem.setDevicesRoleForCapturePreset(capturePreset, role, devices);
+ }
+
+ /**
+ * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int)}
+ * @param capturePreset
+ * @param role
+ * @param devicesToRemove
+ * @return
+ */
+ public int removeDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) {
+ return AudioSystem.removeDevicesRoleForCapturePreset(capturePreset, role, devicesToRemove);
+ }
+
+ /**
+ * Same as {@link AudioSystem#}
+ * @param capturePreset
+ * @param role
+ * @return
+ */
+ public int clearDevicesRoleForCapturePreset(int capturePreset, int role) {
+ return AudioSystem.clearDevicesRoleForCapturePreset(capturePreset, role);
+ }
+
+ /**
* Same as {@link AudioSystem#setParameters(String)}
* @param keyValuePairs
* @return
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index bbc29b0bf89b..f02bb8ffe17f 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -19,6 +19,7 @@ package com.android.server.audio;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.content.ContentResolver;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioFocusInfo;
@@ -94,8 +95,9 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
mContext = cntxt;
mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
mFocusEnforcer = pfe;
- mMultiAudioFocusEnabled = Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0) != 0;
+ final ContentResolver cr = mContext.getContentResolver();
+ mMultiAudioFocusEnabled = Settings.System.getIntForUser(cr,
+ Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0, cr.getUserId()) != 0;
}
protected void dump(PrintWriter pw) {
@@ -1081,8 +1083,9 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
public void updateMultiAudioFocus(boolean enabled) {
Log.d(TAG, "updateMultiAudioFocus( " + enabled + " )");
mMultiAudioFocusEnabled = enabled;
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.MULTI_AUDIO_FOCUS_ENABLED, enabled ? 1 : 0);
+ final ContentResolver cr = mContext.getContentResolver();
+ Settings.System.putIntForUser(cr,
+ Settings.System.MULTI_AUDIO_FOCUS_ENABLED, enabled ? 1 : 0, cr.getUserId());
if (!mFocusStack.isEmpty()) {
final FocusRequester fr = mFocusStack.peek();
fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false);
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index ad3cd67ad65b..73f788901dc9 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -493,6 +493,10 @@ final class ColorFade {
== Display.COLOR_MODE_DISPLAY_P3;
SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
mDisplayManagerInternal.systemScreenshot(mDisplayId);
+ if (screenshotBuffer == null) {
+ Slog.e(TAG, "Failed to take screenshot. Buffer is null");
+ return false;
+ }
s.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(),
screenshotBuffer.getColorSpace());
diff --git a/services/core/java/com/android/server/telecom/OWNERS b/services/core/java/com/android/server/telecom/OWNERS
new file mode 100644
index 000000000000..39be2c1aecc4
--- /dev/null
+++ b/services/core/java/com/android/server/telecom/OWNERS
@@ -0,0 +1,6 @@
+breadley@google.com
+hallliu@google.com
+tgunn@google.com
+xiaotonj@google.com
+shuoq@google.com
+rgreenwalt@google.com
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index 609af8d5bf4d..8d706cb960e9 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -79,6 +79,23 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter {
}
@Override
+ public int setDevicesRoleForCapturePreset(int capturePreset, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
+ public int removeDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
+ public int clearDevicesRoleForCapturePreset(int capturePreset, int role) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
public int setParameters(String keyValuePairs) {
return AudioSystem.AUDIO_STATUS_OK;
}
diff --git a/telecomm/OWNERS b/telecomm/OWNERS
index 673a0a9b558e..9969ee965fbd 100644
--- a/telecomm/OWNERS
+++ b/telecomm/OWNERS
@@ -1,7 +1,8 @@
set noparent
-tgunn@google.com
breadley@google.com
hallliu@google.com
+tgunn@google.com
+xiaotonj@google.com
+shuoq@google.com
rgreenwalt@google.com
-paulye@google.com
diff --git a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
index 4116234c4c8d..3175e456693d 100644
--- a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -223,7 +223,11 @@ public class WifiNl80211Manager {
/**
* Callbacks for SoftAp interface registered using
* {@link #registerApCallback(String, Executor, SoftApCallback)}.
+ *
+ * @deprecated The usage is replaced by vendor HAL
+ * {@code android.hardware.wifi.hostapd.V1_3.IHostapdCallback}.
*/
+ @Deprecated
public interface SoftApCallback {
/**
* Invoked when there is a fatal failure and the SoftAp is shutdown.
@@ -1121,7 +1125,11 @@ public class WifiNl80211Manager {
* @param callback Callback for AP events.
* @return true on success, false on failure (e.g. when called on an interface which has not
* been set up).
+ *
+ * @deprecated The usage is replaced by vendor HAL
+ * {@code android.hardware.wifi.hostapd.V1_3.IHostapdCallback}.
*/
+ @Deprecated
public boolean registerApCallback(@NonNull String ifaceName,
@NonNull @CallbackExecutor Executor executor,
@NonNull SoftApCallback callback) {