summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp81
-rw-r--r--apex/statsd/service/java/com/android/server/stats/StatsManagerService.java7
-rw-r--r--api/current.txt1
-rw-r--r--api/module-lib-current.txt7
-rwxr-xr-xapi/system-current.txt27
-rw-r--r--api/test-current.txt8
-rw-r--r--cmds/screencap/screencap.cpp12
-rw-r--r--core/java/android/app/assist/AssistStructure.java7
-rw-r--r--core/java/android/os/Binder.java1
-rw-r--r--core/java/android/view/BatchedInputEventReceiver.java23
-rw-r--r--core/java/android/view/SurfaceControl.java96
-rw-r--r--core/java/android/view/ViewConfiguration.java2
-rw-r--r--core/java/android/view/accessibility/MagnificationAnimationCallback.java31
-rw-r--r--core/jni/android_media_AudioSystem.cpp83
-rw-r--r--core/jni/android_view_SurfaceControl.cpp110
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/tests/powertests/PowerStatsLoadTests/Android.bp13
-rw-r--r--core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml36
-rw-r--r--core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/ConnectivitySetupRule.java159
-rw-r--r--core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetrics.java278
-rw-r--r--core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java281
-rw-r--r--core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/SystemServiceCallLoadTest.java69
-rw-r--r--core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/WiFiLoadTest.java72
-rw-r--r--framework-jarjar-rules.txt4
-rw-r--r--location/java/android/location/LocationManager.java2
-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.java469
-rw-r--r--media/java/android/media/AudioManagerInternal.java9
-rw-r--r--media/java/android/media/AudioSystem.java129
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl27
-rw-r--r--media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl31
-rw-r--r--media/java/android/media/MediaMetadata.java9
-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/module-lib-current.txt7
-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/accessibility/MagnificationModeSwitch.java18
-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/controls/management/ControlAdapter.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaData.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt11
-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/accessibility/MagnificationModeSwitchTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt1
-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/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java85
-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.java182
-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/biometrics/sensors/AcquisitionClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/Face10.java11
-rw-r--r--services/core/java/com/android/server/display/ColorFade.java4
-rw-r--r--services/core/java/com/android/server/location/util/SystemSettingsHelper.java9
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java25
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java20
-rw-r--r--services/core/java/com/android/server/pm/Settings.java24
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java4
-rw-r--r--services/core/java/com/android/server/telecom/OWNERS6
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java17
-rw-r--r--telecomm/OWNERS5
-rw-r--r--telecomm/java/android/telecom/Logging/SessionManager.java7
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt2
-rw-r--r--tools/validatekeymaps/Main.cpp2
-rw-r--r--wifi/api/current.txt1
-rw-r--r--wifi/java/android/net/wifi/SoftApCapability.java4
-rw-r--r--wifi/java/android/net/wifi/SoftApConfiguration.java6
-rw-r--r--wifi/java/android/net/wifi/SoftApInfo.java4
-rw-r--r--wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl1
-rw-r--r--wifi/java/android/net/wifi/aware/WifiAwareManager.java16
-rw-r--r--wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java8
-rw-r--r--wifi/java/android/net/wifi/util/SdkLevelUtil.java7
-rw-r--r--wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java9
108 files changed, 3179 insertions, 600 deletions
diff --git a/Android.bp b/Android.bp
index fb8474278fea..eacf57ccd38c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -499,53 +499,6 @@ java_library {
installable: false,
}
-java_defaults {
- name: "framework-defaults",
- defaults: ["framework-aidl-export-defaults"],
- installable: true,
-
- aidl: {
- generate_get_transaction_name: true,
- },
-
- srcs: ["core/java/**/*.logtags"],
-
- exclude_srcs: [
- // See comment on framework-atb-backward-compatibility module below
- "core/java/android/content/pm/AndroidTestBaseUpdater.java",
- ],
-
- sdk_version: "core_platform",
- libs: [
- "app-compat-annotations",
- "ext",
- "unsupportedappusage",
- ],
-
- jarjar_rules: ":framework-jarjar-rules",
-
- static_libs: [
- "framework-internal-utils",
- ],
-
- dxflags: [
- "--core-library",
- "--multi-dex",
- ],
-
- plugins: [
- "view-inspector-annotation-processor",
- "staledataclass-annotation-processor",
- "error_prone_android_framework",
- ],
-
- required: [
- // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
- "gps_debug.conf",
- "protolog.conf.json.gz",
- ],
-}
-
filegroup {
name: "framework-jarjar-rules",
srcs: ["framework-jarjar-rules.txt"],
@@ -585,19 +538,47 @@ filegroup {
java_library {
name: "framework-minus-apex",
- defaults: ["framework-defaults"],
- srcs: [":framework-non-updatable-sources"],
+ defaults: ["framework-aidl-export-defaults"],
+ srcs: [
+ ":framework-non-updatable-sources",
+ "core/java/**/*.logtags",
+ ],
+ // See comment on framework-atb-backward-compatibility module below
+ exclude_srcs: ["core/java/android/content/pm/AndroidTestBaseUpdater.java"],
+ aidl: {
+ generate_get_transaction_name: true,
+ },
+ dxflags: [
+ "--core-library",
+ "--multi-dex",
+ ],
installable: true,
+ jarjar_rules: ":framework-jarjar-rules",
javac_shard_size: 150,
+ plugins: [
+ "view-inspector-annotation-processor",
+ "staledataclass-annotation-processor",
+ "error_prone_android_framework",
+ ],
required: [
"framework-platform-compat-config",
+ // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
+ "gps_debug.conf",
"libcore-platform-compat-config",
+ "protolog.conf.json.gz",
"services-platform-compat-config",
"documents-ui-compat-config",
"calendar-provider-compat-config",
],
- libs: ["framework-updatable-stubs-module_libs_api"],
+ libs: [
+ "app-compat-annotations",
+ "ext",
+ "framework-updatable-stubs-module_libs_api",
+ "unsupportedappusage",
+ ],
+ sdk_version: "core_platform",
static_libs: [
+ "framework-internal-utils",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
// in favor of an API stubs dependency in java_library "framework" below.
"mimemap",
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
index 97846f2397a5..1e3846bc4a0b 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -27,6 +27,7 @@ import android.os.Binder;
import android.os.IPullAtomCallback;
import android.os.IStatsManagerService;
import android.os.IStatsd;
+import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -412,8 +413,13 @@ public class StatsManagerService extends IStatsManagerService.Stub {
@Override
public byte[] getData(long key, String packageName) throws IllegalStateException {
enforceDumpAndUsageStatsPermission(packageName);
+ PowerManager powerManager = (PowerManager)
+ mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ /*tag=*/ StatsManagerService.class.getCanonicalName());
int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
+ wl.acquire();
try {
IStatsd statsd = waitForStatsd();
if (statsd != null) {
@@ -423,6 +429,7 @@ public class StatsManagerService extends IStatsManagerService.Stub {
Log.e(TAG, "Failed to getData with statsd");
throw new IllegalStateException(e.getMessage(), e);
} finally {
+ wl.release();
Binder.restoreCallingIdentity(token);
}
throw new IllegalStateException("Failed to connect to statsd to getData");
diff --git a/api/current.txt b/api/current.txt
index e83450136aa4..e678d1656fd4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31814,6 +31814,7 @@ package android.net.wifi.aware {
method public void attach(@NonNull android.net.wifi.aware.AttachCallback, @NonNull android.net.wifi.aware.IdentityChangedListener, @Nullable android.os.Handler);
method public android.net.wifi.aware.Characteristics getCharacteristics();
method public boolean isAvailable();
+ method public boolean isDeviceAttached();
field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0
field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 73511c0f5b87..097609e6e428 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -35,9 +35,16 @@ package android.graphics {
package android.media {
public class AudioManager {
+ method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
+ method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
+ method public void setStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
+ public static final class MediaMetadata.Builder {
+ ctor public MediaMetadata.Builder(@NonNull android.media.MediaMetadata, @IntRange(from=1) int);
+ }
+
}
package android.media.session {
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/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index dec4a567fc81..5c08704a6623 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -30,8 +30,9 @@
#include <binder/ProcessState.h>
-#include <gui/SurfaceComposerClient.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SyncScreenCaptureListener.h>
#include <ui/DisplayInfo.h>
#include <ui/GraphicTypes.h>
@@ -181,13 +182,18 @@ int main(int argc, char** argv)
ProcessState::self()->setThreadPoolMaxThreadCount(0);
ProcessState::self()->startThreadPool();
- ScreenCaptureResults captureResults;
- status_t result = ScreenshotClient::captureDisplay(displayId->value, captureResults);
+ sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+ status_t result = ScreenshotClient::captureDisplay(displayId->value, captureListener);
if (result != NO_ERROR) {
close(fd);
return 1;
}
+ ScreenCaptureResults captureResults = captureListener->waitForResults();
+ if (captureResults.result != NO_ERROR) {
+ close(fd);
+ return 1;
+ }
ui::Dataspace dataspace = captureResults.capturedDataspace;
sp<GraphicBuffer> buffer = captureResults.buffer;
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 22d8c87e9268..b5234f8e2e17 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -36,8 +36,6 @@ import android.view.WindowManagerGlobal;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
-import com.android.internal.util.Preconditions;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -1676,8 +1674,9 @@ public class AssistStructure implements Parcelable {
}
/**
- * Returns the maximum length of the text associated with this node node, or {@code -1}
- * if not supported by the node or not set.
+ * Returns the maximum length of the text associated with this node, or {@code -1} if not
+ * supported by the node or not set. System may set a default value if the text length is
+ * not set.
*
* <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
* not for assist purposes.
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 515704ca77f4..bec96f98dbcd 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -1102,7 +1102,6 @@ public class Binder implements IBinder {
}
private static native long getNativeBBinderHolder();
- private static native long getFinalizer();
/**
* By default, we use the calling uid since we can always trust it.
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/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 6ef086b55c41..3af8958368dc 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -65,6 +65,9 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Handle to an on-screen Surface managed by the system compositor. The SurfaceControl is
@@ -87,10 +90,10 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeWriteToParcel(long nativeObject, Parcel out);
private static native void nativeRelease(long nativeObject);
private static native void nativeDisconnect(long nativeObject);
- private static native ScreenshotHardwareBuffer nativeCaptureDisplay(
- DisplayCaptureArgs captureArgs);
- private static native ScreenshotHardwareBuffer nativeCaptureLayers(
- LayerCaptureArgs captureArgs);
+ private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
+ ScreenCaptureListener captureListener);
+ private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
+ ScreenCaptureListener captureListener);
private static native long nativeMirrorSurface(long mirrorOfObject);
private static native long nativeCreateTransaction();
private static native long nativeGetNativeTransactionFinalizer();
@@ -493,6 +496,8 @@ public final class SurfaceControl implements Parcelable {
private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696;
private static final int INTERNAL_DATASPACE_SCRGB = 411107328;
+ private static final int SCREENSHOT_WAIT_TIME_S = 1;
+
private void assignNativeObject(long nativeObject, String callsite) {
if (mNativeObject != 0) {
release();
@@ -611,6 +616,13 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * @hide
+ */
+ public abstract static class ScreenCaptureListener {
+ abstract void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer);
+ }
+
+ /**
* A common arguments class used for various screenshot requests. This contains arguments that
* are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
* @hide
@@ -685,7 +697,7 @@ public final class SurfaceControl implements Parcelable {
/**
* The arguments class used to make display capture requests.
*
- * @see #nativeCaptureDisplay(DisplayCaptureArgs)
+ * @see #nativeCaptureDisplay(DisplayCaptureArgs, ScreenCaptureListener)
* @hide
*/
public static class DisplayCaptureArgs extends CaptureArgs {
@@ -2226,13 +2238,46 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * @param captureArgs Arguments about how to take the screenshot
+ * @param captureListener A listener to receive the screenshot callback
+ * @hide
+ */
+ public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs,
+ @NonNull ScreenCaptureListener captureListener) {
+ return nativeCaptureDisplay(captureArgs, captureListener);
+ }
+
+ /**
* Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with
* the content.
*
* @hide
*/
public static ScreenshotHardwareBuffer captureDisplay(DisplayCaptureArgs captureArgs) {
- return nativeCaptureDisplay(captureArgs);
+ final AtomicReference<ScreenshotHardwareBuffer> outHardwareBuffer =
+ new AtomicReference<>(null);
+
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener() {
+ @Override
+ void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) {
+ outHardwareBuffer.set(hardwareBuffer);
+ countDownLatch.countDown();
+ }
+ };
+
+ int status = captureDisplay(captureArgs, screenCaptureListener);
+ if (status != 0) {
+ return null;
+ }
+
+ try {
+ countDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to wait for captureDisplay result", e);
+ }
+
+ return outHardwareBuffer.get();
}
/**
@@ -2277,14 +2322,37 @@ public final class SurfaceControl implements Parcelable {
.setPixelFormat(format)
.build();
- return nativeCaptureLayers(captureArgs);
+ return captureLayers(captureArgs);
}
/**
* @hide
*/
public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
- return nativeCaptureLayers(captureArgs);
+ final AtomicReference<ScreenshotHardwareBuffer> outHardwareBuffer =
+ new AtomicReference<>(null);
+
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener() {
+ @Override
+ void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) {
+ outHardwareBuffer.set(hardwareBuffer);
+ countDownLatch.countDown();
+ }
+ };
+
+ int status = captureLayers(captureArgs, screenCaptureListener);
+ if (status != 0) {
+ return null;
+ }
+
+ try {
+ countDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to wait for captureLayers result", e);
+ }
+
+ return outHardwareBuffer.get();
}
/**
@@ -2301,7 +2369,17 @@ public final class SurfaceControl implements Parcelable {
.setExcludeLayers(exclude)
.build();
- return nativeCaptureLayers(captureArgs);
+ return captureLayers(captureArgs);
+ }
+
+ /**
+ * @param captureArgs Arguments about how to take the screenshot
+ * @param captureListener A listener to receive the screenshot callback
+ * @hide
+ */
+ public static int captureLayers(@NonNull LayerCaptureArgs captureArgs,
+ @NonNull ScreenCaptureListener captureListener) {
+ return nativeCaptureLayers(captureArgs, captureListener);
}
/**
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index ccf1fb07d0bb..abf76ece1f3d 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -100,7 +100,7 @@ public class ViewConfiguration {
* Defines the duration in milliseconds a user needs to hold down the
* appropriate buttons (power + volume down) to trigger the screenshot chord.
*/
- private static final int SCREENSHOT_CHORD_KEY_TIMEOUT = 500;
+ private static final int SCREENSHOT_CHORD_KEY_TIMEOUT = 0;
/**
* Defines the duration in milliseconds a user needs to hold down the
diff --git a/core/java/android/view/accessibility/MagnificationAnimationCallback.java b/core/java/android/view/accessibility/MagnificationAnimationCallback.java
new file mode 100644
index 000000000000..491f7fb32a8c
--- /dev/null
+++ b/core/java/android/view/accessibility/MagnificationAnimationCallback.java
@@ -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.view.accessibility;
+
+/**
+ * A callback for magnification animation result.
+ * @hide
+ */
+public interface MagnificationAnimationCallback {
+ /**
+ * Called when the animation is finished or interrupted during animating.
+ *
+ * @param success {@code true} if animating successfully with given spec or the spec did not
+ * change. Otherwise {@code false}
+ */
+ void onResult(boolean success);
+}
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/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 85b4fe197980..416f8372fd81 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -30,6 +30,7 @@
#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <android_runtime/android_view_Surface.h>
#include <android_runtime/android_view_SurfaceSession.h>
+#include <gui/IScreenCaptureListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -188,6 +189,11 @@ static struct {
static struct {
jclass clazz;
+ jmethodID onScreenCaptureComplete;
+} gScreenCaptureListenerClassInfo;
+
+static struct {
+ jclass clazz;
jmethodID ctor;
jfieldID defaultConfig;
jfieldID primaryRefreshRateMin;
@@ -226,6 +232,54 @@ constexpr ui::Dataspace pickDataspaceFromColorMode(const ui::ColorMode colorMode
}
}
+class ScreenCaptureListenerWrapper : public BnScreenCaptureListener {
+public:
+ explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
+ env->GetJavaVM(&mVm);
+ screenCaptureListenerObject = env->NewGlobalRef(jobject);
+ LOG_ALWAYS_FATAL_IF(!screenCaptureListenerObject, "Failed to make global ref");
+ }
+
+ ~ScreenCaptureListenerWrapper() {
+ if (screenCaptureListenerObject) {
+ getenv()->DeleteGlobalRef(screenCaptureListenerObject);
+ screenCaptureListenerObject = nullptr;
+ }
+ }
+
+ status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) {
+ JNIEnv* env = getenv();
+ if (captureResults.result != NO_ERROR || captureResults.buffer == nullptr) {
+ env->CallVoidMethod(screenCaptureListenerObject,
+ gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr);
+ return NO_ERROR;
+ }
+ jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+ env, captureResults.buffer->toAHardwareBuffer());
+ const jint namedColorSpace =
+ fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
+ jobject screenshotHardwareBuffer =
+ env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
+ gScreenshotHardwareBufferClassInfo.builder,
+ jhardwareBuffer, namedColorSpace,
+ captureResults.capturedSecureLayers);
+ env->CallVoidMethod(screenCaptureListenerObject,
+ gScreenCaptureListenerClassInfo.onScreenCaptureComplete,
+ screenshotHardwareBuffer);
+ return NO_ERROR;
+ }
+
+private:
+ jobject screenCaptureListenerObject;
+ JavaVM* mVm;
+
+ JNIEnv* getenv() {
+ JNIEnv* env;
+ mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+ return env;
+ }
+};
+
// ----------------------------------------------------------------------------
static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) {
@@ -327,36 +381,28 @@ static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
return captureArgs;
}
-static jobject nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject) {
+static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
+ jobject screenCaptureListenerObject) {
const DisplayCaptureArgs captureArgs =
displayCaptureArgsFromObject(env, displayCaptureArgsObject);
if (captureArgs.displayToken == NULL) {
- return NULL;
- }
-
- ScreenCaptureResults captureResults;
- status_t res = ScreenshotClient::captureDisplay(captureArgs, captureResults);
- if (res != NO_ERROR) {
- return NULL;
+ return BAD_VALUE;
}
- jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
- env, captureResults.buffer->toAHardwareBuffer());
- const jint namedColorSpace =
- fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
- return env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
- gScreenshotHardwareBufferClassInfo.builder, jhardwareBuffer,
- namedColorSpace, captureResults.capturedSecureLayers);
+ sp<IScreenCaptureListener> captureListener =
+ new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject);
+ return ScreenshotClient::captureDisplay(captureArgs, captureListener);
}
-static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject) {
+static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
+ jobject screenCaptureListenerObject) {
LayerCaptureArgs captureArgs;
getCaptureArgs(env, layerCaptureArgsObject, captureArgs);
SurfaceControl* layer = reinterpret_cast<SurfaceControl*>(
env->GetLongField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.layer));
if (layer == nullptr) {
- return nullptr;
+ return BAD_VALUE;
}
captureArgs.layerHandle = layer->getHandle();
@@ -380,19 +426,9 @@ static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptu
env->ReleaseLongArrayElements(excludeObjectArray, const_cast<jlong*>(objects), JNI_ABORT);
}
- ScreenCaptureResults captureResults;
- status_t res = ScreenshotClient::captureLayers(captureArgs, captureResults);
- if (res != NO_ERROR) {
- return NULL;
- }
-
- jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
- env, captureResults.buffer->toAHardwareBuffer());
- const jint namedColorSpace =
- fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
- return env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
- gScreenshotHardwareBufferClassInfo.builder, jhardwareBuffer,
- namedColorSpace, captureResults.capturedSecureLayers);
+ sp<IScreenCaptureListener> captureListener =
+ new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject);
+ return ScreenshotClient::captureLayers(captureArgs, captureListener);
}
static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) {
@@ -1507,6 +1543,7 @@ static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
// ----------------------------------------------------------------------------
+// clang-format off
static const JNINativeMethod sSurfaceControlMethods[] = {
{"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJLandroid/os/Parcel;)J",
(void*)nativeCreate },
@@ -1649,12 +1686,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
{"nativeSetOverrideScalingMode", "(JJI)V",
(void*)nativeSetOverrideScalingMode },
{"nativeCaptureDisplay",
- "(Landroid/view/SurfaceControl$DisplayCaptureArgs;)"
- "Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;",
+ "(Landroid/view/SurfaceControl$DisplayCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I",
(void*)nativeCaptureDisplay },
{"nativeCaptureLayers",
- "(Landroid/view/SurfaceControl$LayerCaptureArgs;)"
- "Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;",
+ "(Landroid/view/SurfaceControl$LayerCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I",
(void*)nativeCaptureLayers },
{"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
(void*)nativeSetInputWindowInfo },
@@ -1688,6 +1723,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeGetHandle },
{"nativeSetFixedTransformHint", "(JJI)V", (void*)nativeSetFixedTransformHint},
};
+// clang-format on
int register_android_view_SurfaceControl(JNIEnv* env)
{
@@ -1856,6 +1892,12 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gLayerCaptureArgsClassInfo.childrenOnly =
GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z");
+ jclass screenCaptureListenerClazz =
+ FindClassOrDie(env, "android/view/SurfaceControl$ScreenCaptureListener");
+ gScreenCaptureListenerClassInfo.clazz = MakeGlobalRefOrDie(env, screenCaptureListenerClazz);
+ gScreenCaptureListenerClassInfo.onScreenCaptureComplete =
+ GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete",
+ "(Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;)V");
return err;
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5f2e4f905b1c..550162a242dc 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2258,7 +2258,7 @@
<!-- Amount of time in ms the user needs to press the relevant keys to trigger the
screenshot chord -->
- <integer name="config_screenshotChordKeyTimeout">500</integer>
+ <integer name="config_screenshotChordKeyTimeout">0</integer>
<!-- Default width of a vertical scrollbar and height of a horizontal scrollbar.
Takes effect only if the scrollbar drawables have no intrinsic size. -->
@@ -2783,6 +2783,7 @@
<item>power</item>
<item>restart</item>
<item>logout</item>
+ <item>screenshot</item>
<item>bugreport</item>
</string-array>
diff --git a/core/tests/powertests/PowerStatsLoadTests/Android.bp b/core/tests/powertests/PowerStatsLoadTests/Android.bp
new file mode 100644
index 000000000000..66c91adc6540
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/Android.bp
@@ -0,0 +1,13 @@
+android_test {
+ name: "PowerStatsLoadTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "compatibility-device-util-axt",
+ "junit",
+ ],
+ libs: ["android.test.runner"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml b/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml
new file mode 100644
index 000000000000..b1c2a639aff4
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.core.powerstatsloadtests">
+
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.BATTERY_STATS"/>
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.core.powerstatsloadtests"
+ android:label="Power Stats Load Tests" />
+
+ <queries>
+ <!-- The load test resolves http://... intents. Let it do so. -->
+ <package android:name="com.android.chrome"/>
+ </queries>
+</manifest>
diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/ConnectivitySetupRule.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/ConnectivitySetupRule.java
new file mode 100644
index 000000000000..ca2942647f08
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/ConnectivitySetupRule.java
@@ -0,0 +1,159 @@
+/*
+ * 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 com.android.frameworks.core.powerstatsloadtests;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ConnectivitySetupRule implements TestRule {
+
+ private final boolean mWifiEnabled;
+ private final ConnectivityManager mConnectivityManager;
+ private final WifiManager mWifiManager;
+ private boolean mInitialWifiState;
+
+ public ConnectivitySetupRule(boolean wifiEnabled) {
+ mWifiEnabled = wifiEnabled;
+
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ Context context = instrumentation.getContext();
+ mConnectivityManager = context.getSystemService(ConnectivityManager.class);
+ mWifiManager = context.getSystemService(WifiManager.class);
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ mInitialWifiState = isWiFiConnected();
+ setWiFiState(mWifiEnabled);
+ base.evaluate();
+ } finally {
+ setWiFiState(mInitialWifiState);
+ }
+ }
+ };
+ }
+
+ private void setWiFiState(final boolean enable) throws InterruptedException {
+ boolean wiFiConnected = isWiFiConnected();
+ if (enable == wiFiConnected) {
+ return;
+ }
+
+ NetworkTracker tracker = new NetworkTracker(!mWifiEnabled);
+ mConnectivityManager.registerNetworkCallback(
+ new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED).build(),
+ tracker);
+
+ if (enable) {
+ SystemUtil.runShellCommand("svc wifi enable");
+ //noinspection deprecation
+ SystemUtil.runWithShellPermissionIdentity(mWifiManager::reconnect,
+ android.Manifest.permission.NETWORK_SETTINGS);
+ } else {
+ SystemUtil.runShellCommand("svc wifi disable");
+ }
+
+ tracker.waitForExpectedState();
+
+ assertEquals("Wifi must be " + (enable ? "connected to" : "disconnected from")
+ + " an access point for this test.", enable, isWiFiConnected());
+
+ mConnectivityManager.unregisterNetworkCallback(tracker);
+ }
+
+ private boolean isWiFiConnected() {
+ return mWifiManager.isWifiEnabled() && mConnectivityManager.getActiveNetwork() != null
+ && !mConnectivityManager.isActiveNetworkMetered();
+ }
+
+ private class NetworkTracker extends ConnectivityManager.NetworkCallback {
+ private static final int MSG_CHECK_ACTIVE_NETWORK = 1;
+
+ private final CountDownLatch mReceiveLatch = new CountDownLatch(1);
+
+ private final boolean mExpectedMetered;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_CHECK_ACTIVE_NETWORK) {
+ checkActiveNetwork();
+ }
+ }
+ };
+
+ private NetworkTracker(boolean expectedMetered) {
+ mExpectedMetered = expectedMetered;
+ }
+
+ @Override
+ public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties, boolean blocked) {
+ checkActiveNetwork();
+ }
+
+ @Override
+ public void onLost(Network network) {
+ checkActiveNetwork();
+ }
+
+ boolean waitForExpectedState() throws InterruptedException {
+ checkActiveNetwork();
+ return mReceiveLatch.await(60, TimeUnit.SECONDS);
+ }
+
+ private void checkActiveNetwork() {
+ if (mReceiveLatch.getCount() == 0) {
+ return;
+ }
+
+ if (mConnectivityManager.getActiveNetwork() != null
+ && mConnectivityManager.isActiveNetworkMetered() == mExpectedMetered) {
+ mReceiveLatch.countDown();
+ } else {
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_ACTIVE_NETWORK, 5000);
+ }
+ }
+ }
+}
diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetrics.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetrics.java
new file mode 100644
index 000000000000..88cb719add60
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetrics.java
@@ -0,0 +1,278 @@
+/*
+ * 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 com.android.frameworks.core.powerstatsloadtests;
+
+import android.os.Process;
+
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PowerMetrics {
+ private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar";
+ private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media";
+ private static final String PACKAGE_SYSTEMUI = "com.android.systemui";
+ private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER,
+ PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI};
+
+ enum MetricKind {
+ POWER,
+ DURATION,
+ }
+
+ public static final String METRIC_APP_POWER = "appPower";
+ public static final String METRIC_APP_POWER_EXCLUDE_SYSTEM_FROM_TOTAL = "appPowerExcludeSystem";
+ public static final String METRIC_APP_POWER_EXCLUDE_SMEARED = "appPowerExcludeSmeared";
+ public static final String METRIC_SCREEN_POWER = "screenPower";
+ public static final String METRIC_WIFI_POWER = "wifiPower";
+ public static final String METRIC_SYSTEM_SERVICE_CPU_POWER = "systemService";
+ public static final String METRIC_OTHER_POWER = "otherPower";
+ public static final String METRIC_CPU_POWER = "cpuPower";
+ public static final String METRIC_RAM_POWER = "ramPower";
+ public static final String METRIC_WAKELOCK_POWER = "wakelockPower";
+ public static final String METRIC_MOBILE_RADIO_POWER = "mobileRadioPower";
+ public static final String METRIC_BLUETOOTH_POWER = "bluetoothPower";
+ public static final String METRIC_GPS_POWER = "gpsPower";
+ public static final String METRIC_CAMERA_POWER = "cameraPower";
+ public static final String METRIC_FLASHLIGHT_POWER = "flashlightPower";
+ public static final String METRIC_SENSORS_POWER = "sensorsPower";
+ public static final String METRIC_AUDIO_POWER = "audioPower";
+ public static final String METRIC_VIDEO_POWER = "videoPower";
+ public static final String METRIC_CPU_TIME = "cpuTime";
+ public static final String METRIC_CPU_FOREGROUND_TIME = "cpuForegroundTime";
+ public static final String METRIC_WAKELOCK_TIME = "wakelockTime";
+ public static final String METRIC_WIFI_RUNNING_TIME = "wifiRunningTime";
+ public static final String METRIC_BLUETOOTH_RUNNING_TIME = "bluetoothRunningTime";
+ public static final String METRIC_GPS_TIME = "gpsTime";
+ public static final String METRIC_CAMERA_TIME = "cameraTime";
+ public static final String METRIC_FLASHLIGHT_TIME = "flashlightTime";
+ public static final String METRIC_AUDIO_TIME = "audioTime";
+ public static final String METRIC_VIDEO_TIME = "videoTime";
+
+ public static class Metric {
+ public String metricType;
+ public MetricKind metricKind;
+ public String title;
+ public double value;
+ public double total;
+ }
+
+ private final double mMinDrainedPower;
+ private final double mMaxDrainedPower;
+
+ private List<Metric> mMetrics = new ArrayList<>();
+
+ public PowerMetrics(BatteryStatsHelper batteryStatsHelper, int uid) {
+ mMinDrainedPower = batteryStatsHelper.getMinDrainedPower();
+ mMaxDrainedPower = batteryStatsHelper.getMaxDrainedPower();
+
+ List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
+
+ double totalPowerMah = 0;
+ double totalSmearedPowerMah = 0;
+ double totalPowerExcludeSystemMah = 0;
+ double totalScreenPower = 0;
+ double totalProportionalSmearMah = 0;
+ double totalCpuPowerMah = 0;
+ double totalSystemServiceCpuPowerMah = 0;
+ double totalUsagePowerMah = 0;
+ double totalWakeLockPowerMah = 0;
+ double totalMobileRadioPowerMah = 0;
+ double totalWifiPowerMah = 0;
+ double totalBluetoothPowerMah = 0;
+ double totalGpsPowerMah = 0;
+ double totalCameraPowerMah = 0;
+ double totalFlashlightPowerMah = 0;
+ double totalSensorPowerMah = 0;
+ double totalAudioPowerMah = 0;
+ double totalVideoPowerMah = 0;
+
+ long totalCpuTimeMs = 0;
+ long totalCpuFgTimeMs = 0;
+ long totalWakeLockTimeMs = 0;
+ long totalWifiRunningTimeMs = 0;
+ long totalBluetoothRunningTimeMs = 0;
+ long totalGpsTimeMs = 0;
+ long totalCameraTimeMs = 0;
+ long totalFlashlightTimeMs = 0;
+ long totalAudioTimeMs = 0;
+ long totalVideoTimeMs = 0;
+
+ BatterySipper uidSipper = null;
+ for (BatterySipper sipper : usageList) {
+ if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
+ totalScreenPower = sipper.sumPower();
+ }
+
+ if (isHiddenDrainType(sipper.drainType)) {
+ continue;
+ }
+
+ if (sipper.drainType == BatterySipper.DrainType.APP && sipper.getUid() == uid) {
+ uidSipper = sipper;
+ }
+
+ totalPowerMah += sipper.sumPower();
+ totalSmearedPowerMah += sipper.totalSmearedPowerMah;
+ totalProportionalSmearMah += sipper.proportionalSmearMah;
+
+ if (!isSystemSipper(sipper)) {
+ totalPowerExcludeSystemMah += sipper.totalSmearedPowerMah;
+ }
+
+ totalCpuPowerMah += sipper.cpuPowerMah;
+ totalSystemServiceCpuPowerMah += sipper.systemServiceCpuPowerMah;
+ totalUsagePowerMah += sipper.usagePowerMah;
+ totalWakeLockPowerMah += sipper.wakeLockPowerMah;
+ totalMobileRadioPowerMah += sipper.mobileRadioPowerMah;
+ totalWifiPowerMah += sipper.wifiPowerMah;
+ totalBluetoothPowerMah += sipper.bluetoothPowerMah;
+ totalGpsPowerMah += sipper.gpsPowerMah;
+ totalCameraPowerMah += sipper.cameraPowerMah;
+ totalFlashlightPowerMah += sipper.flashlightPowerMah;
+ totalSensorPowerMah += sipper.sensorPowerMah;
+ totalAudioPowerMah += sipper.audioPowerMah;
+ totalVideoPowerMah += sipper.videoPowerMah;
+
+ totalCpuTimeMs += sipper.cpuTimeMs;
+ totalCpuFgTimeMs += sipper.cpuFgTimeMs;
+ totalWakeLockTimeMs += sipper.wakeLockTimeMs;
+ totalWifiRunningTimeMs += sipper.wifiRunningTimeMs;
+ totalBluetoothRunningTimeMs += sipper.bluetoothRunningTimeMs;
+ totalGpsTimeMs += sipper.gpsTimeMs;
+ totalCameraTimeMs += sipper.cameraTimeMs;
+ totalFlashlightTimeMs += sipper.flashlightTimeMs;
+ totalAudioTimeMs += sipper.audioTimeMs;
+ totalVideoTimeMs += sipper.videoTimeMs;
+ }
+
+ if (uidSipper == null) {
+ return;
+ }
+
+ addMetric(METRIC_APP_POWER, MetricKind.POWER, "Total power",
+ uidSipper.totalSmearedPowerMah, totalSmearedPowerMah);
+ addMetric(METRIC_APP_POWER_EXCLUDE_SYSTEM_FROM_TOTAL, MetricKind.POWER,
+ "Total power excluding system",
+ uidSipper.totalSmearedPowerMah, totalPowerExcludeSystemMah);
+ addMetric(METRIC_SCREEN_POWER, MetricKind.POWER, "Screen, smeared",
+ uidSipper.screenPowerMah, totalScreenPower);
+ addMetric(METRIC_OTHER_POWER, MetricKind.POWER, "Other, smeared",
+ uidSipper.proportionalSmearMah, totalProportionalSmearMah);
+ addMetric(METRIC_APP_POWER_EXCLUDE_SMEARED, MetricKind.POWER, "Excluding smeared",
+ uidSipper.totalPowerMah, totalPowerMah);
+ addMetric(METRIC_CPU_POWER, MetricKind.POWER, "CPU",
+ uidSipper.cpuPowerMah, totalCpuPowerMah);
+ addMetric(METRIC_SYSTEM_SERVICE_CPU_POWER, MetricKind.POWER, "System services",
+ uidSipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah);
+ addMetric(METRIC_RAM_POWER, MetricKind.POWER, "RAM",
+ uidSipper.usagePowerMah, totalUsagePowerMah);
+ addMetric(METRIC_WAKELOCK_POWER, MetricKind.POWER, "Wake lock",
+ uidSipper.wakeLockPowerMah, totalWakeLockPowerMah);
+ addMetric(METRIC_MOBILE_RADIO_POWER, MetricKind.POWER, "Mobile radio",
+ uidSipper.mobileRadioPowerMah, totalMobileRadioPowerMah);
+ addMetric(METRIC_WIFI_POWER, MetricKind.POWER, "WiFi",
+ uidSipper.wifiPowerMah, totalWifiPowerMah);
+ addMetric(METRIC_BLUETOOTH_POWER, MetricKind.POWER, "Bluetooth",
+ uidSipper.bluetoothPowerMah, totalBluetoothPowerMah);
+ addMetric(METRIC_GPS_POWER, MetricKind.POWER, "GPS",
+ uidSipper.gpsPowerMah, totalGpsPowerMah);
+ addMetric(METRIC_CAMERA_POWER, MetricKind.POWER, "Camera",
+ uidSipper.cameraPowerMah, totalCameraPowerMah);
+ addMetric(METRIC_FLASHLIGHT_POWER, MetricKind.POWER, "Flashlight",
+ uidSipper.flashlightPowerMah, totalFlashlightPowerMah);
+ addMetric(METRIC_SENSORS_POWER, MetricKind.POWER, "Sensors",
+ uidSipper.sensorPowerMah, totalSensorPowerMah);
+ addMetric(METRIC_AUDIO_POWER, MetricKind.POWER, "Audio",
+ uidSipper.audioPowerMah, totalAudioPowerMah);
+ addMetric(METRIC_VIDEO_POWER, MetricKind.POWER, "Video",
+ uidSipper.videoPowerMah, totalVideoPowerMah);
+
+ addMetric(METRIC_CPU_TIME, MetricKind.DURATION, "CPU time",
+ uidSipper.cpuTimeMs, totalCpuTimeMs);
+ addMetric(METRIC_CPU_FOREGROUND_TIME, MetricKind.DURATION, "CPU foreground time",
+ uidSipper.cpuFgTimeMs, totalCpuFgTimeMs);
+ addMetric(METRIC_WAKELOCK_TIME, MetricKind.DURATION, "Wake lock time",
+ uidSipper.wakeLockTimeMs, totalWakeLockTimeMs);
+ addMetric(METRIC_WIFI_RUNNING_TIME, MetricKind.DURATION, "WiFi running time",
+ uidSipper.wifiRunningTimeMs, totalWifiRunningTimeMs);
+ addMetric(METRIC_BLUETOOTH_RUNNING_TIME, MetricKind.DURATION, "Bluetooth time",
+ uidSipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs);
+ addMetric(METRIC_GPS_TIME, MetricKind.DURATION, "GPS time",
+ uidSipper.gpsTimeMs, totalGpsTimeMs);
+ addMetric(METRIC_CAMERA_TIME, MetricKind.DURATION, "Camera time",
+ uidSipper.cameraTimeMs, totalCameraTimeMs);
+ addMetric(METRIC_FLASHLIGHT_TIME, MetricKind.DURATION, "Flashlight time",
+ uidSipper.flashlightTimeMs, totalFlashlightTimeMs);
+ addMetric(METRIC_AUDIO_TIME, MetricKind.DURATION, "Audio time",
+ uidSipper.audioTimeMs, totalAudioTimeMs);
+ addMetric(METRIC_VIDEO_TIME, MetricKind.DURATION, "Video time",
+ uidSipper.videoTimeMs, totalVideoTimeMs);
+ }
+
+ public List<Metric> getMetrics() {
+ return mMetrics;
+ }
+
+ public double getMinDrainedPower() {
+ return mMinDrainedPower;
+ }
+
+ public double getMaxDrainedPower() {
+ return mMaxDrainedPower;
+ }
+
+ protected boolean isHiddenDrainType(BatterySipper.DrainType drainType) {
+ return drainType == BatterySipper.DrainType.IDLE
+ || drainType == BatterySipper.DrainType.CELL
+ || drainType == BatterySipper.DrainType.SCREEN
+ || drainType == BatterySipper.DrainType.UNACCOUNTED
+ || drainType == BatterySipper.DrainType.OVERCOUNTED
+ || drainType == BatterySipper.DrainType.BLUETOOTH
+ || drainType == BatterySipper.DrainType.WIFI;
+ }
+
+ private boolean isSystemSipper(BatterySipper sipper) {
+ final int uid = sipper.uidObj == null ? -1 : sipper.getUid();
+ if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) {
+ return true;
+ } else if (sipper.mPackages != null) {
+ for (final String packageName : sipper.mPackages) {
+ for (final String systemPackage : PACKAGES_SYSTEM) {
+ if (systemPackage.equals(packageName)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private void addMetric(String metricType, MetricKind metricKind, String title, double amount,
+ double totalAmount) {
+ Metric metric = new Metric();
+ metric.metricType = metricType;
+ metric.metricKind = metricKind;
+ metric.title = title;
+ metric.value = amount;
+ metric.total = totalAmount;
+ mMetrics.add(metric);
+ }
+}
diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java
new file mode 100644
index 000000000000..0cdb404f6aaa
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java
@@ -0,0 +1,281 @@
+/*
+ * 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 com.android.frameworks.core.powerstatsloadtests;
+
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserManager;
+import android.util.Log;
+import android.util.TimeUtils;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.internal.os.LoggingPrintStream;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class PowerMetricsCollector implements TestRule {
+ private final String mTag;
+ private final float mBatteryDrainThresholdPct;
+ private final int mTimeoutMillis;
+
+ private final Context mContext;
+ private final UserManager mUserManager;
+ private final int mUid;
+ private final BatteryStatsHelper mStatsHelper;
+
+ private long mStartTime;
+ private volatile float mInitialBatteryLevel;
+ private volatile float mCurrentBatteryLevel;
+ private int mIterations;
+ private PowerMetrics mInitialPowerMetrics;
+ private PowerMetrics mFinalPowerMetrics;
+ private List<PowerMetrics.Metric> mPowerMetricsDelta;
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ disableCharger();
+ try {
+ prepareBatteryLevelMonitor();
+ mStartTime = SystemClock.uptimeMillis();
+ base.evaluate();
+ captureFinalPowerStatsData();
+ } finally {
+ enableCharger();
+ }
+ }
+ };
+ }
+
+ public PowerMetricsCollector(String tag, float batteryDrainThresholdPct, int timeoutMillis) {
+ mTag = tag;
+ mBatteryDrainThresholdPct = batteryDrainThresholdPct;
+ mTimeoutMillis = timeoutMillis;
+
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = instrumentation.getContext();
+ mUid = Process.myUid();
+ mUserManager = mContext.getSystemService(UserManager.class);
+ mStatsHelper = new BatteryStatsHelper(mContext, false /* collectBatteryBroadcast */);
+ mStatsHelper.create((Bundle) null);
+ }
+
+ private void disableCharger() {
+ // TODO(b/167636754): implement this method once the charger suspension API is available
+ }
+
+ private void enableCharger() {
+ // TODO(b/167636754): implement this method once the charger suspension API is available
+ }
+
+ private PowerMetrics readBatteryStatsData() {
+ mStatsHelper.clearStats();
+ mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
+ mUserManager.getUserProfiles());
+ return new PowerMetrics(mStatsHelper, mUid);
+ }
+
+ protected void prepareBatteryLevelMonitor() {
+ Intent batteryStatus = mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ handleBatteryStatus(intent);
+ }
+ }, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+
+ handleBatteryStatus(batteryStatus);
+ mInitialBatteryLevel = mCurrentBatteryLevel;
+ }
+
+ protected void handleBatteryStatus(Intent intent) {
+ if (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0) {
+ fail("Device must remain disconnected from the power source "
+ + "for the duration of the test");
+ }
+
+ int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+
+ mCurrentBatteryLevel = level * 100 / (float) scale;
+ Log.i(mTag, "Battery level = " + mCurrentBatteryLevel);
+
+ // We delay tracking until the battery level drops. If the resolution of
+ // battery level is 1%, and the initially reported level is 73, we don't know whether
+ // it's 73.1 or 73.7. Once it drops to 72, we can be confident that the real battery
+ // level it is very close to 72.0 and can start tracking.
+ if (mInitialPowerMetrics == null && mCurrentBatteryLevel < mInitialBatteryLevel) {
+ mInitialBatteryLevel = mCurrentBatteryLevel;
+ mInitialPowerMetrics = readBatteryStatsData();
+ }
+ }
+
+ private void captureFinalPowerStatsData() {
+ if (mFinalPowerMetrics != null) {
+ return;
+ }
+
+ mFinalPowerMetrics = readBatteryStatsData();
+
+ mPowerMetricsDelta = new ArrayList<>();
+ List<PowerMetrics.Metric> initialPowerMetrics = mInitialPowerMetrics.getMetrics();
+ List<PowerMetrics.Metric> finalPowerMetrics = mFinalPowerMetrics.getMetrics();
+ for (PowerMetrics.Metric initialMetric : initialPowerMetrics) {
+ PowerMetrics.Metric finalMetric = null;
+ for (PowerMetrics.Metric metric : finalPowerMetrics) {
+ if (metric.title.equals(initialMetric.title)) {
+ finalMetric = metric;
+ break;
+ }
+ }
+
+ if (finalMetric != null) {
+ PowerMetrics.Metric delta = new PowerMetrics.Metric();
+ delta.metricType = initialMetric.metricType;
+ delta.metricKind = initialMetric.metricKind;
+ delta.title = initialMetric.title;
+ delta.total = finalMetric.total - initialMetric.total;
+ delta.value = finalMetric.value - initialMetric.value;
+ mPowerMetricsDelta.add(delta);
+ }
+ }
+ }
+
+ /**
+ * Returns false if sufficient data has been accumulated.
+ */
+ public boolean checkpoint() {
+ long elapsedTime = SystemClock.uptimeMillis() - mStartTime;
+ if (elapsedTime >= mTimeoutMillis) {
+ Log.i(mTag, "Timeout reached " + TimeUtils.formatDuration(elapsedTime));
+ captureFinalPowerStatsData();
+ return false;
+ }
+
+ if (mInitialPowerMetrics == null) {
+ return true;
+ }
+
+ if (mInitialBatteryLevel - mCurrentBatteryLevel >= mBatteryDrainThresholdPct) {
+ Log.i(mTag,
+ "Battery drain reached " + (mInitialBatteryLevel - mCurrentBatteryLevel) + "%");
+ captureFinalPowerStatsData();
+ return false;
+ }
+
+ mIterations++;
+ return true;
+ }
+
+
+ public int getIterationCount() {
+ return mIterations;
+ }
+
+ public void dumpMetrics() {
+ dumpMetrics(new LoggingPrintStream() {
+ @Override
+ protected void log(String line) {
+ Log.i(mTag, line);
+ }
+ });
+ }
+
+ public void dumpMetrics(PrintStream out) {
+ List<PowerMetrics.Metric> initialPowerMetrics = mInitialPowerMetrics.getMetrics();
+ List<PowerMetrics.Metric> finalPowerMetrics = mFinalPowerMetrics.getMetrics();
+
+ out.println("== Power metrics at test start");
+ dumpPowerStatsData(out, initialPowerMetrics);
+
+ out.println("== Power metrics at test end");
+ dumpPowerStatsData(out, finalPowerMetrics);
+
+ out.println("== Power metrics delta");
+ dumpPowerStatsData(out, mPowerMetricsDelta);
+ }
+
+ protected void dumpPowerStatsData(PrintStream out, List<PowerMetrics.Metric> metrics) {
+ Locale locale = Locale.getDefault();
+ for (PowerMetrics.Metric metric : metrics) {
+ double proportion = metric.total != 0 ? metric.value * 100 / metric.total : 0;
+ switch (metric.metricKind) {
+ case POWER:
+ out.println(
+ String.format(locale, " %-30s %7.1f mAh %4.1f%%", metric.title,
+ metric.value, proportion));
+ break;
+ case DURATION:
+ out.println(
+ String.format(locale, " %-30s %,7d ms %4.1f%%", metric.title,
+ (long) metric.value, proportion));
+ break;
+ }
+ }
+ }
+
+ public void dumpMetricAsPercentageOfDrainedPower(String metricType) {
+ double minDrainedPower =
+ mFinalPowerMetrics.getMinDrainedPower() - mInitialPowerMetrics.getMinDrainedPower();
+ double maxDrainedPower =
+ mFinalPowerMetrics.getMaxDrainedPower() - mInitialPowerMetrics.getMaxDrainedPower();
+
+ PowerMetrics.Metric metric = getMetric(metricType);
+ double metricDelta = metric.value;
+
+ if (maxDrainedPower - minDrainedPower < 0.1f) {
+ Log.i(mTag, String.format(Locale.getDefault(),
+ "%s power consumed by the test: %.1f of %.1f mAh (%.1f%%)",
+ metric.title, metricDelta, maxDrainedPower,
+ metricDelta / maxDrainedPower * 100));
+ } else {
+ Log.i(mTag, String.format(Locale.getDefault(),
+ "%s power consumed by the test: %.1f of %.1f - %.1f mAh (%.1f%% - %.1f%%)",
+ metric.title, metricDelta, minDrainedPower, maxDrainedPower,
+ metricDelta / minDrainedPower * 100, metricDelta / maxDrainedPower * 100));
+ }
+ }
+
+ public PowerMetrics.Metric getMetric(String metricType) {
+ for (PowerMetrics.Metric metric : mPowerMetricsDelta) {
+ if (metric.metricType.equals(metricType)) {
+ return metric;
+ }
+ }
+ return null;
+ }
+}
diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/SystemServiceCallLoadTest.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/SystemServiceCallLoadTest.java
new file mode 100644
index 000000000000..911ccba3ac78
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/SystemServiceCallLoadTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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 com.android.frameworks.core.powerstatsloadtests;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class SystemServiceCallLoadTest {
+ private static final String TAG = "SystemServiceCallLoadTest";
+ private static final int TIMEOUT_MILLIS = 60 * 60 * 1000;
+ private static final float BATTERY_DRAIN_THRESHOLD_PCT = 2.99f;
+
+ @Rule
+ public PowerMetricsCollector mPowerMetricsCollector = new PowerMetricsCollector(TAG,
+ BATTERY_DRAIN_THRESHOLD_PCT, TIMEOUT_MILLIS);
+
+ private PackageManager mPackageManager;
+
+ @Before
+ public void setup() {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ mPackageManager = instrumentation.getContext().getPackageManager();
+ }
+
+ @Test
+ public void test() {
+ while (mPowerMetricsCollector.checkpoint()) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.parse("http://example.com/"), "text/plain");
+ intent.addCategory(Intent.CATEGORY_BROWSABLE);
+ ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0);
+ assertNotNull(resolveInfo);
+ }
+
+ mPowerMetricsCollector.dumpMetrics();
+
+ Log.i(TAG, "==");
+ Log.i(TAG, "Total system server calls made " + mPowerMetricsCollector.getIterationCount());
+
+ mPowerMetricsCollector.dumpMetricAsPercentageOfDrainedPower(
+ PowerMetrics.METRIC_SYSTEM_SERVICE_CPU_POWER);
+ }
+}
diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/WiFiLoadTest.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/WiFiLoadTest.java
new file mode 100644
index 000000000000..90627192946d
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/WiFiLoadTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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 com.android.frameworks.core.powerstatsloadtests;
+
+import android.util.Log;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+public class WiFiLoadTest {
+ private static final String TAG = "WiFiLoadTest";
+ private static final String DOWNLOAD_TEST_URL =
+ "https://i.ytimg.com/vi/l5mE3Tpjejs/maxresdefault.jpg";
+
+ private static final int TIMEOUT_MILLIS = 60 * 60 * 1000;
+ private static final float BATTERY_DRAIN_THRESHOLD_PCT = 0.99f;
+
+ @Rule
+ public PowerMetricsCollector mPowerMetricsCollector = new PowerMetricsCollector(TAG,
+ BATTERY_DRAIN_THRESHOLD_PCT, TIMEOUT_MILLIS);
+
+ @Rule
+ public ConnectivitySetupRule mConnectivitySetupRule =
+ new ConnectivitySetupRule(/* WiFi enabled */true);
+
+ @Test
+ public void test() throws IOException {
+ long totalBytesRead = 0;
+ URL url = new URL(DOWNLOAD_TEST_URL);
+ byte[] buffer = new byte[131072]; // Large buffer to minimize CPU usage
+
+ while (mPowerMetricsCollector.checkpoint()) {
+ try (InputStream inputStream = url.openStream()) {
+ while (true) {
+ int count = inputStream.read(buffer);
+ if (count < 0) {
+ break;
+ }
+ totalBytesRead += count;
+ }
+ }
+ }
+
+ mPowerMetricsCollector.dumpMetrics();
+
+ Log.i(TAG, "==");
+ Log.i(TAG, "WiFi running time: " + (long) mPowerMetricsCollector.getMetric(
+ PowerMetrics.METRIC_WIFI_RUNNING_TIME).value);
+ Log.i(TAG, "Total bytes read over WiFi: " + totalBytesRead);
+
+ mPowerMetricsCollector.dumpMetricAsPercentageOfDrainedPower(
+ PowerMetrics.METRIC_WIFI_POWER);
+ }
+}
diff --git a/framework-jarjar-rules.txt b/framework-jarjar-rules.txt
index 70dedb8179b0..d8af726ffa72 100644
--- a/framework-jarjar-rules.txt
+++ b/framework-jarjar-rules.txt
@@ -1,6 +1,2 @@
rule android.hidl.** android.internal.hidl.@1
rule android.net.wifi.WifiAnnotations* android.internal.wifi.WifiAnnotations@1
-
-# Hide media mainline module implementation classes to avoid collisions with
-# app-bundled ExoPlayer classes.
-rule com.google.android.exoplayer2.** android.media.internal.exo.@1
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 6fc702e4a068..04bcbfc5f6b8 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -164,7 +164,7 @@ public class LocationManager {
/**
* The fused location provider.
*
- * <p>This provider combines may combine inputs from several location sources to provide the
+ * <p>This provider may combine inputs from several location sources to provide the
* best possible location fix. It is implicitly used for all API's that involve the
* {@link LocationRequest} object.
*
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..e1e55c25b3fa 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.
@@ -6306,6 +6649,132 @@ public class AudioManager {
}
}
+ /**
+ * Adjusts the volume of the most relevant stream, or the given fallback
+ * stream.
+ * <p>
+ * This method should only be used by applications that replace the
+ * platform-wide management of audio settings or the main telephony
+ * application.
+ * <p>
+ * This method has no effect if the device implements a fixed volume policy
+ * as indicated by {@link #isVolumeFixed()}.
+ * <p>This API checks if the caller has the necessary permissions based on the provided
+ * component name, uid, and pid values.
+ * See {@link #adjustSuggestedStreamVolume(int, int, int)}.
+ *
+ * @param suggestedStreamType The stream type that will be used if there
+ * isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is
+ * valid here.
+ * @param direction The direction to adjust the volume. One of
+ * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
+ * {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
+ * {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
+ * @param flags One or more flags.
+ * @param packageName the package name of client application
+ * @param uid the uid of client application
+ * @param pid the pid of client application
+ * @param targetSdkVersion the target sdk version of client application
+ * @see #adjustVolume(int, int)
+ * @see #adjustStreamVolume(int, int, int)
+ * @see #setStreamVolume(int, int, int)
+ * @see #isVolumeFixed()
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void adjustSuggestedStreamVolumeForUid(int suggestedStreamType, int direction, int flags,
+ @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
+ try {
+ getService().adjustSuggestedStreamVolumeForUid(suggestedStreamType, direction, flags,
+ packageName, uid, pid, UserHandle.getUserHandleForUid(uid), targetSdkVersion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Adjusts the volume of a particular stream by one step in a direction.
+ * <p>
+ * This method should only be used by applications that replace the platform-wide
+ * management of audio settings or the main telephony application.
+ * <p>This method has no effect if the device implements a fixed volume policy
+ * as indicated by {@link #isVolumeFixed()}.
+ * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
+ * unless the app has been granted Do Not Disturb Access.
+ * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
+ * <p>This API checks if the caller has the necessary permissions based on the provided
+ * component name, uid, and pid values.
+ * See {@link #adjustStreamVolume(int, int, int)}.
+ *
+ * @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL},
+ * {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC},
+ * {@link #STREAM_ALARM} or {@link #STREAM_ACCESSIBILITY}.
+ * @param direction The direction to adjust the volume. One of
+ * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
+ * {@link #ADJUST_SAME}.
+ * @param flags One or more flags.
+ * @param packageName the package name of client application
+ * @param uid the uid of client application
+ * @param pid the pid of client application
+ * @param targetSdkVersion the target sdk version of client application
+ * @see #adjustVolume(int, int)
+ * @see #setStreamVolume(int, int, int)
+ * @throws SecurityException if the adjustment triggers a Do Not Disturb change
+ * and the caller is not granted notification policy access.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+ @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
+ try {
+ getService().adjustStreamVolumeForUid(streamType, direction, flags, packageName, uid,
+ pid, UserHandle.getUserHandleForUid(uid), targetSdkVersion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the volume index for a particular stream.
+ * <p>This method has no effect if the device implements a fixed volume policy
+ * as indicated by {@link #isVolumeFixed()}.
+ * <p>From N onward, volume adjustments that would toggle Do Not Disturb are not allowed unless
+ * the app has been granted Do Not Disturb Access.
+ * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
+ * <p>This API checks if the caller has the necessary permissions based on the provided
+ * component name, uid, and pid values.
+ * See {@link #setStreamVolume(int, int, int)}.
+ *
+ * @param streamType The stream whose volume index should be set.
+ * @param index The volume index to set. See
+ * {@link #getStreamMaxVolume(int)} for the largest valid value.
+ * @param flags One or more flags.
+ * @param packageName the package name of client application
+ * @param uid the uid of client application
+ * @param pid the pid of client application
+ * @param targetSdkVersion the target sdk version of client application
+ * @see #getStreamMaxVolume(int)
+ * @see #getStreamVolume(int)
+ * @see #isVolumeFixed()
+ * @throws SecurityException if the volume change triggers a Do Not Disturb change
+ * and the caller is not granted notification policy access.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void setStreamVolumeForUid(int streamType, int index, int flags,
+ @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
+ try {
+ getService().setStreamVolumeForUid(streamType, index, flags, packageName, uid, pid,
+ UserHandle.getUserHandleForUid(uid), targetSdkVersion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
/** @hide
* TODO: make this a @SystemApi */
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index b44d7bba834f..c827932194ae 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -28,15 +28,6 @@ import com.android.server.LocalServices;
*/
public abstract class AudioManagerInternal {
- public abstract void adjustSuggestedStreamVolumeForUid(int streamType, int direction,
- int flags, String callingPackage, int uid, int pid);
-
- public abstract void adjustStreamVolumeForUid(int streamType, int direction, int flags,
- String callingPackage, int uid, int pid);
-
- public abstract void setStreamVolumeForUid(int streamType, int direction, int flags,
- String callingPackage, int uid, int pid);
-
public abstract void setRingerModeDelegate(RingerModeDelegate delegate);
public abstract int getRingerModeInternal();
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..47e60000cd04 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -17,6 +17,7 @@
package android.media;
import android.bluetooth.BluetoothDevice;
+import android.content.ComponentName;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioFocusInfo;
@@ -26,6 +27,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;
@@ -40,6 +42,7 @@ import android.media.audiopolicy.AudioVolumeGroup;
import android.media.audiopolicy.IAudioPolicyCallback;
import android.media.projection.IMediaProjection;
import android.net.Uri;
+import android.os.UserHandle;
import android.view.KeyEvent;
/**
@@ -307,4 +310,28 @@ 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);
+
+ oneway void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+ in String packageName, int uid, int pid, in UserHandle userHandle,
+ int targetSdkVersion);
+
+ oneway void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags,
+ in String packageName, int uid, int pid, in UserHandle userHandle,
+ int targetSdkVersion);
+
+ oneway void setStreamVolumeForUid(int streamType, int direction, int flags,
+ in String packageName, int uid, int pid, in UserHandle userHandle,
+ int targetSdkVersion);
}
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/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index a23191f36efc..523a072b957a 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -15,8 +15,10 @@
*/
package android.media;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.StringDef;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.graphics.Bitmap;
@@ -738,15 +740,16 @@ public final class MediaMetadata implements Parcelable {
/**
* Create a Builder using a {@link MediaMetadata} instance to set
- * initial values, but replace bitmaps with a scaled down copy if they
- * are larger than maxBitmapSize.
+ * initial values, but replace bitmaps with a scaled down copy if their width (or height)
+ * is larger than maxBitmapSize.
*
* @param source The original metadata to copy.
* @param maxBitmapSize The maximum height/width for bitmaps contained
* in the metadata.
* @hide
*/
- public Builder(MediaMetadata source, int maxBitmapSize) {
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public Builder(@NonNull MediaMetadata source, @IntRange(from = 1) int maxBitmapSize) {
this(source);
for (String key : mBundle.keySet()) {
Object value = mBundle.get(key);
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/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index d4c5e7ea375d..1bbbf983b5aa 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -35,9 +35,16 @@ package android.graphics {
package android.media {
public class AudioManager {
+ method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
+ method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
+ method public void setStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
+ public static final class MediaMetadata.Builder {
+ ctor public MediaMetadata.Builder(@NonNull android.media.MediaMetadata, @IntRange(from=1) int);
+ }
+
}
package android.media.session {
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/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 128af3702c49..68e404e36bba 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -43,6 +43,7 @@ class MagnificationModeSwitch {
private static final int DURATION_MS = 5000;
private static final int START_DELAY_MS = 3000;
+ private final Runnable mAnimationTask;
private final Context mContext;
private final WindowManager mWindowManager;
@@ -70,6 +71,14 @@ class MagnificationModeSwitch {
applyResourcesValues();
mImageView.setImageResource(getIconResId(mMagnificationMode));
mImageView.setOnTouchListener(this::onTouch);
+
+ mAnimationTask = () -> {
+ mImageView.animate()
+ .alpha(0f)
+ .setDuration(DURATION_MS)
+ .withEndAction(() -> removeButton())
+ .start();
+ };
}
private void applyResourcesValues() {
@@ -147,13 +156,8 @@ class MagnificationModeSwitch {
// Dismiss the magnification switch button after the button is displayed for a period of
// time.
mImageView.animate().cancel();
- mImageView.animate()
- .alpha(0f)
- .setStartDelay(START_DELAY_MS)
- .setDuration(DURATION_MS)
- .withEndAction(
- () -> removeButton())
- .start();
+ mImageView.removeCallbacks(mAnimationTask);
+ mImageView.postDelayed(mAnimationTask, START_DELAY_MS);
}
void onConfigurationChanged(int configDiff) {
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/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 31830b94e8e4..40662536e57e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -263,6 +263,7 @@ internal class ControlHolder(
val context = itemView.context
val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
+ icon.imageTintList = null
ci.customIcon?.let {
icon.setImageIcon(it)
} ?: run {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 371031020b14..2b529f9a6cde 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -104,6 +104,7 @@ class ControlsUiControllerImpl @Inject constructor (
private var hidden = true
private lateinit var dismissGlobalActions: Runnable
private val popupThemedContext = ContextThemeWrapper(context, R.style.Control_ListPopupWindow)
+ private var retainCache = false
private val collator = Collator.getInstance(context.resources.configuration.locales[0])
private val localeComparator = compareBy<SelectionItem, CharSequence>(collator) {
@@ -149,6 +150,7 @@ class ControlsUiControllerImpl @Inject constructor (
this.parent = parent
this.dismissGlobalActions = dismissGlobalActions
hidden = false
+ retainCache = false
allStructures = controlsController.get().getFavorites()
selectedStructure = loadPreference(allStructures)
@@ -235,6 +237,8 @@ class ControlsUiControllerImpl @Inject constructor (
}
putIntentExtras(i, si)
startActivity(context, i)
+
+ retainCache = true
}
private fun putIntentExtras(intent: Intent, si: StructureInfo) {
@@ -497,7 +501,7 @@ class ControlsUiControllerImpl @Inject constructor (
controlsListingController.get().removeCallback(listingCallback)
- RenderInfo.clearCache()
+ if (!retainCache) RenderInfo.clearCache()
}
override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 3e64749c0dce..9c90510099a2 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -19,6 +19,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS;
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
@@ -548,7 +549,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
return false;
}
- return true;
+ return action.shouldShow();
}
/**
@@ -961,6 +962,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
@VisibleForTesting
class ScreenshotAction extends SinglePressAction implements LongPressAction {
+ final String KEY_SYSTEM_NAV_2BUTTONS = "system_nav_2buttons";
+
public ScreenshotAction() {
super(R.drawable.ic_screenshot, R.string.global_action_screenshot);
}
@@ -993,6 +996,19 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
@Override
+ public boolean shouldShow() {
+ // Include screenshot in power menu for legacy nav because it is not accessible
+ // through Recents in that mode
+ return is2ButtonNavigationEnabled();
+ }
+
+ boolean is2ButtonNavigationEnabled() {
+ return NAV_BAR_MODE_2BUTTON == mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode);
+ }
+
+
+ @Override
public boolean onLongPress() {
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SCREENRECORD_LONG_PRESS)) {
mUiEventLogger.log(GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS);
@@ -1615,6 +1631,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
* @return
*/
CharSequence getMessage();
+
+ default boolean shouldShow() {
+ return true;
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index e5a9ac10389f..f150381f4070 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -42,7 +42,7 @@ class MediaCarouselController @Inject constructor(
private val mediaHostStatesManager: MediaHostStatesManager,
private val activityStarter: ActivityStarter,
@Main executor: DelayableExecutor,
- mediaManager: MediaDataManager,
+ private val mediaManager: MediaDataManager,
configurationController: ConfigurationController,
falsingManager: FalsingManager
) {
@@ -109,6 +109,7 @@ class MediaCarouselController @Inject constructor(
private val pageIndicator: PageIndicator
private val visualStabilityCallback: VisualStabilityManager.Callback
private var needsReordering: Boolean = false
+ private var keysNeedRemoval = mutableSetOf<String>()
private var isRtl: Boolean = false
set(value) {
if (value != field) {
@@ -161,6 +162,10 @@ class MediaCarouselController @Inject constructor(
needsReordering = false
reorderAllPlayers()
}
+
+ keysNeedRemoval.forEach { removePlayer(it) }
+ keysNeedRemoval.clear()
+
// Let's reset our scroll position
mediaCarouselScrollHandler.scrollToStart()
}
@@ -168,13 +173,19 @@ class MediaCarouselController @Inject constructor(
true /* persistent */)
mediaManager.addListener(object : MediaDataManager.Listener {
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
- if (!data.active && !Utils.useMediaResumption(context)) {
- // This view is inactive, let's remove this! This happens e.g when dismissing /
- // timing out a view. We still have the data around because resumption could
- // be on, but we should save the resources and release this.
- onMediaDataRemoved(key)
+ addOrUpdatePlayer(key, oldKey, data)
+ val canRemove = data.isPlaying?.let { !it } ?: data.isClearable
+ if (canRemove && !Utils.useMediaResumption(context)) {
+ // This view isn't playing, let's remove this! This happens e.g when
+ // dismissing/timing out a view. We still have the data around because
+ // resumption could be on, but we should save the resources and release this.
+ if (visualStabilityManager.isReorderingAllowed) {
+ onMediaDataRemoved(key)
+ } else {
+ keysNeedRemoval.add(key)
+ }
} else {
- addOrUpdatePlayer(key, oldKey, data)
+ keysNeedRemoval.remove(key)
}
}
@@ -236,12 +247,12 @@ class MediaCarouselController @Inject constructor(
var newPlayer = mediaControlPanelFactory.get()
newPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), mediaContent))
newPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions
- MediaPlayerData.addMediaPlayer(key, data, newPlayer)
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
newPlayer.view?.player?.setLayoutParams(lp)
newPlayer.bind(data)
newPlayer.setListening(currentlyExpanded)
+ MediaPlayerData.addMediaPlayer(key, data, newPlayer)
updatePlayerToState(newPlayer, noAnimation = true)
reorderAllPlayers()
} else {
@@ -271,6 +282,9 @@ class MediaCarouselController @Inject constructor(
removed.onDestroy()
mediaCarouselScrollHandler.onPlayersChanged()
updatePageIndicator()
+
+ // Inform the media manager of a potentially late dismissal
+ mediaManager.dismissMediaData(key, 0L)
}
}
@@ -478,12 +492,11 @@ class MediaCarouselController @Inject constructor(
internal object MediaPlayerData {
private data class MediaSortKey(
val data: MediaData,
- val updateTime: Long = 0,
- val isPlaying: Boolean = false
+ val updateTime: Long = 0
)
private val comparator =
- compareByDescending<MediaSortKey> { it.isPlaying }
+ compareByDescending<MediaSortKey> { it.data.isPlaying }
.thenByDescending { it.data.isLocalSession }
.thenByDescending { !it.data.resumption }
.thenByDescending { it.updateTime }
@@ -493,7 +506,7 @@ internal object MediaPlayerData {
fun addMediaPlayer(key: String, data: MediaData, player: MediaControlPanel) {
removeMediaPlayer(key)
- val sortKey = MediaSortKey(data, System.currentTimeMillis(), player.isPlaying())
+ val sortKey = MediaSortKey(data, System.currentTimeMillis())
mediaData.put(key, sortKey)
mediaPlayers.put(sortKey, player)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index d6a02687c905..40a879abde34 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -94,7 +94,17 @@ data class MediaData(
* Notification key for cancelling a media player after a timeout (when not using resumption.)
*/
val notificationKey: String? = null,
- var hasCheckedForResume: Boolean = false
+ var hasCheckedForResume: Boolean = false,
+
+ /**
+ * If apps do not report PlaybackState, set as null to imply 'undetermined'
+ */
+ val isPlaying: Boolean? = null,
+
+ /**
+ * Set from the notification and used as fallback when PlaybackState cannot be determined
+ */
+ val isClearable: Boolean = true
)
/** State of a media action. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 0664a41f841d..1f580a953d09 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -136,14 +136,8 @@ class MediaDataFilter @Inject constructor(
/**
* Are there any media entries we should display?
- * If resumption is enabled, this will include inactive players
- * If resumption is disabled, we only want to show active players
*/
- fun hasAnyMedia() = if (mediaResumeListener.isResumptionEnabled()) {
- userEntries.isNotEmpty()
- } else {
- hasActiveMedia()
- }
+ fun hasAnyMedia() = userEntries.isNotEmpty()
/**
* Add a listener for filtered [MediaData] changes
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index b2ad19b5f42f..cb6b22c2321f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -47,6 +47,7 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
import com.android.systemui.statusbar.notification.MediaNotificationProcessor
import com.android.systemui.statusbar.notification.row.HybridGroupManager
import com.android.systemui.util.Assert
@@ -350,6 +351,16 @@ class MediaDataManager(
}
fun dismissMediaData(key: String, delay: Long) {
+ backgroundExecutor.execute {
+ mediaEntries[key]?.let { mediaData ->
+ if (mediaData.isLocalSession) {
+ mediaData.token?.let {
+ val mediaController = mediaControllerFactory.create(it)
+ mediaController.transportControls.stop()
+ }
+ }
+ }
+ }
foregroundExecutor.executeDelayed({ removeEntry(key) }, delay)
}
@@ -500,6 +511,7 @@ class MediaDataManager(
val isLocalSession = mediaController.playbackInfo?.playbackType ==
MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL ?: true
+ val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null
foregroundExecutor.execute {
val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
@@ -509,7 +521,8 @@ class MediaDataManager(
smallIconDrawable, artist, song, artWorkIcon, actionIcons,
actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null,
active, resumeAction = resumeAction, isLocalSession = isLocalSession,
- notificationKey = key, hasCheckedForResume = hasCheckedForResume))
+ notificationKey = key, hasCheckedForResume = hasCheckedForResume,
+ isPlaying = isPlaying, isClearable = sbn.isClearable()))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index c00b5e92f93d..5b59214afdc9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -125,8 +125,6 @@ class MediaResumeListener @Inject constructor(
}, Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED)
}
- fun isResumptionEnabled() = useMediaResumption
-
private fun loadSavedComponents() {
// Make sure list is empty (if we switched users)
resumeComponents.clear()
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/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index e57478eb0988..4d6d71c06085 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -72,6 +72,8 @@ class PrivacyItemController @Inject constructor(
private const val ALL_INDICATORS =
SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+ private const val DEFAULT_ALL_INDICATORS = false
+ private const val DEFAULT_MIC_CAMERA = true
}
@VisibleForTesting
@@ -81,12 +83,12 @@ class PrivacyItemController @Inject constructor(
private fun isAllIndicatorsEnabled(): Boolean {
return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- ALL_INDICATORS, false)
+ ALL_INDICATORS, DEFAULT_ALL_INDICATORS)
}
private fun isMicCameraEnabled(): Boolean {
return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- MIC_CAMERA, false)
+ MIC_CAMERA, DEFAULT_MIC_CAMERA)
}
private var currentUserIds = emptyList<Int>()
@@ -118,12 +120,13 @@ class PrivacyItemController @Inject constructor(
// Running on the ui executor so can iterate on callbacks
if (properties.keyset.contains(ALL_INDICATORS)) {
- allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, false)
+ allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS,
+ DEFAULT_ALL_INDICATORS)
callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) }
}
if (properties.keyset.contains(MIC_CAMERA)) {
- micCameraAvailable = properties.getBoolean(MIC_CAMERA, false)
+ micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
}
internalUiExecutor.updateListeningState()
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/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index cdbc647f152b..64e067396059 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -231,7 +231,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
private void assertShowButtonAnimation() {
verify(mViewPropertyAnimator).cancel();
verify(mViewPropertyAnimator).setDuration(anyLong());
- verify(mViewPropertyAnimator).setStartDelay(anyLong());
verify(mViewPropertyAnimator).alpha(anyFloat());
verify(mViewPropertyAnimator).start();
}
@@ -239,11 +238,15 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
private void initMockImageViewAndAnimator() {
when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator);
when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator);
- when(mViewPropertyAnimator.setStartDelay(anyLong())).thenReturn(mViewPropertyAnimator);
when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn(
mViewPropertyAnimator);
when(mSpyImageView.animate()).thenReturn(mViewPropertyAnimator);
+ doAnswer(invocation -> {
+ Runnable run = invocation.getArgument(0);
+ run.run();
+ return null;
+ }).when(mSpyImageView).postDelayed(any(), anyLong());
}
private void resetMockImageViewAndAnimator() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 909acead9553..b4af786c5579 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -49,6 +49,7 @@ import android.testing.TestableLooper;
import android.util.FeatureFlagUtils;
import android.view.IWindowManager;
import android.view.View;
+import android.view.WindowManagerPolicyConstants;
import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
@@ -241,6 +242,28 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS);
}
+ @Test
+ public void testShouldShowScreenshot() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.integer.config_navBarInteractionMode,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON);
+
+ GlobalActionsDialog.ScreenshotAction screenshotAction =
+ mGlobalActionsDialog.makeScreenshotActionForTesting();
+ assertThat(screenshotAction.shouldShow()).isTrue();
+ }
+
+ @Test
+ public void testShouldNotShowScreenshot() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.integer.config_navBarInteractionMode,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON);
+
+ GlobalActionsDialog.ScreenshotAction screenshotAction =
+ mGlobalActionsDialog.makeScreenshotActionForTesting();
+ assertThat(screenshotAction.shouldShow()).isFalse();
+ }
+
private void verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent event) {
mTestableLooper.processAllMessages();
verify(mUiEventLogger, times(1))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 89538ac8bc9f..609b8474d134 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -74,7 +74,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null,
new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, true,
- false, KEY, false);
+ false, KEY, false, false, false);
mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
index da1ec9869d87..ef8d322ca2ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
@@ -25,7 +25,6 @@ import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -34,6 +33,8 @@ public class MediaPlayerDataTest : SysuiTestCase() {
companion object {
val LOCAL = true
val RESUMPTION = true
+ val PLAYING = true
+ val UNDETERMINED = null
}
@Before
@@ -44,15 +45,13 @@ public class MediaPlayerDataTest : SysuiTestCase() {
@Test
fun addPlayingThenRemote() {
val playerIsPlaying = mock(MediaControlPanel::class.java)
- whenever(playerIsPlaying.isPlaying).thenReturn(true)
- val dataIsPlaying = createMediaData("app1", LOCAL, !RESUMPTION)
+ val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
val playerIsRemote = mock(MediaControlPanel::class.java)
- whenever(playerIsRemote.isPlaying).thenReturn(false)
- val dataIsRemote = createMediaData("app2", !LOCAL, !RESUMPTION)
+ val dataIsRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION)
- MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying)
MediaPlayerData.addMediaPlayer("2", dataIsRemote, playerIsRemote)
+ MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying)
val players = MediaPlayerData.players()
assertThat(players).hasSize(2)
@@ -63,18 +62,16 @@ public class MediaPlayerDataTest : SysuiTestCase() {
@Ignore("Flaky")
fun switchPlayersPlaying() {
val playerIsPlaying1 = mock(MediaControlPanel::class.java)
- whenever(playerIsPlaying1.isPlaying).thenReturn(true)
- val dataIsPlaying1 = createMediaData("app1", LOCAL, !RESUMPTION)
+ var dataIsPlaying1 = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
val playerIsPlaying2 = mock(MediaControlPanel::class.java)
- whenever(playerIsPlaying2.isPlaying).thenReturn(false)
- val dataIsPlaying2 = createMediaData("app2", LOCAL, !RESUMPTION)
+ var dataIsPlaying2 = createMediaData("app2", !PLAYING, LOCAL, !RESUMPTION)
MediaPlayerData.addMediaPlayer("1", dataIsPlaying1, playerIsPlaying1)
MediaPlayerData.addMediaPlayer("2", dataIsPlaying2, playerIsPlaying2)
- whenever(playerIsPlaying1.isPlaying).thenReturn(false)
- whenever(playerIsPlaying2.isPlaying).thenReturn(true)
+ dataIsPlaying1 = createMediaData("app1", !PLAYING, LOCAL, !RESUMPTION)
+ dataIsPlaying2 = createMediaData("app2", PLAYING, LOCAL, !RESUMPTION)
MediaPlayerData.addMediaPlayer("1", dataIsPlaying1, playerIsPlaying1)
MediaPlayerData.addMediaPlayer("2", dataIsPlaying2, playerIsPlaying2)
@@ -87,38 +84,43 @@ public class MediaPlayerDataTest : SysuiTestCase() {
@Test
fun fullOrderTest() {
val playerIsPlaying = mock(MediaControlPanel::class.java)
- whenever(playerIsPlaying.isPlaying).thenReturn(true)
- val dataIsPlaying = createMediaData("app1", LOCAL, !RESUMPTION)
+ val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
val playerIsPlayingAndRemote = mock(MediaControlPanel::class.java)
- whenever(playerIsPlayingAndRemote.isPlaying).thenReturn(true)
- val dataIsPlayingAndRemote = createMediaData("app2", !LOCAL, !RESUMPTION)
+ val dataIsPlayingAndRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION)
val playerIsStoppedAndLocal = mock(MediaControlPanel::class.java)
- whenever(playerIsStoppedAndLocal.isPlaying).thenReturn(false)
- val dataIsStoppedAndLocal = createMediaData("app3", LOCAL, !RESUMPTION)
+ val dataIsStoppedAndLocal = createMediaData("app3", !PLAYING, LOCAL, !RESUMPTION)
val playerIsStoppedAndRemote = mock(MediaControlPanel::class.java)
- whenever(playerIsStoppedAndLocal.isPlaying).thenReturn(false)
- val dataIsStoppedAndRemote = createMediaData("app4", !LOCAL, !RESUMPTION)
+ val dataIsStoppedAndRemote = createMediaData("app4", !PLAYING, !LOCAL, !RESUMPTION)
val playerCanResume = mock(MediaControlPanel::class.java)
- whenever(playerCanResume.isPlaying).thenReturn(false)
- val dataCanResume = createMediaData("app5", LOCAL, RESUMPTION)
+ val dataCanResume = createMediaData("app5", !PLAYING, LOCAL, RESUMPTION)
+
+ val playerUndetermined = mock(MediaControlPanel::class.java)
+ val dataUndetermined = createMediaData("app6", UNDETERMINED, LOCAL, RESUMPTION)
MediaPlayerData.addMediaPlayer("3", dataIsStoppedAndLocal, playerIsStoppedAndLocal)
MediaPlayerData.addMediaPlayer("5", dataIsStoppedAndRemote, playerIsStoppedAndRemote)
MediaPlayerData.addMediaPlayer("4", dataCanResume, playerCanResume)
MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying)
MediaPlayerData.addMediaPlayer("2", dataIsPlayingAndRemote, playerIsPlayingAndRemote)
+ MediaPlayerData.addMediaPlayer("6", dataUndetermined, playerUndetermined)
val players = MediaPlayerData.players()
- assertThat(players).hasSize(5)
+ assertThat(players).hasSize(6)
assertThat(players).containsExactly(playerIsPlaying, playerIsPlayingAndRemote,
- playerIsStoppedAndLocal, playerCanResume, playerIsStoppedAndRemote).inOrder()
+ playerIsStoppedAndLocal, playerCanResume, playerIsStoppedAndRemote,
+ playerUndetermined).inOrder()
}
- private fun createMediaData(app: String, isLocalSession: Boolean, resumption: Boolean) =
+ private fun createMediaData(
+ app: String,
+ isPlaying: Boolean?,
+ isLocalSession: Boolean,
+ resumption: Boolean
+ ) =
MediaData(0, false, 0, app, null, null, null, null, emptyList(), emptyList<Int>(), "",
- null, null, null, true, null, isLocalSession, resumption, null, false)
+ null, null, null, true, null, isLocalSession, resumption, null, false, isPlaying)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
index 4ba29e6e02a6..25fb7d300b8f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -96,22 +96,24 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
}
@Test
- fun testNotListeningByDefault() {
+ fun testNotListeningAllByDefault() {
assertFalse(privacyItemController.allIndicatorsAvailable)
- assertFalse(privacyItemController.micCameraAvailable)
+ }
- verify(appOpsController, never()).addCallback(any(), any())
+ @Test
+ fun testMicCameraListeningByDefault() {
+ assertTrue(privacyItemController.micCameraAvailable)
}
@Test
fun testMicCameraChanged() {
- changeMicCamera(true)
+ changeMicCamera(false) // default is true
executor.runAllReady()
- verify(callback).onFlagMicCameraChanged(true)
+ verify(callback).onFlagMicCameraChanged(false)
verify(callback, never()).onFlagAllChanged(anyBoolean())
- assertTrue(privacyItemController.micCameraAvailable)
+ assertFalse(privacyItemController.micCameraAvailable)
assertFalse(privacyItemController.allIndicatorsAvailable)
}
@@ -124,20 +126,19 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
verify(callback, never()).onFlagMicCameraChanged(anyBoolean())
assertTrue(privacyItemController.allIndicatorsAvailable)
- assertFalse(privacyItemController.micCameraAvailable)
}
@Test
fun testBothChanged() {
changeAll(true)
- changeMicCamera(true)
+ changeMicCamera(false)
executor.runAllReady()
verify(callback, atLeastOnce()).onFlagAllChanged(true)
- verify(callback, atLeastOnce()).onFlagMicCameraChanged(true)
+ verify(callback, atLeastOnce()).onFlagMicCameraChanged(false)
assertTrue(privacyItemController.allIndicatorsAvailable)
- assertTrue(privacyItemController.micCameraAvailable)
+ assertFalse(privacyItemController.micCameraAvailable)
}
@Test
@@ -157,18 +158,11 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
}
@Test
- fun testAll_listening() {
- changeAll(true)
- executor.runAllReady()
-
- verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
- }
-
- @Test
fun testAllFalse_notListening() {
changeAll(true)
executor.runAllReady()
changeAll(false)
+ changeMicCamera(false)
executor.runAllReady()
verify(appOpsController).removeCallback(any(), any())
@@ -176,8 +170,8 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
@Test
fun testSomeListening_stillListening() {
+ // Mic and camera are true by default
changeAll(true)
- changeMicCamera(true)
executor.runAllReady()
changeAll(false)
executor.runAllReady()
@@ -186,7 +180,8 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
}
@Test
- fun testAllDeleted_stopListening() {
+ fun testAllDeleted_micCameraFalse_stopListening() {
+ changeMicCamera(false)
changeAll(true)
executor.runAllReady()
changeAll(null)
@@ -196,13 +191,13 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
}
@Test
- fun testMicDeleted_stopListening() {
+ fun testMicDeleted_stillListening() {
changeMicCamera(true)
executor.runAllReady()
changeMicCamera(null)
executor.runAllReady()
- verify(appOpsController).removeCallback(any(), any())
+ verify(appOpsController, never()).removeCallback(any(), any())
}
private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index fb42baaa0cb5..f152a74da0d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -273,6 +273,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
@Test
fun testNotListeningWhenIndicatorsDisabled() {
changeAll(false)
+ changeMicCamera(false)
privacyItemController.addCallback(callback)
executor.runAllReady()
verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
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/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index b6f2a47dd5e2..c583dcc2b1be 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -37,6 +37,7 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.MagnificationSpec;
import android.view.View;
+import android.view.accessibility.MagnificationAnimationCallback;
import android.view.animation.DecelerateInterpolator;
import com.android.internal.R;
@@ -63,7 +64,7 @@ public class FullScreenMagnificationController {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "FullScreenMagnificationController";
- private static final Runnable STUB_RUNNABLE = () -> {
+ private static final MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> {
};
public static final float MIN_SCALE = 1.0f;
public static final float MAX_SCALE = 8.0f;
@@ -304,18 +305,19 @@ public class FullScreenMagnificationController {
}
}
- void sendSpecToAnimation(MagnificationSpec spec, Runnable endCallback) {
+ void sendSpecToAnimation(MagnificationSpec spec,
+ MagnificationAnimationCallback animationCallback) {
if (DEBUG) {
Slog.i(LOG_TAG,
- "sendSpecToAnimation(spec = " + spec + ", endCallback = " + endCallback
- + ")");
+ "sendSpecToAnimation(spec = " + spec + ", animationCallback = "
+ + animationCallback + ")");
}
if (Thread.currentThread().getId() == mMainThreadId) {
- mSpecAnimationBridge.updateSentSpecMainThread(spec, endCallback);
+ mSpecAnimationBridge.updateSentSpecMainThread(spec, animationCallback);
} else {
final Message m = PooledLambda.obtainMessage(
SpecAnimationBridge::updateSentSpecMainThread,
- mSpecAnimationBridge, spec, endCallback);
+ mSpecAnimationBridge, spec, animationCallback);
mControllerCtx.getHandler().sendMessage(m);
}
}
@@ -415,11 +417,11 @@ public class FullScreenMagnificationController {
@GuardedBy("mLock")
boolean reset(boolean animate) {
- return reset(transformToStubRunnable(animate));
+ return reset(transformToStubCallback(animate));
}
@GuardedBy("mLock")
- boolean reset(Runnable endCallback) {
+ boolean reset(MagnificationAnimationCallback animationCallback) {
if (!mRegistered) {
return false;
}
@@ -430,7 +432,7 @@ public class FullScreenMagnificationController {
onMagnificationChangedLocked();
}
mIdOfLastServiceToMagnify = INVALID_ID;
- sendSpecToAnimation(spec, endCallback);
+ sendSpecToAnimation(spec, animationCallback);
return changed;
}
@@ -458,24 +460,23 @@ public class FullScreenMagnificationController {
final float centerX = normPivotX + offsetX;
final float centerY = normPivotY + offsetY;
mIdOfLastServiceToMagnify = id;
- return setScaleAndCenter(scale, centerX, centerY, transformToStubRunnable(animate), id);
+ return setScaleAndCenter(scale, centerX, centerY, transformToStubCallback(animate), id);
}
@GuardedBy("mLock")
boolean setScaleAndCenter(float scale, float centerX, float centerY,
- Runnable endCallback, int id) {
+ MagnificationAnimationCallback animationCallback, int id) {
if (!mRegistered) {
return false;
}
if (DEBUG) {
Slog.i(LOG_TAG,
"setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX
- + ", centerY = " + centerY + ", endCallback = " + endCallback
- + ", id = " + id
- + ")");
+ + ", centerY = " + centerY + ", endCallback = "
+ + animationCallback + ", id = " + id + ")");
}
final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
- sendSpecToAnimation(mCurrentMagnificationSpec, endCallback);
+ sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback);
if (isMagnifying() && (id != INVALID_ID)) {
mIdOfLastServiceToMagnify = id;
}
@@ -875,7 +876,7 @@ public class FullScreenMagnificationController {
* the spec did not change
*/
public boolean reset(int displayId, boolean animate) {
- return reset(displayId, animate ? STUB_RUNNABLE : null);
+ return reset(displayId, animate ? STUB_ANIMATION_CALLBACK : null);
}
/**
@@ -883,18 +884,19 @@ public class FullScreenMagnificationController {
* transition.
*
* @param displayId The logical display id.
- * @param endCallback Called when the animation is ended or the spec did not change.
+ * @param animationCallback Called when the animation result is valid.
* {@code null} to transition immediately
* @return {@code true} if the magnification spec changed, {@code false} if
* the spec did not change
*/
- public boolean reset(int displayId, Runnable endCallback) {
+ public boolean reset(int displayId,
+ MagnificationAnimationCallback animationCallback) {
synchronized (mLock) {
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null) {
return false;
}
- return display.reset(endCallback);
+ return display.reset(animationCallback);
}
}
@@ -946,7 +948,7 @@ public class FullScreenMagnificationController {
return false;
}
return display.setScaleAndCenter(Float.NaN, centerX, centerY,
- animate ? STUB_RUNNABLE : null, id);
+ animate ? STUB_ANIMATION_CALLBACK : null, id);
}
}
@@ -970,7 +972,7 @@ public class FullScreenMagnificationController {
public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
boolean animate, int id) {
return setScaleAndCenter(displayId, scale, centerX, centerY,
- transformToStubRunnable(animate), id);
+ transformToStubCallback(animate), id);
}
/**
@@ -984,20 +986,20 @@ public class FullScreenMagnificationController {
* center and scale, or {@link Float#NaN} to leave unchanged
* @param centerY the screen-relative Y coordinate around which to
* center and scale, or {@link Float#NaN} to leave unchanged
- * @param endCallback called when the transition is finished successfully or the spec did not
- * change. {@code null} to transition immediately.
+ * @param animationCallback Called when the animation result is valid.
+ * {@code null} to transition immediately
* @param id the ID of the service requesting the change
* @return {@code true} if the magnification spec changed, {@code false} if
* the spec did not change
*/
public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
- Runnable endCallback, int id) {
+ MagnificationAnimationCallback animationCallback, int id) {
synchronized (mLock) {
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null) {
return false;
}
- return display.setScaleAndCenter(scale, centerX, centerY, endCallback, id);
+ return display.setScaleAndCenter(scale, centerX, centerY, animationCallback, id);
}
}
@@ -1230,7 +1232,7 @@ public class FullScreenMagnificationController {
private final ValueAnimator mValueAnimator;
// Called when the callee wants animating and the sent spec matches the target spec.
- private Runnable mEndCallback;
+ private MagnificationAnimationCallback mAnimationCallback;
private final Object mLock;
private final int mDisplayId;
@@ -1268,33 +1270,35 @@ public class FullScreenMagnificationController {
}
}
- void updateSentSpecMainThread(MagnificationSpec spec, Runnable endCallback) {
+ void updateSentSpecMainThread(MagnificationSpec spec,
+ MagnificationAnimationCallback animationCallback) {
if (mValueAnimator.isRunning()) {
- // Avoid AnimationEnd Callback.
- mEndCallback = null;
mValueAnimator.cancel();
}
- mEndCallback = endCallback;
+ mAnimationCallback = animationCallback;
// If the current and sent specs don't match, update the sent spec.
synchronized (mLock) {
final boolean changed = !mSentMagnificationSpec.equals(spec);
if (changed) {
- if (mEndCallback != null) {
+ if (mAnimationCallback != null) {
animateMagnificationSpecLocked(spec);
} else {
setMagnificationSpecLocked(spec);
}
} else {
- sendEndCallbackMainThread();
+ sendEndCallbackMainThread(true);
}
}
}
- private void sendEndCallbackMainThread() {
- if (mEndCallback != null) {
- mEndCallback.run();
- mEndCallback = null;
+ private void sendEndCallbackMainThread(boolean success) {
+ if (mAnimationCallback != null) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "sendEndCallbackMainThread: " + success);
+ }
+ mAnimationCallback.onResult(success);
+ mAnimationCallback = null;
}
}
@@ -1337,17 +1341,16 @@ public class FullScreenMagnificationController {
@Override
public void onAnimationStart(Animator animation) {
-
}
@Override
public void onAnimationEnd(Animator animation) {
- sendEndCallbackMainThread();
+ sendEndCallbackMainThread(true);
}
@Override
public void onAnimationCancel(Animator animation) {
-
+ sendEndCallbackMainThread(false);
}
@Override
@@ -1481,7 +1484,7 @@ public class FullScreenMagnificationController {
}
@Nullable
- private static Runnable transformToStubRunnable(boolean animate) {
- return animate ? STUB_RUNNABLE : null;
+ private static MagnificationAnimationCallback transformToStubCallback(boolean animate) {
+ return animate ? STUB_ANIMATION_CALLBACK : null;
}
}
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..f63c2ee5ee94 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) {
@@ -4097,6 +4186,62 @@ public class AudioService extends IAudioService.Stub
}
}
+ /** @see AudioManager#adjustSuggestedStreamVolumeForUid(int, int, int, String, int, int, int) */
+ @Override
+ public void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags,
+ @NonNull String packageName, int uid, int pid, UserHandle userHandle,
+ int targetSdkVersion) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Should only be called from system process");
+ }
+
+ final boolean hasModifyAudioSettings =
+ mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ // direction and stream type swap here because the public
+ // adjustSuggested has a different order than the other methods.
+ adjustSuggestedStreamVolume(direction, streamType, flags, packageName, packageName, uid,
+ hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+ }
+
+ /** @see AudioManager#adjustStreamVolumeForUid(int, int, int, String, int, int, int) */
+ @Override
+ public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+ @NonNull String packageName, int uid, int pid, UserHandle userHandle,
+ int targetSdkVersion) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Should only be called from system process");
+ }
+
+ if (direction != AudioManager.ADJUST_SAME) {
+ sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_VOL_UID, streamType,
+ direction/*val1*/, flags/*val2*/,
+ new StringBuilder(packageName).append(" uid:").append(uid)
+ .toString()));
+ }
+ final boolean hasModifyAudioSettings =
+ mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid,
+ hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+ }
+
+ /** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
+ @Override
+ public void setStreamVolumeForUid(int streamType, int index, int flags,
+ @NonNull String packageName, int uid, int pid, UserHandle userHandle,
+ int targetSdkVersion) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Should only be called from system process");
+ }
+
+ final boolean hasModifyAudioSettings =
+ mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ setStreamVolume(streamType, index, flags, packageName, packageName, uid,
+ hasModifyAudioSettings);
+ }
+
//==========================================================================================
// Sound Effects
//==========================================================================================
@@ -7982,43 +8127,6 @@ public class AudioService extends IAudioService.Stub
}
@Override
- public void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags,
- String callingPackage, int uid, int pid) {
- final boolean hasModifyAudioSettings =
- mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
- // direction and stream type swap here because the public
- // adjustSuggested has a different order than the other methods.
- adjustSuggestedStreamVolume(direction, streamType, flags, callingPackage,
- callingPackage, uid, hasModifyAudioSettings, VOL_ADJUST_NORMAL);
- }
-
- @Override
- public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
- String callingPackage, int uid, int pid) {
- if (direction != AudioManager.ADJUST_SAME) {
- sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_VOL_UID, streamType,
- direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)
- .append(" uid:").append(uid).toString()));
- }
- final boolean hasModifyAudioSettings =
- mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
- adjustStreamVolume(streamType, direction, flags, callingPackage,
- callingPackage, uid, hasModifyAudioSettings, VOL_ADJUST_NORMAL);
- }
-
- @Override
- public void setStreamVolumeForUid(int streamType, int direction, int flags,
- String callingPackage, int uid, int pid) {
- final boolean hasModifyAudioSettings =
- mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
- setStreamVolume(streamType, direction, flags, callingPackage, callingPackage, uid,
- hasModifyAudioSettings);
- }
-
- @Override
public int getRingerModeInternal() {
return AudioService.this.getRingerModeInternal();
}
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/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 73fc17aa26f1..9898d7676178 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -98,7 +98,11 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I
}
if (finish) {
- mCallback.onClientFinished(this, false /* success */);
+ if (mCallback == null) {
+ Slog.e(TAG, "Callback is null, perhaps the client hasn't been started yet?");
+ } else {
+ mCallback.onClientFinished(this, false /* success */);
+ }
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index e585b48d49b6..3c9dddd0b905 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -176,6 +176,9 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde
// TODO(b/157790417): Move this to the scheduler
void binderDiedInternal(boolean clearListener) {
+ Slog.e(TAG, "Binder died, owner: " + getOwnerString()
+ + ", operation: " + this.getClass().getName());
+
if (isAlreadyDone()) {
Slog.w(TAG, "Binder died but client is finished, ignoring");
return;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
index c134a3faca4f..d2c35feccc11 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
@@ -510,8 +510,15 @@ class Face10 implements IHwBinder.DeathRecipient {
if (mCurrentChallengeOwner != null) {
Slog.w(TAG, "Current challenge owner: " + mCurrentChallengeOwner
+ ", interrupted by: " + opPackageName);
+ final ClientMonitorCallbackConverter listener =
+ mCurrentChallengeOwner.getListener();
+ if (listener == null) {
+ Slog.w(TAG, "Null listener, skip sending interruption callback");
+ return;
+ }
+
try {
- mCurrentChallengeOwner.getListener().onChallengeInterrupted(mSensorId);
+ listener.onChallengeInterrupted(mSensorId);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to notify challenge interrupted", e);
}
@@ -524,7 +531,7 @@ class Face10 implements IHwBinder.DeathRecipient {
@Override
public void onClientStarted(@NonNull ClientMonitor<?> clientMonitor) {
if (client != clientMonitor) {
- Slog.e(TAG, "scheduleGenerateChallenge, mismatched client."
+ Slog.e(TAG, "scheduleGenerateChallenge onClientStarted, mismatched client."
+ " Expecting: " + client + ", received: " + clientMonitor);
return;
}
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/location/util/SystemSettingsHelper.java b/services/core/java/com/android/server/location/util/SystemSettingsHelper.java
index ff4ba914cb9c..39aeaba16579 100644
--- a/services/core/java/com/android/server/location/util/SystemSettingsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemSettingsHelper.java
@@ -29,6 +29,7 @@ import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG;
import android.app.ActivityManager;
+import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
@@ -330,11 +331,13 @@ public class SystemSettingsHelper extends SettingsHelper {
@Override
public float getCoarseLocationAccuracyM() {
long identity = Binder.clearCallingIdentity();
+ final ContentResolver cr = mContext.getContentResolver();
try {
- return Settings.Secure.getFloat(
- mContext.getContentResolver(),
+ return Settings.Secure.getFloatForUser(
+ cr,
LOCATION_COARSE_ACCURACY_M,
- DEFAULT_COARSE_LOCATION_ACCURACY_M);
+ DEFAULT_COARSE_LOCATION_ACCURACY_M,
+ cr.getUserId());
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 1e02f49c43e4..793cfcd77414 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -23,7 +23,6 @@ import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.AudioManager;
-import android.media.AudioManagerInternal;
import android.media.AudioSystem;
import android.media.MediaMetadata;
import android.media.Rating;
@@ -53,8 +52,6 @@ import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
-import com.android.server.LocalServices;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -144,7 +141,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
// Volume handling fields
private AudioAttributes mAudioAttrs;
private AudioManager mAudioManager;
- private AudioManagerInternal mAudioManagerInternal;
private int mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL;
private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
private int mMaxVolume = 0;
@@ -179,7 +175,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
mContext = mService.getContext();
mHandler = new MessageHandler(handlerLooper);
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
mAudioAttrs = DEFAULT_ATTRIBUTES;
mPolicies = policies;
@@ -328,8 +323,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
@Override
public void run() {
try {
- mAudioManagerInternal.setStreamVolumeForUid(stream, volumeValue, flags,
- opPackageName, uid, pid);
+ mAudioManager.setStreamVolumeForUid(stream, volumeValue, flags,
+ opPackageName, uid, pid,
+ mContext.getApplicationInfo().targetSdkVersion);
} catch (IllegalArgumentException | SecurityException e) {
Log.e(TAG, "Cannot set volume: stream=" + stream + ", value=" + volumeValue
+ ", flags=" + flags, e);
@@ -518,16 +514,19 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
if (useSuggested) {
if (AudioSystem.isStreamActive(stream, 0)) {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream,
- direction, flags, opPackageName, uid, pid);
+ mAudioManager.adjustSuggestedStreamVolumeForUid(stream,
+ direction, flags, opPackageName, uid, pid,
+ mContext.getApplicationInfo().targetSdkVersion);
} else {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
+ mAudioManager.adjustSuggestedStreamVolumeForUid(
AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
- flags | previousFlagPlaySound, opPackageName, uid, pid);
+ flags | previousFlagPlaySound, opPackageName, uid, pid,
+ mContext.getApplicationInfo().targetSdkVersion);
}
} else {
- mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
- opPackageName, uid, pid);
+ mAudioManager.adjustStreamVolumeForUid(stream, direction, flags,
+ opPackageName, uid, pid,
+ mContext.getApplicationInfo().targetSdkVersion);
}
} catch (IllegalArgumentException | SecurityException e) {
Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream="
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 9521611c241d..d34502922b66 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -42,7 +42,6 @@ import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.media.AudioManager;
-import android.media.AudioManagerInternal;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioSystem;
import android.media.IRemoteVolumeController;
@@ -85,7 +84,6 @@ import android.view.ViewConfiguration;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
-import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
import com.android.server.Watchdog;
@@ -136,7 +134,7 @@ public class MediaSessionService extends SystemService implements Monitor {
new ArrayList<>();
private KeyguardManager mKeyguardManager;
- private AudioManagerInternal mAudioManagerInternal;
+ private AudioManager mAudioManager;
private ContentResolver mContentResolver;
private boolean mHasFeatureLeanback;
@@ -162,6 +160,7 @@ public class MediaSessionService extends SystemService implements Monitor {
PowerManager pm = mContext.getSystemService(PowerManager.class);
mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
mNotificationManager = mContext.getSystemService(NotificationManager.class);
+ mAudioManager = mContext.getSystemService(AudioManager.class);
}
@Override
@@ -169,7 +168,6 @@ public class MediaSessionService extends SystemService implements Monitor {
publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
Watchdog.getInstance().addMonitor(this);
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(mContext);
mAudioPlayerStateMonitor.registerListener(
(config, isRemoved) -> {
@@ -2057,8 +2055,9 @@ public class MediaSessionService extends SystemService implements Monitor {
callingPid = pid;
}
try {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(suggestedStream,
- direction, flags, callingOpPackageName, callingUid, callingPid);
+ mAudioManager.adjustSuggestedStreamVolumeForUid(suggestedStream,
+ direction, flags, callingOpPackageName, callingUid, callingPid,
+ getContext().getApplicationInfo().targetSdkVersion);
} catch (SecurityException | IllegalArgumentException e) {
Log.e(TAG, "Cannot adjust volume: direction=" + direction
+ ", suggestedStream=" + suggestedStream + ", flags=" + flags
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9f78f0f08fd1..89e0393f02f8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -19867,7 +19867,7 @@ public class PackageManagerService extends IPackageManager.Stub
final PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId);
final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
if (removeExisting && existing != null) {
- removeFiltersLocked(pir, filter, existing);
+ mSettings.removeFiltersLPw(pir, filter, existing);
}
pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
scheduleWritePackageRestrictionsLocked(userId);
@@ -19968,7 +19968,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
if (existing != null) {
- removeFiltersLocked(pir, filter, existing);
+ mSettings.removeFiltersLPw(pir, filter, existing);
}
}
}
@@ -19976,22 +19976,6 @@ public class PackageManagerService extends IPackageManager.Stub
"Replacing preferred", false);
}
- private void removeFiltersLocked(@NonNull PreferredIntentResolver pir,
- @NonNull IntentFilter filter, @NonNull List<PreferredActivity> existing) {
- if (DEBUG_PREFERRED) {
- Slog.i(TAG, existing.size() + " preferred matches for:");
- filter.dump(new LogPrinter(Log.INFO, TAG), " ");
- }
- for (int i = existing.size() - 1; i >= 0; --i) {
- final PreferredActivity pa = existing.get(i);
- if (DEBUG_PREFERRED) {
- Slog.i(TAG, "Removing preferred activity " + pa.mPref.mComponent + ":");
- pa.dump(new LogPrinter(Log.INFO, TAG), " ");
- }
- pir.removeFilter(pa);
- }
- }
-
@Override
public void clearPackagePreferredActivities(String packageName) {
final int callingUid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 659e2a32e267..bd8d0c820c5c 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -102,7 +102,6 @@ import com.android.internal.util.XmlUtils;
import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.LocalServices;
-import com.android.server.pm.Installer.Batch;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -3192,6 +3191,22 @@ public final class Settings {
}
}
+ void removeFiltersLPw(@NonNull PreferredIntentResolver pir,
+ @NonNull IntentFilter filter, @NonNull List<PreferredActivity> existing) {
+ if (PackageManagerService.DEBUG_PREFERRED) {
+ Slog.i(TAG, existing.size() + " preferred matches for:");
+ filter.dump(new LogPrinter(Log.INFO, TAG), " ");
+ }
+ for (int i = existing.size() - 1; i >= 0; --i) {
+ final PreferredActivity pa = existing.get(i);
+ if (PackageManagerService.DEBUG_PREFERRED) {
+ Slog.i(TAG, "Removing preferred activity " + pa.mPref.mComponent + ":");
+ pa.dump(new LogPrinter(Log.INFO, TAG), " ");
+ }
+ pir.removeFilter(pa);
+ }
+ }
+
private void applyDefaultPreferredActivityLPw(
PackageManagerInternal pmInternal, IntentFilter tmpPa, ComponentName cn, int userId) {
// The initial preferences only specify the target activity
@@ -3395,8 +3410,13 @@ public final class Settings {
Slog.w(TAG, "Malformed mimetype " + intent.getType() + " for " + cn);
}
}
+ final PreferredIntentResolver pir = editPreferredActivitiesLPw(userId);
+ final List<PreferredActivity> existing = pir.findFilters(filter);
+ if (existing != null) {
+ removeFiltersLPw(pir, filter, existing);
+ }
PreferredActivity pa = new PreferredActivity(filter, systemMatch, set, cn, true);
- editPreferredActivitiesLPw(userId).addFilter(pa);
+ pir.addFilter(pa);
} else if (haveNonSys == null) {
StringBuilder sb = new StringBuilder();
sb.append("No component ");
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index f74cd611e9d0..0314cf8605bc 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -3005,10 +3005,10 @@ public class StatsPullAtomService extends SystemService {
Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED, 1, userId);
int unlockDismissesKeyguard = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD, 0, userId);
+ Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD, 1, userId);
int unlockAttentionRequired = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, 1, userId);
+ Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, 0, userId);
int unlockAppEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.FACE_UNLOCK_APP_ENABLED, 1, userId);
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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 80455833a3eb..b6f0f9ffe001 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7132,9 +7132,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return (deviceOwner != null) ? deviceOwner.keepUninstalledPackages : null;
}
+ /**
+ * Logs a warning when the device doesn't have {@code PackageManager.FEATURE_DEVICE_ADMIN}.
+ *
+ * @param message action that was not executed; should not end with a period because the missing
+ * feature will be appended to it.
+ */
+ private void logMissingFeatureAction(String message) {
+ Slog.w(LOG_TAG, message + " because device does not have the "
+ + PackageManager.FEATURE_DEVICE_ADMIN + " feature.");
+ }
+
@Override
public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId) {
if (!mHasFeature) {
+ logMissingFeatureAction("Cannot set " + ComponentName.flattenToShortString(admin)
+ + " as device owner for user " + userId);
return false;
}
if (admin == null
@@ -7456,6 +7469,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean setProfileOwner(ComponentName who, String ownerName, int userHandle) {
if (!mHasFeature) {
+ logMissingFeatureAction("Cannot set " + ComponentName.flattenToShortString(who)
+ + " as profile owner for user " + userHandle);
return false;
}
if (who == null
@@ -7676,6 +7691,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setUserProvisioningState(int newState, int userHandle) {
if (!mHasFeature) {
+ logMissingFeatureAction("Cannot set provisioning state " + newState + " for user "
+ + userHandle);
return;
}
@@ -7753,6 +7770,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setProfileEnabled(ComponentName who) {
if (!mHasFeature) {
+ logMissingFeatureAction("Cannot enable profile for "
+ + ComponentName.flattenToShortString(who));
return;
}
Objects.requireNonNull(who, "ComponentName is null");
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 57bfbf33d680..6acd9b6b3803 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -44,6 +44,7 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.os.Looper;
import android.view.MagnificationSpec;
+import android.view.accessibility.MagnificationAnimationCallback;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -99,12 +100,12 @@ public class FullScreenMagnificationControllerTest {
ValueAnimator.AnimatorListener mStateListener;
FullScreenMagnificationController mFullScreenMagnificationController;
- Runnable mEndCallback;
+ MagnificationAnimationCallback mAnimationCallback;
@Before
public void setUp() {
Looper looper = InstrumentationRegistry.getContext().getMainLooper();
- mEndCallback = Mockito.mock(Runnable.class);
+ mAnimationCallback = Mockito.mock(MagnificationAnimationCallback.class);
// Pretending ID of the Thread associated with looper as main thread ID in controller
when(mMockContext.getMainLooper()).thenReturn(looper);
when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
@@ -323,7 +324,6 @@ public class FullScreenMagnificationControllerTest {
for (int i = 0; i < DISPLAY_COUNT; i++) {
setScaleAndCenter_animated_stateChangesAndAnimationHappens(i);
resetMockWindowManager();
- Mockito.reset(mEndCallback);
}
}
@@ -336,7 +336,7 @@ public class FullScreenMagnificationControllerTest {
MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);
assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale,
- newCenter.x, newCenter.y, mEndCallback, SERVICE_ID_1));
+ newCenter.x, newCenter.y, mAnimationCallback, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
@@ -365,18 +365,17 @@ public class FullScreenMagnificationControllerTest {
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
mStateListener.onAnimationEnd(mMockValueAnimator);
verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
- verify(mEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
public void testSetScaleAndCenterWithAnimation_sameSpec_noAnimationButInvokeEndCallback() {
for (int i = 0; i < DISPLAY_COUNT; i++) {
- setScaleAndCenter_sameSpec_noAnimationButInvokeEndCallback(i);
- Mockito.reset(mEndCallback);
+ setScaleAndCenter_sameSpec_noAnimationButInvokeCallbacks(i);
}
}
- private void setScaleAndCenter_sameSpec_noAnimationButInvokeEndCallback(int displayId) {
+ private void setScaleAndCenter_sameSpec_noAnimationButInvokeCallbacks(int displayId) {
register(displayId);
final PointF center = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
final float targetScale = 2.0f;
@@ -385,11 +384,11 @@ public class FullScreenMagnificationControllerTest {
mMessageCapturingHandler.sendAllMessages();
assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId,
- targetScale, center.x, center.y, mEndCallback, SERVICE_ID_1));
+ targetScale, center.x, center.y, mAnimationCallback, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
verify(mMockValueAnimator, never()).start();
- verify(mEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
@@ -673,38 +672,38 @@ public class FullScreenMagnificationControllerTest {
public void testReset_notMagnifying_noStateChangeButInvokeCallback() {
for (int i = 0; i < DISPLAY_COUNT; i++) {
reset_notMagnifying_noStateChangeButInvokeCallback(i);
- Mockito.reset(mEndCallback);
}
}
private void reset_notMagnifying_noStateChangeButInvokeCallback(int displayId) {
register(displayId);
- assertFalse(mFullScreenMagnificationController.reset(displayId, mEndCallback));
+ assertFalse(mFullScreenMagnificationController.reset(displayId, mAnimationCallback));
mMessageCapturingHandler.sendAllMessages();
verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId),
any(Region.class), anyFloat(), anyFloat(), anyFloat());
- verify(mEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
- public void testReset_Magnifying_resetsMagnificationAndInvokeLastEndCallback() {
+ public void testReset_Magnifying_resetsMagnificationAndInvokeCallbacks() {
for (int i = 0; i < DISPLAY_COUNT; i++) {
- reset_Magnifying_resetsMagnificationAndInvokeLastEndCallback(i);
+ reset_Magnifying_resetsMagnificationAndInvokeCallbacks(i);
}
}
- private void reset_Magnifying_resetsMagnificationAndInvokeLastEndCallback(int displayId) {
+ private void reset_Magnifying_resetsMagnificationAndInvokeCallbacks(int displayId) {
register(displayId);
float scale = 2.5f;
PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId,
- scale, firstCenter.x, firstCenter.y, mEndCallback, SERVICE_ID_1));
+ scale, firstCenter.x, firstCenter.y, mAnimationCallback, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
Mockito.reset(mMockValueAnimator);
// Stubs the logic after the animation is started.
doAnswer(invocation -> {
+ mStateListener.onAnimationCancel(mMockValueAnimator);
mStateListener.onAnimationEnd(mMockValueAnimator);
return null;
}).when(mMockValueAnimator).cancel();
@@ -713,13 +712,14 @@ public class FullScreenMagnificationControllerTest {
float fraction = 0.33f;
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
- Runnable lastEndCallback = Mockito.mock(Runnable.class);
+ MagnificationAnimationCallback lastAnimationCallback = Mockito.mock(
+ MagnificationAnimationCallback.class);
- assertTrue(mFullScreenMagnificationController.reset(displayId, lastEndCallback));
+ assertTrue(mFullScreenMagnificationController.reset(displayId, lastAnimationCallback));
mMessageCapturingHandler.sendAllMessages();
// Verify expected actions.
- verify(mEndCallback, never()).run();
+ verify(mAnimationCallback).onResult(false);
verify(mMockValueAnimator).start();
verify(mMockValueAnimator).cancel();
@@ -729,7 +729,7 @@ public class FullScreenMagnificationControllerTest {
mStateListener.onAnimationEnd(mMockValueAnimator);
assertFalse(mFullScreenMagnificationController.isMagnifying(DISPLAY_0));
- verify(lastEndCallback).run();
+ verify(lastAnimationCallback).onResult(true);
}
@Test
@@ -1142,6 +1142,7 @@ public class FullScreenMagnificationControllerTest {
verify(mMockValueAnimator).addListener(animatorListenerArgumentCaptor.capture());
mStateListener = animatorListenerArgumentCaptor.getValue();
Mockito.reset(mMockValueAnimator); // Ignore other initialization
+ Mockito.reset(mAnimationCallback);
}
private void zoomIn2xToMiddle(int displayId) {
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/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index 67e5eabf54eb..9d17219c1ae4 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -17,6 +17,7 @@
package android.telecom.Logging;
import android.annotation.Nullable;
+import android.content.ContentResolver;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
@@ -453,7 +454,9 @@ public class SessionManager {
* perform a sweep to check and make sure that the session is still not incomplete (stale).
*/
private long getCleanupTimeout(Context context) {
- return Settings.Secure.getLong(context.getContentResolver(), TIMEOUTS_PREFIX +
- "stale_session_cleanup_timeout_millis", DEFAULT_SESSION_TIMEOUT_MS);
+ final ContentResolver cr = context.getContentResolver();
+ return Settings.Secure.getLongForUser(cr, TIMEOUTS_PREFIX
+ + "stale_session_cleanup_timeout_millis", DEFAULT_SESSION_TIMEOUT_MS,
+ cr.getUserId());
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index c1a3ed695096..69b11872e123 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -109,7 +109,7 @@ fun LayersAssertion.navBarLayerRotatesAndScales(
}
if (startingPos == endingPos) {
- all("navBarLayerRotatesAndScales", enabled, bugId) {
+ all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
}
}
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 0aca13e95a52..7c150f9c8db9 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -16,8 +16,8 @@
#include <input/KeyCharacterMap.h>
#include <input/KeyLayoutMap.h>
+#include <input/PropertyMap.h>
#include <input/VirtualKeyMap.h>
-#include <utils/PropertyMap.h>
#include <stdarg.h>
#include <stdio.h>
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index ae9b5ab07851..d0742c7c61ad 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -655,6 +655,7 @@ package android.net.wifi.aware {
method public void attach(@NonNull android.net.wifi.aware.AttachCallback, @NonNull android.net.wifi.aware.IdentityChangedListener, @Nullable android.os.Handler);
method public android.net.wifi.aware.Characteristics getCharacteristics();
method public boolean isAvailable();
+ method public boolean isDeviceAttached();
field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0
field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java
index 99c4eac7977b..cf54f26fc5e4 100644
--- a/wifi/java/android/net/wifi/SoftApCapability.java
+++ b/wifi/java/android/net/wifi/SoftApCapability.java
@@ -21,7 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.wifi.SoftApConfiguration.BandType;
-import android.os.Build;
+import android.net.wifi.util.SdkLevelUtil;
import android.os.Parcel;
import android.os.Parcelable;
@@ -176,7 +176,7 @@ public final class SoftApCapability implements Parcelable {
*/
@NonNull
public int[] getSupportedChannelList(@BandType int band) {
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+ if (!SdkLevelUtil.isAtLeastS()) {
throw new UnsupportedOperationException();
}
switch (band) {
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 393fe8d3ab9a..bc837b3c344d 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -22,7 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.MacAddress;
-import android.os.Build;
+import android.net.wifi.util.SdkLevelUtil;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -551,7 +551,7 @@ public final class SoftApConfiguration implements Parcelable {
@SystemApi
@MacRandomizationSetting
public int getMacRandomizationSetting() {
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+ if (!SdkLevelUtil.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return mMacRandomizationSetting;
@@ -1046,7 +1046,7 @@ public final class SoftApConfiguration implements Parcelable {
@NonNull
public Builder setMacRandomizationSetting(
@MacRandomizationSetting int macRandomizationSetting) {
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+ if (!SdkLevelUtil.isAtLeastS()) {
throw new UnsupportedOperationException();
}
mMacRandomizationSetting = macRandomizationSetting;
diff --git a/wifi/java/android/net/wifi/SoftApInfo.java b/wifi/java/android/net/wifi/SoftApInfo.java
index 4791275cdce5..40981f7b0fb1 100644
--- a/wifi/java/android/net/wifi/SoftApInfo.java
+++ b/wifi/java/android/net/wifi/SoftApInfo.java
@@ -20,7 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.MacAddress;
-import android.os.Build;
+import android.net.wifi.util.SdkLevelUtil;
import android.os.Parcel;
import android.os.Parcelable;
@@ -138,7 +138,7 @@ public final class SoftApInfo implements Parcelable {
*/
@Nullable
public MacAddress getBssid() {
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+ if (!SdkLevelUtil.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return mBssid;
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
index 88f95ad4d495..f5b1edce1d69 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
@@ -36,6 +36,7 @@ interface IWifiAwareManager
// Aware API
boolean isUsageEnabled();
Characteristics getCharacteristics();
+ boolean isDeviceAttached();
// client API
void connect(in IBinder binder, in String callingPackage, in String callingFeatureId,
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 6352a2f4305c..d6e46fd52caf 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -179,6 +179,22 @@ public class WifiAwareManager {
}
/**
+ * Return the current status of the Aware service: whether ot not the device is already attached
+ * to an Aware cluster. To attach to an Aware cluster, please use
+ * {@link #attach(AttachCallback, Handler)} or
+ * {@link #attach(AttachCallback, IdentityChangedListener, Handler)}.
+ * @return A boolean indicating whether the device is attached to a cluster at this time (true)
+ * or not (false).
+ */
+ public boolean isDeviceAttached() {
+ try {
+ return mService.isDeviceAttached();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the characteristics of the Wi-Fi Aware interface: a set of parameters which specify
* limitations on configurations, e.g. the maximum service name length.
*
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) {
diff --git a/wifi/java/android/net/wifi/util/SdkLevelUtil.java b/wifi/java/android/net/wifi/util/SdkLevelUtil.java
index f29b0a6f3611..042634c7125c 100644
--- a/wifi/java/android/net/wifi/util/SdkLevelUtil.java
+++ b/wifi/java/android/net/wifi/util/SdkLevelUtil.java
@@ -30,8 +30,13 @@ public class SdkLevelUtil {
/** This class is instantiable to allow easy mocking. */
public SdkLevelUtil() { }
+ /** See {@link #isAtLeastS()}. This version is non-static to allow easy mocking. */
+ public boolean isAtLeastSMockable() {
+ return isAtLeastS();
+ }
+
/** Returns true if the Android platform SDK is at least "S", false otherwise. */
- public boolean isAtLeastS() {
+ public static boolean isAtLeastS() {
// TODO(b/167575586): after S SDK finalization, this method should just be
// `return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;`
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 3e5ee14be898..5fe0cb42073d 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -146,6 +146,15 @@ public class WifiAwareManagerTest {
verify(mockAwareService).getCharacteristics();
}
+ /**
+ * Validate pass-through of isDeviceAttached() API.
+ */
+ @Test
+ public void testIsAttached() throws Exception {
+ mDut.isDeviceAttached();
+ verify(mockAwareService).isDeviceAttached();
+ }
+
/*
* WifiAwareEventCallbackProxy Tests
*/