summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/preloaded-classes6
-rw-r--r--config/preloaded-classes6
-rw-r--r--core/api/current.txt2
-rw-r--r--core/api/removed.txt32
-rw-r--r--core/api/test-current.txt4
-rw-r--r--core/java/android/accessibilityservice/AccessibilityTrace.java6
-rw-r--r--core/java/android/app/AppOpsManager.java134
-rw-r--r--core/java/android/app/AppOpsManagerInternal.java31
-rw-r--r--core/java/android/app/DownloadManager.java9
-rw-r--r--core/java/android/app/Notification.java62
-rw-r--r--core/java/android/content/AttributionSource.java7
-rw-r--r--core/java/android/os/vibrator/flags.aconfig7
-rw-r--r--core/java/android/service/notification/Condition.java5
-rw-r--r--core/java/android/speech/RecognitionService.java7
-rw-r--r--core/java/android/speech/SpeechRecognizerImpl.java37
-rwxr-xr-xcore/java/android/util/DisplayMetrics.java13
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java12
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl4
-rw-r--r--core/java/android/view/accessibility/IMagnificationConnection.aidl (renamed from core/java/android/view/accessibility/IWindowMagnificationConnection.aidl)4
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl28
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl6
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java10
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java125
-rw-r--r--media/java/android/media/AudioHalVersionInfo.java3
-rw-r--r--media/java/android/media/AudioManager.java71
-rw-r--r--media/java/android/media/IAudioService.aidl14
-rw-r--r--packages/SettingsLib/Spa/gradle/libs.versions.toml2
-rw-r--r--packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jarbin63375 -> 43462 bytes
-rw-r--r--packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties2
-rwxr-xr-xpackages/SettingsLib/Spa/gradlew17
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt5
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppRepositoryTest.kt27
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java21
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt7
-rw-r--r--packages/SystemUI/res/values/dimens.xml19
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt (renamed from packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt)44
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt130
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java)38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt)138
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt258
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt104
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt137
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java20
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java40
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java50
-rw-r--r--services/core/Android.bp2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java39
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java176
-rw-r--r--services/core/java/com/android/server/audio/AdiDeviceState.java69
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java36
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java233
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java71
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java82
-rw-r--r--services/core/java/com/android/server/audio/LoudnessCodecHelper.java21
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java40
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodUtils.java19
-rw-r--r--services/core/java/com/android/server/inputmethod/SubtypeUtils.java17
-rw-r--r--services/core/java/com/android/server/inputmethod/SystemLocaleWrapper.java108
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java18
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java5
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java12
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java46
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java20
-rw-r--r--services/core/java/com/android/server/policy/AppOpsPolicy.java44
-rw-r--r--services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java163
-rw-r--r--services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java16
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java4
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java4
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorControlService.java105
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorControllerHolder.java70
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java8
-rw-r--r--services/manifest_services.xml10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java55
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MockMagnificationConnection.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java)14
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java49
-rw-r--r--services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java42
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java63
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java72
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java7
-rw-r--r--services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java58
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java106
106 files changed, 2881 insertions, 1040 deletions
diff --git a/boot/preloaded-classes b/boot/preloaded-classes
index 72322ef5ec9e..548fa2f00ef0 100644
--- a/boot/preloaded-classes
+++ b/boot/preloaded-classes
@@ -8924,9 +8924,9 @@ android.view.accessibility.IAccessibilityManager
android.view.accessibility.IAccessibilityManagerClient$Stub$Proxy
android.view.accessibility.IAccessibilityManagerClient$Stub
android.view.accessibility.IAccessibilityManagerClient
-android.view.accessibility.IWindowMagnificationConnection$Stub$Proxy
-android.view.accessibility.IWindowMagnificationConnection$Stub
-android.view.accessibility.IWindowMagnificationConnection
+android.view.accessibility.IMagnificationConnection$Stub$Proxy
+android.view.accessibility.IMagnificationConnection$Stub
+android.view.accessibility.IMagnificationConnection
android.view.accessibility.WeakSparseArray$WeakReferenceWithId
android.view.accessibility.WeakSparseArray
android.view.animation.AccelerateDecelerateInterpolator
diff --git a/config/preloaded-classes b/config/preloaded-classes
index cace87c76a15..c49971eb68ae 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -8955,9 +8955,9 @@ android.view.accessibility.IAccessibilityManager
android.view.accessibility.IAccessibilityManagerClient$Stub$Proxy
android.view.accessibility.IAccessibilityManagerClient$Stub
android.view.accessibility.IAccessibilityManagerClient
-android.view.accessibility.IWindowMagnificationConnection$Stub$Proxy
-android.view.accessibility.IWindowMagnificationConnection$Stub
-android.view.accessibility.IWindowMagnificationConnection
+android.view.accessibility.IMagnificationConnection$Stub$Proxy
+android.view.accessibility.IMagnificationConnection$Stub
+android.view.accessibility.IMagnificationConnection
android.view.accessibility.WeakSparseArray$WeakReferenceWithId
android.view.accessibility.WeakSparseArray
android.view.animation.AccelerateDecelerateInterpolator
diff --git a/core/api/current.txt b/core/api/current.txt
index 119a3de6835e..137982102bd1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -40630,7 +40630,6 @@ package android.service.notification {
method public static boolean isValidId(android.net.Uri, String);
method public static android.net.Uri.Builder newId(android.content.Context);
method public static String relevanceToString(int);
- method @FlaggedApi("android.app.modes_api") @NonNull public static String sourceToString(int);
method public static String stateToString(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.Condition> CREATOR;
@@ -49269,6 +49268,7 @@ package android.util {
field public static final int DENSITY_300 = 300; // 0x12c
field public static final int DENSITY_340 = 340; // 0x154
field public static final int DENSITY_360 = 360; // 0x168
+ field @FlaggedApi("com.android.window.flags.density_390_api") public static final int DENSITY_390 = 390; // 0x186
field public static final int DENSITY_400 = 400; // 0x190
field public static final int DENSITY_420 = 420; // 0x1a4
field public static final int DENSITY_440 = 440; // 0x1b8
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 12b1f6a1fba3..b58c822e39bc 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -8,16 +8,6 @@ package android.app {
method @Deprecated public void setLatestEventInfo(android.content.Context, CharSequence, CharSequence, android.app.PendingIntent);
}
- public static final class Notification.BubbleMetadata implements android.os.Parcelable {
- method @Deprecated @Nullable public android.graphics.drawable.Icon getBubbleIcon();
- method @Deprecated @Nullable public android.app.PendingIntent getBubbleIntent();
- }
-
- public static final class Notification.BubbleMetadata.Builder {
- method @Deprecated @NonNull public android.app.Notification.BubbleMetadata.Builder createIntentBubble(@NonNull android.app.PendingIntent, @NonNull android.graphics.drawable.Icon);
- method @Deprecated @NonNull public android.app.Notification.BubbleMetadata.Builder createShortcutBubble(@NonNull String);
- }
-
public static class Notification.Builder {
method @Deprecated public android.app.Notification.Builder setChannel(String);
method @Deprecated public android.app.Notification.Builder setTimeout(long);
@@ -111,28 +101,6 @@ package android.graphics {
field @Deprecated public static final int MATRIX_SAVE_FLAG = 1; // 0x1
}
- public final class ImageDecoder implements java.lang.AutoCloseable {
- method @Deprecated public boolean getAsAlphaMask();
- method @Deprecated public boolean getConserveMemory();
- method @Deprecated public boolean getDecodeAsAlphaMask();
- method @Deprecated public boolean getMutable();
- method @Deprecated public boolean getRequireUnpremultiplied();
- method @Deprecated public android.graphics.ImageDecoder setAsAlphaMask(boolean);
- method @Deprecated public void setConserveMemory(boolean);
- method @Deprecated public android.graphics.ImageDecoder setDecodeAsAlphaMask(boolean);
- method @Deprecated public android.graphics.ImageDecoder setMutable(boolean);
- method @Deprecated public android.graphics.ImageDecoder setRequireUnpremultiplied(boolean);
- method @Deprecated public android.graphics.ImageDecoder setResize(int, int);
- method @Deprecated public android.graphics.ImageDecoder setResize(int);
- field @Deprecated public static final int ERROR_SOURCE_ERROR = 3; // 0x3
- field @Deprecated public static final int ERROR_SOURCE_EXCEPTION = 1; // 0x1
- field @Deprecated public static final int ERROR_SOURCE_INCOMPLETE = 2; // 0x2
- }
-
- @Deprecated public static class ImageDecoder.IncompleteException extends java.io.IOException {
- ctor public ImageDecoder.IncompleteException();
- }
-
@Deprecated public class LayerRasterizer extends android.graphics.Rasterizer {
ctor public LayerRasterizer();
method public void addLayer(android.graphics.Paint, float, float);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f4c8429619dd..71a05a909a09 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3101,6 +3101,10 @@ package android.service.watchdog {
package android.speech {
+ public abstract class RecognitionService extends android.app.Service {
+ method public void onBindInternal();
+ }
+
public class SpeechRecognizer {
method @MainThread @NonNull public static android.speech.SpeechRecognizer createOnDeviceTestingSpeechRecognizer(@NonNull android.content.Context);
method @RequiresPermission(android.Manifest.permission.MANAGE_SPEECH_RECOGNITION) public void setTemporaryOnDeviceRecognizer(@Nullable android.content.ComponentName);
diff --git a/core/java/android/accessibilityservice/AccessibilityTrace.java b/core/java/android/accessibilityservice/AccessibilityTrace.java
index f28015ab4685..7700b33253fd 100644
--- a/core/java/android/accessibilityservice/AccessibilityTrace.java
+++ b/core/java/android/accessibilityservice/AccessibilityTrace.java
@@ -35,7 +35,7 @@ public interface AccessibilityTrace {
String NAME_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK =
"IAccessibilityInteractionConnectionCallback";
String NAME_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK = "IRemoteMagnificationAnimationCallback";
- String NAME_WINDOW_MAGNIFICATION_CONNECTION = "IWindowMagnificationConnection";
+ String NAME_MAGNIFICATION_CONNECTION = "IMagnificationConnection";
String NAME_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK = "IWindowMagnificationConnectionCallback";
String NAME_WINDOW_MANAGER_INTERNAL = "WindowManagerInternal";
String NAME_WINDOWS_FOR_ACCESSIBILITY_CALLBACK = "WindowsForAccessibilityCallback";
@@ -58,7 +58,7 @@ public interface AccessibilityTrace {
long FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION = 0x0000000000000010L;
long FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK = 0x0000000000000020L;
long FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK = 0x0000000000000040L;
- long FLAGS_WINDOW_MAGNIFICATION_CONNECTION = 0x0000000000000080L;
+ long FLAGS_MAGNIFICATION_CONNECTION = 0x0000000000000080L;
long FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK = 0x0000000000000100L;
long FLAGS_WINDOW_MANAGER_INTERNAL = 0x0000000000000200L;
long FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK = 0x0000000000000400L;
@@ -98,7 +98,7 @@ public interface AccessibilityTrace {
NAME_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK,
FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK),
new AbstractMap.SimpleEntry<String, Long>(
- NAME_WINDOW_MAGNIFICATION_CONNECTION, FLAGS_WINDOW_MAGNIFICATION_CONNECTION),
+ NAME_MAGNIFICATION_CONNECTION, FLAGS_MAGNIFICATION_CONNECTION),
new AbstractMap.SimpleEntry<String, Long>(
NAME_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK),
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index aec042739c0d..71fe47e7b949 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -8370,9 +8370,29 @@ public class AppOpsManager {
* Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
* @hide
*/
+ public int unsafeCheckOpRawNoThrow(int op, @NonNull AttributionSource attributionSource) {
+ return unsafeCheckOpRawNoThrow(op, attributionSource.getUid(),
+ attributionSource.getPackageName(), attributionSource.getDeviceId());
+ }
+
+ /**
+ * Returns the <em>raw</em> mode associated with the op.
+ * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
+ * @hide
+ */
public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
+ return unsafeCheckOpRawNoThrow(op, uid, packageName, Context.DEVICE_ID_DEFAULT);
+ }
+
+ private int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName,
+ int virtualDeviceId) {
try {
- return mService.checkOperationRaw(op, uid, packageName, null);
+ if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
+ return mService.checkOperationRaw(op, uid, packageName, null);
+ } else {
+ return mService.checkOperationRawForDevice(op, uid, packageName, null,
+ Context.DEVICE_ID_DEFAULT);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -8517,12 +8537,29 @@ public class AppOpsManager {
}
/**
+ * @see #noteOp(String, int, String, String, String)
+ *
+ * @hide
+ */
+ public int noteOpNoThrow(int op, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ return noteOpNoThrow(op, attributionSource.getUid(), attributionSource.getPackageName(),
+ attributionSource.getAttributionTag(), attributionSource.getDeviceId(), message);
+ }
+
+ /**
* @see #noteOpNoThrow(String, int, String, String, String)
*
* @hide
*/
public int noteOpNoThrow(int op, int uid, @Nullable String packageName,
@Nullable String attributionTag, @Nullable String message) {
+ return noteOpNoThrow(op, uid, packageName, attributionTag, Context.DEVICE_ID_DEFAULT,
+ message);
+ }
+
+ private int noteOpNoThrow(int op, int uid, @Nullable String packageName,
+ @Nullable String attributionTag, int virtualDeviceId, @Nullable String message) {
try {
collectNoteOpCallsForValidation(op);
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
@@ -8535,9 +8572,15 @@ public class AppOpsManager {
}
}
- SyncNotedAppOp syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
+ SyncNotedAppOp syncOp;
+ if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
+ syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
-
+ } else {
+ syncOp = mService.noteOperationForDevice(op, uid, packageName, attributionTag,
+ virtualDeviceId, collectionMode == COLLECT_ASYNC, message,
+ shouldCollectMessage);
+ }
if (syncOp.getOpMode() == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
collectNotedOpForSelf(syncOp);
@@ -8775,7 +8818,8 @@ public class AppOpsManager {
@UnsupportedAppUsage
public int checkOp(int op, int uid, String packageName) {
try {
- int mode = mService.checkOperation(op, uid, packageName);
+ int mode = mService.checkOperationForDevice(op, uid, packageName,
+ Context.DEVICE_ID_DEFAULT);
if (mode == MODE_ERRORED) {
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
@@ -8786,6 +8830,19 @@ public class AppOpsManager {
}
/**
+ * Like {@link #checkOp} but instead of throwing a {@link SecurityException}, it
+ * returns {@link #MODE_ERRORED}.
+ *
+ * @see #checkOp(int, int, String)
+ *
+ * @hide
+ */
+ public int checkOpNoThrow(int op, AttributionSource attributionSource) {
+ return checkOpNoThrow(op, attributionSource.getUid(), attributionSource.getPackageName(),
+ attributionSource.getDeviceId());
+ }
+
+ /**
* Like {@link #checkOp} but instead of throwing a {@link SecurityException} it
* returns {@link #MODE_ERRORED}.
*
@@ -8795,8 +8852,18 @@ public class AppOpsManager {
*/
@UnsupportedAppUsage
public int checkOpNoThrow(int op, int uid, String packageName) {
+ return checkOpNoThrow(op, uid, packageName, Context.DEVICE_ID_DEFAULT);
+ }
+
+ private int checkOpNoThrow(int op, int uid, String packageName, int virtualDeviceId) {
try {
- int mode = mService.checkOperation(op, uid, packageName);
+ int mode;
+ if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
+ mode = mService.checkOperation(op, uid, packageName);
+ } else {
+ mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId);
+ }
+
return mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : mode;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -9026,9 +9093,32 @@ public class AppOpsManager {
*
* @hide
*/
+ public int startOpNoThrow(@NonNull IBinder token, int op,
+ @NonNull AttributionSource attributionSource,
+ boolean startIfModeDefault, @Nullable String message,
+ @AttributionFlags int attributionFlags, int attributionChainId) {
+ return startOpNoThrow(token, op, attributionSource.getUid(),
+ attributionSource.getPackageName(), startIfModeDefault,
+ attributionSource.getAttributionTag(), attributionSource.getDeviceId(),
+ message, attributionFlags, attributionChainId);
+ }
+
+ /**
+ * @see #startOpNoThrow(String, int, String, String, String)
+ *
+ * @hide
+ */
public int startOpNoThrow(@NonNull IBinder token, int op, int uid, @NonNull String packageName,
boolean startIfModeDefault, @Nullable String attributionTag, @Nullable String message,
@AttributionFlags int attributionFlags, int attributionChainId) {
+ return startOpNoThrow(token, op, uid, packageName, startIfModeDefault, attributionTag,
+ Context.DEVICE_ID_DEFAULT, message, attributionFlags, attributionChainId);
+ }
+
+ private int startOpNoThrow(@NonNull IBinder token, int op, int uid, @NonNull String packageName,
+ boolean startIfModeDefault, @Nullable String attributionTag, int virtualDeviceId,
+ @Nullable String message, @AttributionFlags int attributionFlags,
+ int attributionChainId) {
try {
collectNoteOpCallsForValidation(op);
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
@@ -9041,10 +9131,17 @@ public class AppOpsManager {
}
}
- SyncNotedAppOp syncOp = mService.startOperation(token, op, uid, packageName,
+ SyncNotedAppOp syncOp;
+ if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
+ syncOp = mService.startOperation(token, op, uid, packageName,
attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message,
shouldCollectMessage, attributionFlags, attributionChainId);
-
+ } else {
+ syncOp = mService.startOperationForDevice(token, op, uid, packageName,
+ attributionTag, virtualDeviceId, startIfModeDefault,
+ collectionMode == COLLECT_ASYNC, message, shouldCollectMessage,
+ attributionFlags, attributionChainId);
+ }
if (syncOp.getOpMode() == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
collectNotedOpForSelf(syncOp);
@@ -9252,10 +9349,31 @@ public class AppOpsManager {
*
* @hide
*/
+ public void finishOp(IBinder token, int op, @NonNull AttributionSource attributionSource) {
+ finishOp(token, op, attributionSource.getUid(),
+ attributionSource.getPackageName(), attributionSource.getAttributionTag(),
+ attributionSource.getDeviceId());
+ }
+
+ /**
+ * @see #finishOp(String, int, String, String)
+ *
+ * @hide
+ */
public void finishOp(IBinder token, int op, int uid, @NonNull String packageName,
@Nullable String attributionTag) {
+ finishOp(token, op, uid, packageName, attributionTag, Context.DEVICE_ID_DEFAULT);
+ }
+
+ private void finishOp(IBinder token, int op, int uid, @NonNull String packageName,
+ @Nullable String attributionTag, int virtualDeviceId ) {
try {
- mService.finishOperation(token, op, uid, packageName, attributionTag);
+ if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
+ mService.finishOperation(token, op, uid, packageName, attributionTag);
+ } else {
+ mService.finishOperationForDevice(token, op, uid, packageName, attributionTag,
+ virtualDeviceId);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 43023fe9c2ab..8daee5867238 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -26,11 +26,11 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.app.IAppOpsCallback;
-import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.DodecFunction;
+import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
-import com.android.internal.util.function.QuintConsumer;
-import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.UndecFunction;
/**
@@ -48,13 +48,14 @@ public abstract class AppOpsManagerInternal {
* @param uid The UID for which to check.
* @param packageName The package for which to check.
* @param attributionTag The attribution tag for which to check.
+ * @param virtualDeviceId the device for which to check the op
* @param raw Whether to check the raw op i.e. not interpret the mode based on UID state.
* @param superImpl The super implementation.
* @return The app op check result.
*/
int checkOperation(int code, int uid, String packageName, @Nullable String attributionTag,
- boolean raw, QuintFunction<Integer, Integer, String, String, Boolean, Integer>
- superImpl);
+ int virtualDeviceId, boolean raw, HexFunction<Integer, Integer, String, String,
+ Integer, Boolean, Integer> superImpl);
/**
* Allows overriding check audio operation behavior.
@@ -76,16 +77,17 @@ public abstract class AppOpsManagerInternal {
* @param uid The UID for which to note.
* @param packageName The package for which to note. {@code null} for system package.
* @param featureId Id of the feature in the package
+ * @param virtualDeviceId the device for which to note the op
* @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
* @param message The message in the async noted op
* @param superImpl The super implementation.
* @return The app op note result.
*/
SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
- @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
+ @Nullable String featureId, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage,
- @NonNull HeptFunction<Integer, Integer, String, String, Boolean, String, Boolean,
- SyncNotedAppOp> superImpl);
+ @NonNull OctFunction<Integer, Integer, String, String, Integer, Boolean, String,
+ Boolean, SyncNotedAppOp> superImpl);
/**
* Allows overriding note proxy operation behavior.
@@ -113,6 +115,7 @@ public abstract class AppOpsManagerInternal {
* @param uid The UID for which to note.
* @param packageName The package for which to note. {@code null} for system package.
* @param attributionTag the attribution tag.
+ * @param virtualDeviceId the device for which to start the op
* @param startIfModeDefault Whether to start the op of the mode is default.
* @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
* @param message The message in the async noted op
@@ -123,11 +126,11 @@ public abstract class AppOpsManagerInternal {
* @return The app op note result.
*/
SyncNotedAppOp startOperation(IBinder token, int code, int uid,
- @Nullable String packageName, @Nullable String attributionTag,
+ @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage,
@AttributionFlags int attributionFlags, int attributionChainId,
- @NonNull UndecFunction<IBinder, Integer, Integer, String, String, Boolean,
+ @NonNull DodecFunction<IBinder, Integer, Integer, String, String, Integer, Boolean,
Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl);
/**
@@ -164,11 +167,13 @@ public abstract class AppOpsManagerInternal {
* @param uid The UID for which the op was noted.
* @param packageName The package for which it was noted. {@code null} for system package.
* @param attributionTag the attribution tag.
+ * @param virtualDeviceId the device for which to finish the op
+ * @param superImpl
*/
default void finishOperation(IBinder clientId, int code, int uid, String packageName,
- String attributionTag,
- @NonNull QuintConsumer<IBinder, Integer, Integer, String, String> superImpl) {
- superImpl.accept(clientId, code, uid, packageName, attributionTag);
+ String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer,
+ Integer, String, String, Integer> superImpl) {
+ superImpl.accept(clientId, code, uid, packageName, attributionTag, virtualDeviceId);
}
/**
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index fd13174a6a39..b781ce50c4db 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -1112,12 +1112,17 @@ public class DownloadManager {
* ready to execute it and connectivity is available.
*
* @param request the parameters specifying this download
- * @return an ID for the download, unique across the system. This ID is used to make future
- * calls related to this download.
+ * @return an ID for the download, unique across the system. This ID is used to make
+ * future calls related to this download. Returns -1 if the operation fails.
*/
public long enqueue(Request request) {
ContentValues values = request.toContentValues(mPackageName);
Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values);
+ if (downloadUri == null) {
+ // If insert fails due to RemoteException, it would return a null uri.
+ return -1;
+ }
+
long id = Long.parseLong(downloadUri.getLastPathSegment());
return id;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8c5773a05764..c003540100ae 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -10383,16 +10383,6 @@ public class Notification implements Parcelable
}
/**
- * @deprecated use {@link #getIntent()} instead.
- * @removed Removed from the R SDK but was never publicly stable.
- */
- @Nullable
- @Deprecated
- public PendingIntent getBubbleIntent() {
- return mPendingIntent;
- }
-
- /**
* @return the pending intent to send when the bubble is dismissed by a user, if one exists.
*/
@Nullable
@@ -10411,16 +10401,6 @@ public class Notification implements Parcelable
}
/**
- * @deprecated use {@link #getIcon()} instead.
- * @removed Removed from the R SDK but was never publicly stable.
- */
- @Nullable
- @Deprecated
- public Icon getBubbleIcon() {
- return mIcon;
- }
-
- /**
* @return the ideal height, in DPs, for the floating window that app content defined by
* {@link #getIntent()} for this bubble. A value of 0 indicates a desired height has
* not been set.
@@ -10677,48 +10657,6 @@ public class Notification implements Parcelable
}
/**
- * @deprecated use {@link Builder#Builder(String)} instead.
- * @removed Removed from the R SDK but was never publicly stable.
- */
- @NonNull
- @Deprecated
- public BubbleMetadata.Builder createShortcutBubble(@NonNull String shortcutId) {
- if (!TextUtils.isEmpty(shortcutId)) {
- // If shortcut id is set, we don't use these if they were previously set.
- mPendingIntent = null;
- mIcon = null;
- }
- mShortcutId = shortcutId;
- return this;
- }
-
- /**
- * @deprecated use {@link Builder#Builder(PendingIntent, Icon)} instead.
- * @removed Removed from the R SDK but was never publicly stable.
- */
- @NonNull
- @Deprecated
- public BubbleMetadata.Builder createIntentBubble(@NonNull PendingIntent intent,
- @NonNull Icon icon) {
- if (intent == null) {
- throw new IllegalArgumentException("Bubble requires non-null pending intent");
- }
- if (icon == null) {
- throw new IllegalArgumentException("Bubbles require non-null icon");
- }
- if (icon.getType() != TYPE_URI_ADAPTIVE_BITMAP
- && icon.getType() != TYPE_URI) {
- Log.w(TAG, "Bubbles work best with icons of TYPE_URI or "
- + "TYPE_URI_ADAPTIVE_BITMAP. "
- + "In the future, using an icon of this type will be required.");
- }
- mShortcutId = null;
- mPendingIntent = intent;
- mIcon = icon;
- return this;
- }
-
- /**
* Sets the intent for the bubble.
*
* <p>The intent that will be used when the bubble is expanded. This will display the
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 697c25c2a1ec..b2074a6e7309 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -107,6 +107,13 @@ public final class AttributionSource implements Parcelable {
}
/** @hide */
+ public AttributionSource(int uid, @Nullable String packageName,
+ @Nullable String attributionTag, int virtualDeviceId) {
+ this(uid, Process.INVALID_PID, packageName, attributionTag, sDefaultToken, null,
+ virtualDeviceId, null);
+ }
+
+ /** @hide */
public AttributionSource(int uid, int pid, @Nullable String packageName,
@Nullable String attributionTag) {
this(uid, pid, packageName, attributionTag, sDefaultToken);
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 69d86a6604ad..437668c9a7de 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -44,3 +44,10 @@ flag {
description: "Enables the independent keyboard vibration settings feature"
bug: "289107579"
}
+
+flag {
+ namespace: "haptics"
+ name: "adaptive_haptics_enabled"
+ description: "Enables the adaptive haptics feature"
+ bug: "305961689"
+}
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index d76fa5be4940..531626b1e85f 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -257,7 +257,10 @@ public final class Condition implements Parcelable {
throw new IllegalArgumentException("state is invalid: " + state);
}
- /** Provides a human-readable string version of the Source enum. */
+ /**
+ * Provides a human-readable string version of the Source enum.
+ * @hide
+ */
@FlaggedApi(Flags.FLAG_MODES_API)
public static @NonNull String sourceToString(@Source int source) {
if (source == SOURCE_UNKNOWN) return "SOURCE_UNKNOWN";
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 7f313c177053..cd80a0b597e4 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
+import android.annotation.TestApi;
import android.app.AppOpsManager;
import android.app.Service;
import android.content.AttributionSource;
@@ -514,9 +515,15 @@ public abstract class RecognitionService extends Service {
@Override
public final IBinder onBind(final Intent intent) {
if (DBG) Log.d(TAG, "#onBind, intent=" + intent);
+ onBindInternal();
return mBinder;
}
+ /** @hide */
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+ @TestApi
+ public void onBindInternal() { }
+
@Override
public void onDestroy() {
if (DBG) Log.d(TAG, "#onDestroy");
diff --git a/core/java/android/speech/SpeechRecognizerImpl.java b/core/java/android/speech/SpeechRecognizerImpl.java
index 6c008743f07a..f3f17defce5d 100644
--- a/core/java/android/speech/SpeechRecognizerImpl.java
+++ b/core/java/android/speech/SpeechRecognizerImpl.java
@@ -61,6 +61,7 @@ class SpeechRecognizerImpl extends SpeechRecognizer {
private static final int MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT = 5;
private static final int MSG_CHECK_RECOGNITION_SUPPORT = 6;
private static final int MSG_TRIGGER_MODEL_DOWNLOAD = 7;
+ private static final int MSG_DESTROY = 8;
/** The actual RecognitionService endpoint */
private IRecognitionService mService;
@@ -77,39 +78,31 @@ class SpeechRecognizerImpl extends SpeechRecognizer {
private IRecognitionServiceManager mManagerService;
/** Handler that will execute the main tasks */
- private Handler mHandler = new Handler(Looper.getMainLooper()) {
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_START:
- handleStartListening((Intent) msg.obj);
- break;
- case MSG_STOP:
- handleStopMessage();
- break;
- case MSG_CANCEL:
- handleCancelMessage();
- break;
- case MSG_CHANGE_LISTENER:
- handleChangeListener((RecognitionListener) msg.obj);
- break;
- case MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT:
- handleSetTemporaryComponent((ComponentName) msg.obj);
- break;
- case MSG_CHECK_RECOGNITION_SUPPORT:
+ case MSG_START -> handleStartListening((Intent) msg.obj);
+ case MSG_STOP -> handleStopMessage();
+ case MSG_CANCEL -> handleCancelMessage();
+ case MSG_CHANGE_LISTENER -> handleChangeListener((RecognitionListener) msg.obj);
+ case MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT ->
+ handleSetTemporaryComponent((ComponentName) msg.obj);
+ case MSG_CHECK_RECOGNITION_SUPPORT -> {
CheckRecognitionSupportArgs args = (CheckRecognitionSupportArgs) msg.obj;
handleCheckRecognitionSupport(
args.mIntent, args.mCallbackExecutor, args.mCallback);
- break;
- case MSG_TRIGGER_MODEL_DOWNLOAD:
+ }
+ case MSG_TRIGGER_MODEL_DOWNLOAD -> {
ModelDownloadListenerArgs modelDownloadListenerArgs =
(ModelDownloadListenerArgs) msg.obj;
handleTriggerModelDownload(
modelDownloadListenerArgs.mIntent,
modelDownloadListenerArgs.mExecutor,
modelDownloadListenerArgs.mModelDownloadListener);
- break;
+ }
+ case MSG_DESTROY -> handleDestroy();
}
}
};
@@ -433,6 +426,10 @@ class SpeechRecognizerImpl extends SpeechRecognizer {
@Override
public void destroy() {
+ putMessage(mHandler.obtainMessage(MSG_DESTROY));
+ }
+
+ private void handleDestroy() {
if (mService != null) {
try {
mService.cancel(mListener, /*isShutdown*/ true);
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 9148c4a101d7..f14485b09424 100755
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -16,6 +16,9 @@
package android.util;
+import static com.android.window.flags.Flags.FLAG_DENSITY_390_API;
+
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -59,6 +62,7 @@ public class DisplayMetrics {
DENSITY_XHIGH,
DENSITY_340,
DENSITY_360,
+ DENSITY_390,
DENSITY_400,
DENSITY_420,
DENSITY_440,
@@ -182,6 +186,15 @@ public class DisplayMetrics {
* This is not a density that applications should target, instead relying
* on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
*/
+ @FlaggedApi(FLAG_DENSITY_390_API)
+ public static final int DENSITY_390 = 390;
+
+ /**
+ * Intermediate density for screens that sit somewhere between
+ * {@link #DENSITY_XHIGH} (320 dpi) and {@link #DENSITY_XXHIGH} (480 dpi).
+ * This is not a density that applications should target, instead relying
+ * on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
+ */
public static final int DENSITY_400 = 400;
/**
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 07ae1345a688..3dbe65ef4180 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1823,13 +1823,13 @@ public final class AccessibilityManager {
/**
*
- * Sets an {@link IWindowMagnificationConnection} that manipulates window magnification.
+ * Sets an {@link IMagnificationConnection} that manipulates magnification in SystemUI.
*
- * @param connection The connection that manipulates window magnification.
+ * @param connection The connection that manipulates magnification in SystemUI.
* @hide
*/
- public void setWindowMagnificationConnection(@Nullable
- IWindowMagnificationConnection connection) {
+ public void setMagnificationConnection(@Nullable
+ IMagnificationConnection connection) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
@@ -1838,9 +1838,9 @@ public final class AccessibilityManager {
}
}
try {
- service.setWindowMagnificationConnection(connection);
+ service.setMagnificationConnection(connection);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error setting window magnfication connection", re);
+ Log.e(LOG_TAG, "Error setting magnification connection", re);
}
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 7a1112f1446f..f741080c57a8 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -27,7 +27,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityManagerClient;
import android.view.accessibility.AccessibilityWindowAttributes;
-import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IMagnificationConnection;
import android.view.InputEvent;
import android.view.IWindow;
import android.view.MagnificationSpec;
@@ -90,7 +90,7 @@ interface IAccessibilityManager {
oneway void registerSystemAction(in RemoteAction action, int actionId);
oneway void unregisterSystemAction(int actionId);
- oneway void setWindowMagnificationConnection(in IWindowMagnificationConnection connection);
+ oneway void setMagnificationConnection(in IMagnificationConnection connection);
void associateEmbeddedHierarchy(IBinder host, IBinder embedded);
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IMagnificationConnection.aidl
index a404bd6f8c97..a5e8aaf97de4 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IMagnificationConnection.aidl
@@ -23,11 +23,11 @@ import android.view.accessibility.IRemoteMagnificationAnimationCallback;
/**
* Interface for interaction between {@link AccessibilityManagerService}
- * and {@link WindowMagnification} in SystemUI.
+ * and {@link Magnification} in SystemUI.
*
* @hide
*/
-oneway interface IWindowMagnificationConnection {
+oneway interface IMagnificationConnection {
/**
* Enables window magnification on specified display with given center and scale and animation.
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 492e2ac7cc28..3a321e5c26f7 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -140,14 +140,26 @@ interface IAppOpsService {
void collectNoteOpCallsForValidation(String stackTrace, int op, String packageName, long version);
SyncNotedAppOp noteProxyOperationWithState(int code,
- in AttributionSourceState attributionSourceStateState,
- boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
- boolean skipProxyOperation);
+ in AttributionSourceState attributionSourceStateState,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation);
SyncNotedAppOp startProxyOperationWithState(IBinder clientId, int code,
- in AttributionSourceState attributionSourceStateState, boolean startIfModeDefault,
- boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
- boolean skipProxyOperation, int proxyAttributionFlags, int proxiedAttributionFlags,
- int attributionChainId);
+ in AttributionSourceState attributionSourceStateState, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation, int proxyAttributionFlags, int proxiedAttributionFlags,
+ int attributionChainId);
void finishProxyOperationWithState(IBinder clientId, int code,
- in AttributionSourceState attributionSourceStateState, boolean skipProxyOperation);
+ in AttributionSourceState attributionSourceStateState, boolean skipProxyOperation);
+ int checkOperationRawForDevice(int code, int uid, String packageName,
+ @nullable String attributionTag, int virtualDeviceId);
+ int checkOperationForDevice(int code, int uid, String packageName, int virtualDeviceId);
+ SyncNotedAppOp noteOperationForDevice(int code, int uid, String packageName,
+ @nullable String attributionTag, int virtualDeviceId,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
+ SyncNotedAppOp startOperationForDevice(IBinder clientId, int code, int uid, String packageName,
+ @nullable String attributionTag, int virtualDeviceId, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ int attributionFlags, int attributionChainId);
+ void finishOperationForDevice(IBinder clientId, int code, int uid, String packageName,
+ @nullable String attributionTag, int virtualDeviceId);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 4d8eeac6d5ab..5351c6dbf94f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -285,12 +285,12 @@ oneway interface IStatusBar
void suppressAmbientDisplay(boolean suppress);
/**
- * Requests {@link WindowMagnification} to set window magnification connection through
- * {@link AccessibilityManager#setWindowMagnificationConnection(IWindowMagnificationConnection)}
+ * Requests {@link Magnification} to set magnification connection to SystemUI through
+ * {@link AccessibilityManager#setMagnificationConnection(IMagnificationConnection)}
*
* @param connect {@code true} if needs connection, otherwise set the connection to null.
*/
- void requestWindowMagnificationConnection(boolean connect);
+ void requestMagnificationConnection(boolean connect);
/**
* Allow for pass-through arguments from `adb shell cmd statusbar <args>`, and write to the
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index 84252f97d8e5..e2f255407a37 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -208,14 +208,14 @@ public class AccessibilityManagerTest {
}
@Test
- public void testSetWindowMagnificationConnection() throws Exception {
+ public void testSetMagnificationConnection() throws Exception {
AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
- IWindowMagnificationConnection connection = Mockito.mock(
- IWindowMagnificationConnection.class);
+ IMagnificationConnection connection = Mockito.mock(
+ IMagnificationConnection.class);
- manager.setWindowMagnificationConnection(connection);
+ manager.setMagnificationConnection(connection);
- verify(mMockService).setWindowMagnificationConnection(connection);
+ verify(mMockService).setMagnificationConnection(connection);
}
@Test
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index b2da233fc21e..639517996724 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -681,12 +681,6 @@ public final class ImageDecoder implements AutoCloseable {
}
};
- /** @removed
- * @deprecated Subsumed by {@link #DecodeException}.
- */
- @Deprecated
- public static class IncompleteException extends IOException {};
-
/**
* Interface for changing the default settings of a decode.
*
@@ -713,24 +707,6 @@ public final class ImageDecoder implements AutoCloseable {
};
- /** @removed
- * @deprecated Replaced by {@link #DecodeException#SOURCE_EXCEPTION}.
- */
- @Deprecated
- public static final int ERROR_SOURCE_EXCEPTION = 1;
-
- /** @removed
- * @deprecated Replaced by {@link #DecodeException#SOURCE_INCOMPLETE}.
- */
- @Deprecated
- public static final int ERROR_SOURCE_INCOMPLETE = 2;
-
- /** @removed
- * @deprecated Replaced by {@link #DecodeException#SOURCE_MALFORMED_DATA}.
- */
- @Deprecated
- public static final int ERROR_SOURCE_ERROR = 3;
-
/**
* Information about an interrupted decode.
*/
@@ -1178,14 +1154,6 @@ public final class ImageDecoder implements AutoCloseable {
}
// Modifiers
- /** @removed
- * @deprecated Renamed to {@link #setTargetSize}.
- */
- @Deprecated
- public ImageDecoder setResize(int width, int height) {
- this.setTargetSize(width, height);
- return this;
- }
/**
* Specify the size of the output {@link Drawable} or {@link Bitmap}.
@@ -1217,15 +1185,6 @@ public final class ImageDecoder implements AutoCloseable {
mDesiredHeight = height;
}
- /** @removed
- * @deprecated Renamed to {@link #setTargetSampleSize}.
- */
- @Deprecated
- public ImageDecoder setResize(int sampleSize) {
- this.setTargetSampleSize(sampleSize);
- return this;
- }
-
private int getTargetDimension(int original, int sampleSize, int computed) {
// Sampling will never result in a smaller size than 1.
if (sampleSize >= original) {
@@ -1381,15 +1340,6 @@ public final class ImageDecoder implements AutoCloseable {
mUnpremultipliedRequired = unpremultipliedRequired;
}
- /** @removed
- * @deprecated Renamed to {@link #setUnpremultipliedRequired}.
- */
- @Deprecated
- public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) {
- this.setUnpremultipliedRequired(unpremultipliedRequired);
- return this;
- }
-
/**
* Return whether the {@link Bitmap} will have unpremultiplied pixels.
*/
@@ -1397,14 +1347,6 @@ public final class ImageDecoder implements AutoCloseable {
return mUnpremultipliedRequired;
}
- /** @removed
- * @deprecated Renamed to {@link #isUnpremultipliedRequired}.
- */
- @Deprecated
- public boolean getRequireUnpremultiplied() {
- return this.isUnpremultipliedRequired();
- }
-
/**
* Modify the image after decoding and scaling.
*
@@ -1528,15 +1470,6 @@ public final class ImageDecoder implements AutoCloseable {
mMutable = mutable;
}
- /** @removed
- * @deprecated Renamed to {@link #setMutableRequired}.
- */
- @Deprecated
- public ImageDecoder setMutable(boolean mutable) {
- this.setMutableRequired(mutable);
- return this;
- }
-
/**
* Return whether the decoded {@link Bitmap} will be mutable.
*/
@@ -1544,14 +1477,6 @@ public final class ImageDecoder implements AutoCloseable {
return mMutable;
}
- /** @removed
- * @deprecated Renamed to {@link #isMutableRequired}.
- */
- @Deprecated
- public boolean getMutable() {
- return this.isMutableRequired();
- }
-
/**
* Save memory if possible by using a denser {@link Bitmap.Config} at the
* cost of some image quality.
@@ -1597,22 +1522,6 @@ public final class ImageDecoder implements AutoCloseable {
return mConserveMemory ? MEMORY_POLICY_LOW_RAM : MEMORY_POLICY_DEFAULT;
}
- /** @removed
- * @deprecated Replaced by {@link #setMemorySizePolicy}.
- */
- @Deprecated
- public void setConserveMemory(boolean conserveMemory) {
- mConserveMemory = conserveMemory;
- }
-
- /** @removed
- * @deprecated Replaced by {@link #getMemorySizePolicy}.
- */
- @Deprecated
- public boolean getConserveMemory() {
- return mConserveMemory;
- }
-
/**
* Specify whether to potentially treat the output as an alpha mask.
*
@@ -1632,24 +1541,6 @@ public final class ImageDecoder implements AutoCloseable {
mDecodeAsAlphaMask = enabled;
}
- /** @removed
- * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
- */
- @Deprecated
- public ImageDecoder setDecodeAsAlphaMask(boolean enabled) {
- this.setDecodeAsAlphaMaskEnabled(enabled);
- return this;
- }
-
- /** @removed
- * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
- */
- @Deprecated
- public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
- this.setDecodeAsAlphaMask(asAlphaMask);
- return this;
- }
-
/**
* Return whether to treat single channel input as alpha.
*
@@ -1662,22 +1553,6 @@ public final class ImageDecoder implements AutoCloseable {
return mDecodeAsAlphaMask;
}
- /** @removed
- * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
- */
- @Deprecated
- public boolean getDecodeAsAlphaMask() {
- return mDecodeAsAlphaMask;
- }
-
- /** @removed
- * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
- */
- @Deprecated
- public boolean getAsAlphaMask() {
- return this.getDecodeAsAlphaMask();
- }
-
/**
* Specify the desired {@link ColorSpace} for the output.
*
diff --git a/media/java/android/media/AudioHalVersionInfo.java b/media/java/android/media/AudioHalVersionInfo.java
index c881a03c6732..0f48abeb6882 100644
--- a/media/java/android/media/AudioHalVersionInfo.java
+++ b/media/java/android/media/AudioHalVersionInfo.java
@@ -80,8 +80,9 @@ public final class AudioHalVersionInfo implements Parcelable, Comparable<AudioHa
* List of all valid Audio HAL versions. This list need to be in sync with sAudioHALVersions
* defined in frameworks/av/media/libaudiohal/FactoryHalHidl.cpp.
*/
+ // TODO: add AIDL_1_0 with sAudioHALVersions.
public static final @NonNull List<AudioHalVersionInfo> VERSIONS =
- List.of(AIDL_1_0, HIDL_7_1, HIDL_7_0, HIDL_6_0, HIDL_5_0, HIDL_4_0);
+ List.of(HIDL_7_1, HIDL_7_0, HIDL_6_0, HIDL_5_0, HIDL_4_0);
private static final String TAG = "AudioHalVersionInfo";
private AudioHalVersion mHalVersion = new AudioHalVersion();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 1e3234902a45..627f73c750fe 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -20,6 +20,7 @@ import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAUL
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.media.audio.Flags.autoPublicVolumeApiHardening;
+import static android.media.audio.Flags.automaticBtDeviceType;
import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
import android.Manifest;
@@ -7170,10 +7171,52 @@ public class AudioManager {
* Sets the audio device type of a Bluetooth device given its MAC address
*/
@RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
- public void setBluetoothAudioDeviceCategory(@NonNull String address, boolean isBle,
+ public void setBluetoothAudioDeviceCategory_legacy(@NonNull String address, boolean isBle,
@AudioDeviceCategory int btAudioDeviceType) {
+ if (automaticBtDeviceType()) {
+ // do nothing
+ return;
+ }
+ try {
+ getService().setBluetoothAudioDeviceCategory_legacy(address, isBle, btAudioDeviceType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Gets the audio device type of a Bluetooth device given its MAC address
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ @AudioDeviceCategory
+ public int getBluetoothAudioDeviceCategory_legacy(@NonNull String address, boolean isBle) {
+ if (automaticBtDeviceType()) {
+ return AUDIO_DEVICE_CATEGORY_UNKNOWN;
+ }
+ try {
+ return getService().getBluetoothAudioDeviceCategory_legacy(address, isBle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Sets the audio device type of a Bluetooth device given its MAC address
+ *
+ * @return {@code true} if the device type was set successfully. If the
+ * audio device type was automatically identified this method will
+ * return {@code false}.
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public boolean setBluetoothAudioDeviceCategory(@NonNull String address,
+ @AudioDeviceCategory int btAudioDeviceCategory) {
+ if (!automaticBtDeviceType()) {
+ return false;
+ }
try {
- getService().setBluetoothAudioDeviceCategory(address, isBle, btAudioDeviceType);
+ return getService().setBluetoothAudioDeviceCategory(address, btAudioDeviceCategory);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -7185,9 +7228,29 @@ public class AudioManager {
*/
@RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
@AudioDeviceCategory
- public int getBluetoothAudioDeviceCategory(@NonNull String address, boolean isBle) {
+ public int getBluetoothAudioDeviceCategory(@NonNull String address) {
+ if (!automaticBtDeviceType()) {
+ return AUDIO_DEVICE_CATEGORY_UNKNOWN;
+ }
+ try {
+ return getService().getBluetoothAudioDeviceCategory(address);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns {@code true} if the audio device type of a Bluetooth device can
+ * be automatically identified
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public boolean isBluetoothAudioDeviceCategoryFixed(@NonNull String address) {
+ if (!automaticBtDeviceType()) {
+ return false;
+ }
try {
- return getService().getBluetoothAudioDeviceCategory(address, isBle);
+ return getService().isBluetoothAudioDeviceCategoryFixed(address);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 5c8758a6aa73..a52f0b08330d 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -338,10 +338,20 @@ interface IAudioService {
oneway void setCsdAsAFeatureEnabled(boolean csdToggleValue);
@EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
- oneway void setBluetoothAudioDeviceCategory(in String address, boolean isBle, int deviceType);
+ oneway void setBluetoothAudioDeviceCategory_legacy(in String address, boolean isBle,
+ int deviceCategory);
@EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
- int getBluetoothAudioDeviceCategory(in String address, boolean isBle);
+ int getBluetoothAudioDeviceCategory_legacy(in String address, boolean isBle);
+
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ boolean setBluetoothAudioDeviceCategory(in String address, int deviceCategory);
+
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ int getBluetoothAudioDeviceCategory(in String address);
+
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ boolean isBluetoothAudioDeviceCategoryFixed(in String address);
int setHdmiSystemAudioSupported(boolean on);
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index b40e911bd8de..9703c347859c 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
#
[versions]
-agp = "8.1.4"
+agp = "8.2.0"
compose-compiler = "1.5.1"
dexmaker-mockito = "2.28.3"
jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
index 033e24c4cdf4..d64cd4917707 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index ce89de6ffd65..516749de880a 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,7 +16,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/packages/SettingsLib/Spa/gradlew b/packages/SettingsLib/Spa/gradlew
index fcb6fca147c0..1aa94a426907 100755
--- a/packages/SettingsLib/Spa/gradlew
+++ b/packages/SettingsLib/Spa/gradlew
@@ -83,7 +83,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC3045
+ # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC3045
+ # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -201,11 +202,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-# Collect all arguments for the java command;
-# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-# shell script including quotes and variable substitutions, so put them in
-# double quotes to make sure that they get re-expanded; and
-# * put everything else in single quotes, so that it's not re-expanded.
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt
index de2cf1f5fdf6..81a8b324f70f 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt
@@ -19,11 +19,11 @@ package com.android.settingslib.spaprivileged.model.app
import android.content.Context
import android.content.pm.ApplicationInfo
import android.graphics.drawable.Drawable
+import android.util.IconDrawableFactory
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
import androidx.compose.ui.platform.LocalContext
-import com.android.settingslib.Utils
import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.framework.common.userManager
@@ -65,6 +65,7 @@ interface AppRepository {
internal class AppRepositoryImpl(private val context: Context) : AppRepository {
private val packageManager = context.packageManager
+ private val iconDrawableFactory = IconDrawableFactory.newInstance(context)
override fun loadLabel(app: ApplicationInfo): String = app.loadLabel(packageManager).toString()
@@ -72,7 +73,7 @@ internal class AppRepositoryImpl(private val context: Context) : AppRepository {
override fun produceIcon(app: ApplicationInfo) =
produceState<Drawable?>(initialValue = null, app) {
withContext(Dispatchers.IO) {
- value = Utils.getBadgedIcon(context, app)
+ value = iconDrawableFactory.getBadgedIcon(app)
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppRepositoryTest.kt
index 8f458d33c126..70e405557dc7 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppRepositoryTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppRepositoryTest.kt
@@ -27,14 +27,12 @@ import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.google.common.truth.Truth.assertThat
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Spy
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@@ -42,23 +40,14 @@ class AppRepositoryTest {
@get:Rule
val composeTestRule = createComposeRule()
- @get:Rule
- val mockito: MockitoRule = MockitoJUnit.rule()
-
- @Spy
- private val context: Context = ApplicationProvider.getApplicationContext()
-
- @Mock
- private lateinit var userManager: UserManager
+ private val userManager = mock<UserManager>()
- private lateinit var appRepository: AppRepositoryImpl
-
- @Before
- fun setUp() {
- whenever(context.userManager).thenReturn(userManager)
- appRepository = AppRepositoryImpl(context)
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { userManager } doReturn userManager
}
+ private val appRepository = AppRepositoryImpl(context)
+
@Test
fun produceIconContentDescription_workProfile() {
whenever(userManager.isManagedProfile(APP.userId)).thenReturn(true)
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 079cde08f9bb..8e1067f28ffb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -81,6 +81,8 @@ import java.util.Objects;
import java.util.UUID;
import java.util.regex.Pattern;
+import javax.annotation.Nullable;
+
/**
* Keeps track of information about all installed applications, lazy-loading
* as needed.
@@ -492,7 +494,8 @@ public class ApplicationsState {
ApplicationInfo info = getAppInfoLocked(packageName, userId);
if (info == null) {
try {
- info = mIpm.getApplicationInfo(packageName, 0, userId);
+ info = mIpm.getApplicationInfo(packageName,
+ PackageManager.MATCH_ARCHIVED_PACKAGES, userId);
} catch (RemoteException e) {
Log.w(TAG, "getEntry couldn't reach PackageManager", e);
return null;
@@ -1612,7 +1615,7 @@ public class ApplicationsState {
}
public static class AppEntry extends SizeInfo {
- public final File apkFile;
+ @Nullable public final File apkFile;
public final long id;
public String label;
public long size;
@@ -1671,7 +1674,7 @@ public class ApplicationsState {
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public AppEntry(Context context, ApplicationInfo info, long id) {
- apkFile = new File(info.sourceDir);
+ this.apkFile = info.sourceDir != null ? new File(info.sourceDir) : null;
this.id = id;
this.info = info;
this.size = SIZE_UNKNOWN;
@@ -1717,13 +1720,13 @@ public class ApplicationsState {
public void ensureLabel(Context context) {
if (this.label == null || !this.mounted) {
- if (!this.apkFile.exists()) {
- this.mounted = false;
- this.label = info.packageName;
- } else {
+ if (this.apkFile != null && this.apkFile.exists()) {
this.mounted = true;
CharSequence label = info.loadLabel(context.getPackageManager());
this.label = label != null ? label.toString() : info.packageName;
+ } else {
+ this.mounted = false;
+ this.label = info.packageName;
}
}
}
@@ -1738,7 +1741,7 @@ public class ApplicationsState {
}
if (this.icon == null) {
- if (this.apkFile.exists()) {
+ if (this.apkFile != null && this.apkFile.exists()) {
this.icon = Utils.getBadgedIcon(context, info);
return true;
} else {
@@ -1748,7 +1751,7 @@ public class ApplicationsState {
} else if (!this.mounted) {
// If the app wasn't mounted but is now mounted, reload
// its icon.
- if (this.apkFile.exists()) {
+ if (this.apkFile != null && this.apkFile.exists()) {
this.mounted = true;
this.icon = Utils.getBadgedIcon(context, info);
return true;
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index ca24d94070f9..cdd074d872c0 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -173,8 +173,10 @@ open class ClockRegistry(
{ "Skipping initial load of known clock package package: $str1" }
)
+ var isCurrentClock = false
var isClockListChanged = false
for (metadata in knownClocks) {
+ isCurrentClock = isCurrentClock || currentClockId == metadata.clockId
val id = metadata.clockId
val info =
availableClocks.concurrentGetOrPut(id, ClockInfo(metadata, null, manager)) {
@@ -207,8 +209,9 @@ open class ClockRegistry(
}
verifyLoadedProviders()
- // Load executed via verifyLoadedProviders
- return false
+ // Load immediately if it's the current clock, otherwise let verifyLoadedProviders
+ // load and unload clocks as necessary on the background thread.
+ return isCurrentClock
}
override fun onPluginLoaded(
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7db21b2be04d..b9478018d7d3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -125,6 +125,25 @@
<dimen name="navigation_edge_cancelled_arrow_height">0dp</dimen>
<dimen name="navigation_edge_cancelled_edge_corners">6dp</dimen>
+ <!--
+ NOTICE: STATUS BAR INTERNALS. DO NOT READ THESE OUTSIDE OF STATUS BAR.
+
+ Below are the bottom margin values for each rotation [1].
+ Only used when the value is >= 0.
+ A value of 0 means that the content has 0 bottom margin, and will be at the bottom of the
+ status bar.
+ When the value is < 0, the value is ignored, and content will be centered vertically.
+
+ [1] Rotation defined as in android.view.Surface.Rotation.
+ Rotation 0 means natural orientation. If a device is naturally portrait (e.g. a phone),
+ rotation 0 is portrait. If a device is naturally landscape (e.g a tablet), rotation 0 is
+ landscape.
+ -->
+ <dimen name="status_bar_bottom_aligned_margin_rotation_0">-1px</dimen>
+ <dimen name="status_bar_bottom_aligned_margin_rotation_90">-1px</dimen>
+ <dimen name="status_bar_bottom_aligned_margin_rotation_180">-1px</dimen>
+ <dimen name="status_bar_bottom_aligned_margin_rotation_270">-1px</dimen>
+
<!-- Height of the system icons container view in the status bar -->
<dimen name="status_bar_system_icons_height">@dimen/status_bar_icon_size_sp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index 1edb551eb944..3cb6314639e8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -34,8 +34,8 @@ import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.view.accessibility.IWindowMagnificationConnection;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -55,7 +55,7 @@ import javax.inject.Inject;
/**
* Class to handle the interaction with
* {@link com.android.server.accessibility.AccessibilityManagerService}. It invokes
- * {@link AccessibilityManager#setWindowMagnificationConnection(IWindowMagnificationConnection)}
+ * {@link AccessibilityManager#setMagnificationConnection(IMagnificationConnection)}
* when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called.
*/
@SysUISingleton
@@ -484,11 +484,11 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks {
}
@Override
- public void requestWindowMagnificationConnection(boolean connect) {
+ public void requestMagnificationConnection(boolean connect) {
if (connect) {
- setWindowMagnificationConnection();
+ setMagnificationConnection();
} else {
- clearWindowMagnificationConnection();
+ clearMagnificationConnection();
}
}
@@ -499,17 +499,17 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks {
magnificationController -> magnificationController.dump(pw));
}
- private void setWindowMagnificationConnection() {
+ private void setMagnificationConnection() {
if (mMagnificationConnectionImpl == null) {
mMagnificationConnectionImpl = new MagnificationConnectionImpl(this,
mHandler);
}
- mAccessibilityManager.setWindowMagnificationConnection(
+ mAccessibilityManager.setMagnificationConnection(
mMagnificationConnectionImpl);
}
- private void clearWindowMagnificationConnection() {
- mAccessibilityManager.setWindowMagnificationConnection(null);
+ private void clearMagnificationConnection() {
+ mAccessibilityManager.setMagnificationConnection(null);
//TODO: destroy controllers.
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
index 5f0d496dd5d1..4944531989d3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
@@ -21,8 +21,8 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
+import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import com.android.systemui.dagger.qualifiers.Main;
@@ -30,9 +30,9 @@ import com.android.systemui.dagger.qualifiers.Main;
/**
* Implementation of window magnification connection.
*
- * @see IWindowMagnificationConnection
+ * @see IMagnificationConnection
*/
-class MagnificationConnectionImpl extends IWindowMagnificationConnection.Stub {
+class MagnificationConnectionImpl extends IMagnificationConnection.Stub {
private static final String TAG = "WindowMagnificationConnectionImpl";
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt
index 2d2f29564263..91bc0c144773 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt
@@ -42,18 +42,21 @@ import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SystemSettings
import com.android.systemui.util.time.SystemClock
import java.util.concurrent.atomic.AtomicInteger
+import javax.inject.Inject
import kotlin.math.roundToInt
/** The Dialog that contains a seekbar for changing the font size. */
-class FontScalingDialog(
- context: Context,
+class FontScalingDialogDelegate @Inject constructor(
+ private val context: Context,
+ private val systemUIDialogFactory: SystemUIDialog.Factory,
+ private val layoutInflater: LayoutInflater,
private val systemSettings: SystemSettings,
private val secureSettings: SecureSettings,
private val systemClock: SystemClock,
private val userTracker: UserTracker,
@Main mainHandler: Handler,
- @Background private val backgroundDelayableExecutor: DelayableExecutor
-) : SystemUIDialog(context) {
+ @Background private val backgroundDelayableExecutor: DelayableExecutor,
+) : SystemUIDialog.Delegate {
private val MIN_UPDATE_INTERVAL_MS: Long = 800
private val CHANGE_BY_SEEKBAR_DELAY_MS: Long = 100
private val CHANGE_BY_BUTTON_DELAY_MS: Long = 300
@@ -75,19 +78,22 @@ class FontScalingDialog(
}
}
- override fun onCreate(savedInstanceState: Bundle?) {
- setTitle(R.string.font_scaling_dialog_title)
- setView(LayoutInflater.from(context).inflate(R.layout.font_scaling_dialog, null))
- setPositiveButton(
- R.string.quick_settings_done,
- /* onClick = */ null,
- /* dismissOnClick = */ true
+ override fun createDialog(): SystemUIDialog = systemUIDialogFactory.create(this)
+
+ override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ dialog.setTitle(R.string.font_scaling_dialog_title)
+ dialog.setView(layoutInflater.inflate(R.layout.font_scaling_dialog, null))
+ dialog.setPositiveButton(
+ R.string.quick_settings_done,
+ /* onClick = */ null,
+ /* dismissOnClick = */ true
)
- super.onCreate(savedInstanceState)
+ }
- title = requireViewById(com.android.internal.R.id.alertTitle)
- doneButton = requireViewById(com.android.internal.R.id.button1)
- seekBarWithIconButtonsView = requireViewById(R.id.font_scaling_slider)
+ override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ title = dialog.requireViewById(com.android.internal.R.id.alertTitle)
+ doneButton = dialog.requireViewById(com.android.internal.R.id.button1)
+ seekBarWithIconButtonsView = dialog.requireViewById(R.id.font_scaling_slider)
val labelArray = arrayOfNulls<String>(strEntryValues.size)
for (i in strEntryValues.indices) {
@@ -135,7 +141,7 @@ class FontScalingDialog(
}
}
)
- doneButton.setOnClickListener { dismiss() }
+ doneButton.setOnClickListener { dialog.dismiss() }
systemSettings.registerContentObserver(Settings.System.FONT_SCALE, fontSizeObserver)
}
@@ -156,7 +162,7 @@ class FontScalingDialog(
backgroundDelayableExecutor.executeDelayed({ updateFontScale() }, delayMs)
}
- override fun stop() {
+ override fun onStop(dialog: SystemUIDialog) {
cancelUpdateFontScaleRunnable?.run()
cancelUpdateFontScaleRunnable = null
systemSettings.unregisterContentObserver(fontSizeObserver)
@@ -189,9 +195,7 @@ class FontScalingDialog(
return strEntryValues.size - 1
}
- override fun onConfigurationChanged(configuration: Configuration) {
- super.onConfigurationChanged(configuration)
-
+ override fun onConfigurationChanged(dialog: SystemUIDialog, configuration: Configuration) {
val configDiff = configuration.diff(this.configuration)
this.configuration.setTo(configuration)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
index 64e3f16abec3..14d365839417 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
@@ -23,7 +23,7 @@ import android.view.View
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
import com.android.systemui.res.R
-import com.android.systemui.accessibility.fontscaling.FontScalingDialog
+import com.android.systemui.accessibility.fontscaling.FontScalingDialogDelegate
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.qualifiers.Background
@@ -36,14 +36,10 @@ import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.settings.SystemSettings
-import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
+import javax.inject.Provider
class FontScalingTile
@Inject
@@ -59,11 +55,7 @@ constructor(
qsLogger: QSLogger,
private val keyguardStateController: KeyguardStateController,
private val dialogLaunchAnimator: DialogLaunchAnimator,
- private val systemSettings: SystemSettings,
- private val secureSettings: SecureSettings,
- private val systemClock: SystemClock,
- private val userTracker: UserTracker,
- @Background private val backgroundDelayableExecutor: DelayableExecutor
+ private val fontScalingDialogDelegateProvider: Provider<FontScalingDialogDelegate>
) :
QSTileImpl<QSTile.State?>(
host,
@@ -87,16 +79,7 @@ constructor(
val animateFromView: Boolean = view != null && !keyguardStateController.isShowing
val runnable = Runnable {
- val dialog: SystemUIDialog =
- FontScalingDialog(
- mContext,
- systemSettings,
- secureSettings,
- systemClock,
- userTracker,
- mainHandler,
- backgroundDelayableExecutor
- )
+ val dialog: SystemUIDialog = fontScalingDialogDelegateProvider.get().createDialog()
if (animateFromView) {
dialogLaunchAnimator.showFromView(
dialog,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index cb95b25ece80..2460a3314be5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -23,11 +23,11 @@ import android.app.PendingIntent
import android.app.StatusBarManager
import android.content.Intent
import android.content.res.Configuration
+import android.graphics.Insets
import android.os.Bundle
import android.os.Trace
import android.os.Trace.TRACE_TAG_APP
import android.provider.AlarmClock
-import android.util.Pair
import android.view.DisplayCutout
import android.view.View
import android.view.WindowInsets
@@ -402,9 +402,9 @@ constructor(
private fun updateConstraintsForInsets(view: MotionLayout, insets: WindowInsets) {
val cutout = insets.displayCutout.also { this.cutout = it }
- val sbInsets: Pair<Int, Int> = insetsProvider.getStatusBarContentInsetsForCurrentRotation()
- val cutoutLeft = sbInsets.first
- val cutoutRight = sbInsets.second
+ val sbInsets: Insets = insetsProvider.getStatusBarContentInsetsForCurrentRotation()
+ val cutoutLeft = sbInsets.left
+ val cutoutRight = sbInsets.right
val hasCornerCutout: Boolean = insetsProvider.currentRotationHasCornerCutout()
updateQQSPaddings()
// Set these guides as the left/right limits for content that lives in the top row, using
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index d88fab0deaa3..ada7d3ea1698 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -153,7 +153,7 @@ public class CommandQueue extends IStatusBar.Stub implements
private static final int MSG_HIDE_TOAST = 53 << MSG_SHIFT;
private static final int MSG_TRACING_STATE_CHANGED = 54 << MSG_SHIFT;
private static final int MSG_SUPPRESS_AMBIENT_DISPLAY = 55 << MSG_SHIFT;
- private static final int MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION = 56 << MSG_SHIFT;
+ private static final int MSG_REQUEST_MAGNIFICATION_CONNECTION = 56 << MSG_SHIFT;
//TODO(b/169175022) Update name and when feature name is locked.
private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE = 58 << MSG_SHIFT;
private static final int MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED = 59 << MSG_SHIFT;
@@ -426,11 +426,11 @@ public class CommandQueue extends IStatusBar.Stub implements
/**
* Requests {@link com.android.systemui.accessibility.Magnification} to invoke
* {@code android.view.accessibility.AccessibilityManager#
- * setWindowMagnificationConnection(IWindowMagnificationConnection)}
+ * setMagnificationConnection(IMagnificationConnection)}
*
* @param connect {@code true} if needs connection, otherwise set the connection to null.
*/
- default void requestWindowMagnificationConnection(boolean connect) { }
+ default void requestMagnificationConnection(boolean connect) { }
/**
* @see IStatusBar#setNavigationBarLumaSamplingEnabled(int, boolean)
@@ -1125,9 +1125,9 @@ public class CommandQueue extends IStatusBar.Stub implements
}
@Override
- public void requestWindowMagnificationConnection(boolean connect) {
+ public void requestMagnificationConnection(boolean connect) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION, connect)
+ mHandler.obtainMessage(MSG_REQUEST_MAGNIFICATION_CONNECTION, connect)
.sendToTarget();
}
}
@@ -1767,9 +1767,9 @@ public class CommandQueue extends IStatusBar.Stub implements
callbacks.suppressAmbientDisplay((boolean) msg.obj);
}
break;
- case MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION:
+ case MSG_REQUEST_MAGNIFICATION_CONNECTION:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).requestWindowMagnificationConnection((Boolean) msg.obj);
+ mCallbacks.get(i).requestMagnificationConnection((Boolean) msg.obj);
}
break;
case MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index a36d36c3827b..618dec22b32c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -278,6 +278,7 @@ open class PrivacyDotViewController @Inject constructor(
var contentInsets = state.contentRectForRotation(rot)
tl.setPadding(0, state.paddingTop, 0, 0)
(tl.layoutParams as FrameLayout.LayoutParams).apply {
+ topMargin = contentInsets.top
height = contentInsets.height()
if (rtl) {
width = contentInsets.left
@@ -290,6 +291,7 @@ open class PrivacyDotViewController @Inject constructor(
contentInsets = state.contentRectForRotation(rot)
tr.setPadding(0, state.paddingTop, 0, 0)
(tr.layoutParams as FrameLayout.LayoutParams).apply {
+ topMargin = contentInsets.top
height = contentInsets.height()
if (rtl) {
width = contentInsets.left
@@ -302,6 +304,7 @@ open class PrivacyDotViewController @Inject constructor(
contentInsets = state.contentRectForRotation(rot)
br.setPadding(0, state.paddingTop, 0, 0)
(br.layoutParams as FrameLayout.LayoutParams).apply {
+ topMargin = contentInsets.top
height = contentInsets.height()
if (rtl) {
width = contentInsets.left
@@ -314,6 +317,7 @@ open class PrivacyDotViewController @Inject constructor(
contentInsets = state.contentRectForRotation(rot)
bl.setPadding(0, state.paddingTop, 0, 0)
(bl.layoutParams as FrameLayout.LayoutParams).apply {
+ topMargin = contentInsets.top
height = contentInsets.height()
if (rtl) {
width = contentInsets.left
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index fec176523b4c..118f5f0515be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -87,8 +87,8 @@ class SystemEventChipAnimationController @Inject constructor(
animationWindowView.addView(
it.view,
layoutParamsDefault(
- if (animationWindowView.isLayoutRtl) insets.first
- else insets.second))
+ if (animationWindowView.isLayoutRtl) insets.left
+ else insets.right))
it.view.alpha = 0f
// For some reason, the window view's measured width is always 0 here, so use the
// parent (status bar)
@@ -289,7 +289,7 @@ class SystemEventChipAnimationController @Inject constructor(
*/
private fun updateChipBounds(chip: BackgroundAnimatableView, contentArea: Rect) {
// decide which direction we're animating from, and then set some screen coordinates
- val chipTop = (contentArea.bottom - chip.view.measuredHeight) / 2
+ val chipTop = contentArea.top + (contentArea.height() - chip.view.measuredHeight) / 2
val chipBottom = chipTop + chip.view.measuredHeight
val chipRight: Int
val chipLeft: Int
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 8a64a509a0e9..145dbff81144 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -24,11 +24,11 @@ import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Trace;
import android.util.AttributeSet;
-import android.util.Pair;
import android.util.TypedValue;
import android.view.DisplayCutout;
import android.view.Gravity;
@@ -103,7 +103,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
private DisplayCutout mDisplayCutout;
private int mRoundedCornerPadding = 0;
// right and left padding applied to this view to account for cutouts and rounded corners
- private Pair<Integer, Integer> mPadding = new Pair(0, 0);
+ private Insets mPadding = Insets.of(0, 0, 0, 0);
/**
* The clipping on the top
@@ -184,7 +184,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
int marginStart = calculateMargin(
getResources().getDimensionPixelSize(R.dimen.keyguard_carrier_text_margin),
- mPadding.first);
+ mPadding.left);
lp.setMarginStart(marginStart);
mCarrierLabel.setLayoutParams(lp);
@@ -303,9 +303,9 @@ public class KeyguardStatusBarView extends RelativeLayout {
// consider privacy dot space
final int minLeft = (isLayoutRtl() && mIsPrivacyDotEnabled)
- ? Math.max(mMinDotWidth, mPadding.first) : mPadding.first;
+ ? Math.max(mMinDotWidth, mPadding.left) : mPadding.left;
final int minRight = (!isLayoutRtl() && mIsPrivacyDotEnabled)
- ? Math.max(mMinDotWidth, mPadding.second) : mPadding.second;
+ ? Math.max(mMinDotWidth, mPadding.right) : mPadding.right;
setPadding(minLeft, waterfallTop, minRight, 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index a27e67b965a5..cb7bc256504e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -20,10 +20,10 @@ package com.android.systemui.statusbar.phone;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.Pair;
import android.view.DisplayCutout;
import android.view.MotionEvent;
import android.view.View;
@@ -271,13 +271,12 @@ public class PhoneStatusBarView extends FrameLayout {
}
private void updateSafeInsets() {
- Pair<Integer, Integer> insets = mContentInsetsProvider
+ Insets insets = mContentInsetsProvider
.getStatusBarContentInsetsForCurrentRotation();
-
setPadding(
- insets.first,
- getPaddingTop(),
- insets.second,
+ insets.left,
+ insets.top,
+ insets.right,
getPaddingBottom());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index cba72d08840f..3b96f5793fe8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -16,13 +16,16 @@
package com.android.systemui.statusbar.phone
+import android.annotation.Px
import android.content.Context
import android.content.res.Resources
+import android.graphics.Insets
import android.graphics.Point
import android.graphics.Rect
import android.util.LruCache
import android.util.Pair
import android.view.DisplayCutout
+import android.view.Surface
import androidx.annotation.VisibleForTesting
import com.android.internal.policy.SystemBarUtils
import com.android.systemui.Dumpable
@@ -154,13 +157,13 @@ class StatusBarContentInsetsProvider @Inject constructor(
}
/**
- * Calculate the distance from the left and right edges of the screen to the status bar
+ * Calculate the distance from the left, right and top edges of the screen to the status bar
* content area. This differs from the content area rects in that these values can be used
* directly as padding.
*
* @param rotation the target rotation for which to calculate insets
*/
- fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Pair<Int, Int> =
+ fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets =
traceSection(tag = "StatusBarContentInsetsProvider.getStatusBarContentInsetsForRotation") {
val displayCutout = checkNotNull(context.display).cutout
val key = getCacheKey(rotation, displayCutout)
@@ -175,15 +178,14 @@ class StatusBarContentInsetsProvider @Inject constructor(
val area = insetsCache[key] ?: getAndSetCalculatedAreaForRotation(
rotation, displayCutout, getResourcesForRotation(rotation, context), key)
- Pair(area.left, width - area.right)
+ Insets.of(area.left, area.top, /* right= */ width - area.right, /* bottom= */ 0)
}
/**
- * Calculate the left and right insets for the status bar content in the device's current
- * rotation
+ * Calculate the insets for the status bar content in the device's current rotation
* @see getStatusBarContentAreaForRotation
*/
- fun getStatusBarContentInsetsForCurrentRotation(): Pair<Int, Int> {
+ fun getStatusBarContentInsetsForCurrentRotation(): Insets {
return getStatusBarContentInsetsForRotation(getExactRotation(context))
}
@@ -251,6 +253,10 @@ class StatusBarContentInsetsProvider @Inject constructor(
minRight = max(minDotPadding, roundedCornerPadding)
}
+ val bottomAlignedMargin = getBottomAlignedMargin(targetRotation, rotatedResources)
+ val statusBarContentHeight =
+ rotatedResources.getDimensionPixelSize(R.dimen.status_bar_icon_size_sp)
+
return calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
@@ -260,7 +266,22 @@ class StatusBarContentInsetsProvider @Inject constructor(
minLeft,
minRight,
configurationController.isLayoutRtl,
- dotWidth)
+ dotWidth,
+ bottomAlignedMargin,
+ statusBarContentHeight)
+ }
+
+ @Px
+ private fun getBottomAlignedMargin(targetRotation: Int, resources: Resources): Int {
+ val dimenRes =
+ when (targetRotation) {
+ Surface.ROTATION_0 -> R.dimen.status_bar_bottom_aligned_margin_rotation_0
+ Surface.ROTATION_90 -> R.dimen.status_bar_bottom_aligned_margin_rotation_90
+ Surface.ROTATION_180 -> R.dimen.status_bar_bottom_aligned_margin_rotation_180
+ Surface.ROTATION_270 -> R.dimen.status_bar_bottom_aligned_margin_rotation_270
+ else -> throw IllegalStateException("Unknown rotation: $targetRotation")
+ }
+ return resources.getDimensionPixelSize(dimenRes)
}
fun getStatusBarPaddingTop(@Rotation rotation: Int? = null): Int {
@@ -329,8 +350,7 @@ fun getPrivacyChipBoundingRectForInsets(
}
/**
- * Calculates the exact left and right positions for the status bar contents for the given
- * rotation
+ * Calculates the exact left and right positions for the status bar contents for the given rotation
*
* @param currentRotation current device rotation
* @param targetRotation rotation for which to calculate the status bar content rect
@@ -341,9 +361,12 @@ fun getPrivacyChipBoundingRectForInsets(
* @param minRight the minimum padding to enforce on the right
* @param isRtl current layout direction is Right-To-Left or not
* @param dotWidth privacy dot image width (0 if privacy dot is disabled)
- *
+ * @param bottomAlignedMargin the bottom margin that the status bar content should have. -1 if none,
+ * and content should be centered vertically.
+ * @param statusBarContentHeight the height of the status bar contents (icons, text, etc)
* @see [RotationUtils#getResourcesForRotation]
*/
+@VisibleForTesting
fun calculateInsetsForRotationWithRotatedResources(
@Rotation currentRotation: Int,
@Rotation targetRotation: Int,
@@ -353,7 +376,9 @@ fun calculateInsetsForRotationWithRotatedResources(
minLeft: Int,
minRight: Int,
isRtl: Boolean,
- dotWidth: Int
+ dotWidth: Int,
+ bottomAlignedMargin: Int,
+ statusBarContentHeight: Int
): Rect {
/*
TODO: Check if this is ever used for devices with no rounded corners
@@ -363,7 +388,7 @@ fun calculateInsetsForRotationWithRotatedResources(
val rotZeroBounds = getRotationZeroDisplayBounds(maxBounds, currentRotation)
- val sbLeftRight = getStatusBarLeftRight(
+ return getStatusBarContentBounds(
displayCutout,
statusBarHeight,
rotZeroBounds.right,
@@ -375,9 +400,9 @@ fun calculateInsetsForRotationWithRotatedResources(
isRtl,
dotWidth,
targetRotation,
- currentRotation)
-
- return sbLeftRight
+ currentRotation,
+ bottomAlignedMargin,
+ statusBarContentHeight)
}
/**
@@ -399,26 +424,30 @@ fun calculateInsetsForRotationWithRotatedResources(
* @return a Rect which exactly calculates the Status Bar's content rect relative to the target
* rotation
*/
-private fun getStatusBarLeftRight(
- displayCutout: DisplayCutout?,
- sbHeight: Int,
- width: Int,
- height: Int,
- cWidth: Int,
- cHeight: Int,
- minLeft: Int,
- minRight: Int,
- isRtl: Boolean,
- dotWidth: Int,
- @Rotation targetRotation: Int,
- @Rotation currentRotation: Int
+private fun getStatusBarContentBounds(
+ displayCutout: DisplayCutout?,
+ sbHeight: Int,
+ width: Int,
+ height: Int,
+ cWidth: Int,
+ cHeight: Int,
+ minLeft: Int,
+ minRight: Int,
+ isRtl: Boolean,
+ dotWidth: Int,
+ @Rotation targetRotation: Int,
+ @Rotation currentRotation: Int,
+ bottomAlignedMargin: Int,
+ statusBarContentHeight: Int
): Rect {
+ val insetTop = getInsetTop(bottomAlignedMargin, statusBarContentHeight, sbHeight)
+
val logicalDisplayWidth = if (targetRotation.isHorizontal()) height else width
val cutoutRects = displayCutout?.boundingRects
if (cutoutRects == null || cutoutRects.isEmpty()) {
return Rect(minLeft,
- 0,
+ insetTop,
logicalDisplayWidth - minRight,
sbHeight)
}
@@ -455,7 +484,48 @@ private fun getStatusBarLeftRight(
// is very close to but not directly touch edges.
}
- return Rect(leftMargin, 0, logicalDisplayWidth - rightMargin, sbHeight)
+ return Rect(leftMargin, insetTop, logicalDisplayWidth - rightMargin, sbHeight)
+}
+
+/*
+ * Returns the inset top of the status bar.
+ *
+ * Only greater than 0, when we want the content to be bottom aligned.
+ *
+ * Common case when we want content to be vertically centered within the status bar.
+ * Example dimensions:
+ * - Status bar height: 50dp
+ * - Content height: 20dp
+ * _______________________________________________
+ * | |
+ * | |
+ * | 09:00 5G [] 74% | 20dp Content CENTER_VERTICAL gravity
+ * | |
+ * |_____________________________________________|
+ *
+ * Case when we want bottom alignment and a bottom margin of 10dp.
+ * We need to make the status bar height artificially smaller using top padding/inset.
+ * - Status bar height: 50dp
+ * - Content height: 20dp
+ * - Bottom margin: 10dp
+ * ______________________________________________
+ * |_____________________________________________| 10dp top inset/padding
+ * | | 40dp new artificial status bar height
+ * | 09:00 5G [] 74% | 20dp Content CENTER_VERTICAL gravity
+ * |_____________________________________________| 10dp bottom margin
+ */
+@Px
+private fun getInsetTop(
+ bottomAlignedMargin: Int,
+ statusBarContentHeight: Int,
+ statusBarHeight: Int
+): Int {
+ val bottomAlignmentEnabled = bottomAlignedMargin >= 0
+ if (!bottomAlignmentEnabled) {
+ return 0
+ }
+ val newArtificialStatusBarHeight = bottomAlignedMargin * 2 + statusBarContentHeight
+ return statusBarHeight - newArtificialStatusBarHeight
}
private fun sbRect(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 2df30dccb13d..93bc96022292 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -203,6 +203,28 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
SysUiState sysUiState,
BroadcastDispatcher broadcastDispatcher,
DialogLaunchAnimator dialogLaunchAnimator,
+ Delegate delegate) {
+ this(
+ context,
+ theme,
+ dismissOnDeviceLock,
+ featureFlags,
+ dialogManager,
+ sysUiState,
+ broadcastDispatcher,
+ dialogLaunchAnimator,
+ (DialogDelegate<SystemUIDialog>) delegate);
+ }
+
+ public SystemUIDialog(
+ Context context,
+ int theme,
+ boolean dismissOnDeviceLock,
+ FeatureFlags featureFlags,
+ SystemUIDialogManager dialogManager,
+ SysUiState sysUiState,
+ BroadcastDispatcher broadcastDispatcher,
+ DialogLaunchAnimator dialogLaunchAnimator,
DialogDelegate<SystemUIDialog> delegate) {
super(context, theme);
mContext = context;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index 43952824f9a7..235aa218715d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -33,8 +33,8 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import androidx.test.filters.SmallTest;
@@ -53,13 +53,13 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
- * Tests for {@link android.view.accessibility.IWindowMagnificationConnection} retrieved from
+ * Tests for {@link android.view.accessibility.IMagnificationConnection} retrieved from
* {@link Magnification}
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class IWindowMagnificationConnectionTest extends SysuiTestCase {
+public class IMagnificationConnectionTest extends SysuiTestCase {
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
@Mock
@@ -85,7 +85,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
@Mock
private AccessibilityLogger mA11yLogger;
- private IWindowMagnificationConnection mIWindowMagnificationConnection;
+ private IMagnificationConnection mIMagnificationConnection;
private Magnification mMagnification;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
@@ -94,10 +94,10 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
getContext().addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
doAnswer(invocation -> {
- mIWindowMagnificationConnection = invocation.getArgument(0);
+ mIMagnificationConnection = invocation.getArgument(0);
return null;
- }).when(mAccessibilityManager).setWindowMagnificationConnection(
- any(IWindowMagnificationConnection.class));
+ }).when(mAccessibilityManager).setMagnificationConnection(
+ any(IMagnificationConnection.class));
mMagnification = new Magnification(getContext(),
getContext().getMainThreadHandler(), mCommandQueue,
mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
@@ -107,14 +107,14 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
mContext.getSystemService(DisplayManager.class));
- mMagnification.requestWindowMagnificationConnection(true);
- assertNotNull(mIWindowMagnificationConnection);
- mIWindowMagnificationConnection.setConnectionCallback(mConnectionCallback);
+ mMagnification.requestMagnificationConnection(true);
+ assertNotNull(mIMagnificationConnection);
+ mIMagnificationConnection.setConnectionCallback(mConnectionCallback);
}
@Test
public void enableWindowMagnification_passThrough() throws RemoteException {
- mIWindowMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN,
+ mIMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN,
Float.NaN, 0f, 0f, mAnimationCallback);
waitForIdleSync();
@@ -124,7 +124,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
@Test
public void disableWindowMagnification_deleteWindowMagnification() throws RemoteException {
- mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY,
+ mIMagnificationConnection.disableWindowMagnification(TEST_DISPLAY,
mAnimationCallback);
waitForIdleSync();
@@ -134,7 +134,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
@Test
public void setScaleForWindowMagnification() throws RemoteException {
- mIWindowMagnificationConnection.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
+ mIMagnificationConnection.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
waitForIdleSync();
verify(mWindowMagnificationController).setScale(3.0f);
@@ -142,7 +142,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
@Test
public void moveWindowMagnifier() throws RemoteException {
- mIWindowMagnificationConnection.moveWindowMagnifier(TEST_DISPLAY, 100f, 200f);
+ mIMagnificationConnection.moveWindowMagnifier(TEST_DISPLAY, 100f, 200f);
waitForIdleSync();
verify(mWindowMagnificationController).moveWindowMagnifier(100f, 200f);
@@ -150,7 +150,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
@Test
public void moveWindowMagnifierToPosition() throws RemoteException {
- mIWindowMagnificationConnection.moveWindowMagnifierToPosition(TEST_DISPLAY,
+ mIMagnificationConnection.moveWindowMagnifierToPosition(TEST_DISPLAY,
100f, 200f, mAnimationCallback);
waitForIdleSync();
@@ -163,7 +163,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
// magnification settings panel should not be showing
assertFalse(mMagnification.isMagnificationSettingsPanelShowing(TEST_DISPLAY));
- mIWindowMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
+ mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
waitForIdleSync();
@@ -173,7 +173,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
@Test
public void removeMagnificationButton() throws RemoteException {
- mIWindowMagnificationConnection.removeMagnificationButton(TEST_DISPLAY);
+ mIMagnificationConnection.removeMagnificationButton(TEST_DISPLAY);
waitForIdleSync();
verify(mModeSwitchesController).removeButton(TEST_DISPLAY);
@@ -181,7 +181,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
@Test
public void removeMagnificationSettingsPanel() throws RemoteException {
- mIWindowMagnificationConnection.removeMagnificationSettingsPanel(TEST_DISPLAY);
+ mIMagnificationConnection.removeMagnificationSettingsPanel(TEST_DISPLAY);
waitForIdleSync();
verify(mMagnificationSettingsController).closeMagnificationSettings();
@@ -191,7 +191,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
public void onUserMagnificationScaleChanged() throws RemoteException {
final int testUserId = 1;
final float testScale = 3.0f;
- mIWindowMagnificationConnection.onUserMagnificationScaleChanged(
+ mIMagnificationConnection.onUserMagnificationScaleChanged(
testUserId, TEST_DISPLAY, testScale);
waitForIdleSync();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index c972febf2c7e..39c8f5d724b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -43,7 +43,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import androidx.test.filters.SmallTest;
@@ -98,11 +98,11 @@ public class MagnificationTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
getContext().addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
doAnswer(invocation -> {
- IWindowMagnificationConnection connection = invocation.getArgument(0);
+ IMagnificationConnection connection = invocation.getArgument(0);
connection.setConnectionCallback(mConnectionCallback);
return null;
- }).when(mAccessibilityManager).setWindowMagnificationConnection(
- any(IWindowMagnificationConnection.class));
+ }).when(mAccessibilityManager).setMagnificationConnection(
+ any(IMagnificationConnection.class));
when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
@@ -138,22 +138,22 @@ public class MagnificationTest extends SysuiTestCase {
@Test
public void requestWindowMagnificationConnection_setConnectionAndListener() {
- mCommandQueue.requestWindowMagnificationConnection(true);
+ mCommandQueue.requestMagnificationConnection(true);
waitForIdleSync();
- verify(mAccessibilityManager).setWindowMagnificationConnection(any(
- IWindowMagnificationConnection.class));
+ verify(mAccessibilityManager).setMagnificationConnection(any(
+ IMagnificationConnection.class));
- mCommandQueue.requestWindowMagnificationConnection(false);
+ mCommandQueue.requestMagnificationConnection(false);
waitForIdleSync();
- verify(mAccessibilityManager).setWindowMagnificationConnection(isNull());
+ verify(mAccessibilityManager).setMagnificationConnection(isNull());
}
@Test
public void onWindowMagnifierBoundsChanged() throws RemoteException {
final Rect testBounds = new Rect(0, 0, 500, 600);
- mCommandQueue.requestWindowMagnificationConnection(true);
+ mCommandQueue.requestMagnificationConnection(true);
waitForIdleSync();
mMagnification.mWindowMagnifierCallback
@@ -166,7 +166,7 @@ public class MagnificationTest extends SysuiTestCase {
public void onPerformScaleAction_enabled_notifyCallback() throws RemoteException {
final float newScale = 4.0f;
final boolean updatePersistence = true;
- mCommandQueue.requestWindowMagnificationConnection(true);
+ mCommandQueue.requestMagnificationConnection(true);
waitForIdleSync();
mMagnification.mWindowMagnifierCallback
@@ -178,7 +178,7 @@ public class MagnificationTest extends SysuiTestCase {
@Test
public void onAccessibilityActionPerformed_enabled_notifyCallback() throws RemoteException {
- mCommandQueue.requestWindowMagnificationConnection(true);
+ mCommandQueue.requestMagnificationConnection(true);
waitForIdleSync();
mMagnification.mWindowMagnifierCallback
@@ -189,7 +189,7 @@ public class MagnificationTest extends SysuiTestCase {
@Test
public void onMove_enabled_notifyCallback() throws RemoteException {
- mCommandQueue.requestWindowMagnificationConnection(true);
+ mCommandQueue.requestMagnificationConnection(true);
waitForIdleSync();
mMagnification.mWindowMagnifierCallback.onMove(TEST_DISPLAY);
@@ -254,7 +254,7 @@ public class MagnificationTest extends SysuiTestCase {
@Test
public void onMagnifierScale_notifyCallback() throws RemoteException {
- mCommandQueue.requestWindowMagnificationConnection(true);
+ mCommandQueue.requestMagnificationConnection(true);
waitForIdleSync();
final float scale = 3.0f;
final boolean updatePersistence = false;
@@ -271,7 +271,7 @@ public class MagnificationTest extends SysuiTestCase {
public void onModeSwitch_windowEnabledAndSwitchToFullscreen_hidePanelAndNotifyCallback()
throws RemoteException {
when(mWindowMagnificationController.isActivated()).thenReturn(true);
- mCommandQueue.requestWindowMagnificationConnection(true);
+ mCommandQueue.requestMagnificationConnection(true);
waitForIdleSync();
mMagnification.mMagnificationSettingsControllerCallback.onModeSwitch(
@@ -289,7 +289,7 @@ public class MagnificationTest extends SysuiTestCase {
public void onModeSwitch_switchToSameMode_doNothing()
throws RemoteException {
when(mWindowMagnificationController.isActivated()).thenReturn(true);
- mCommandQueue.requestWindowMagnificationConnection(true);
+ mCommandQueue.requestMagnificationConnection(true);
waitForIdleSync();
mMagnification.mMagnificationSettingsControllerCallback.onModeSwitch(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
index 83bee932fc59..bfb5485e47b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
@@ -20,18 +20,24 @@ import android.os.Handler
import android.provider.Settings
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Button
import android.widget.SeekBar
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.model.SysUiState
import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SystemSettings
@@ -40,25 +46,25 @@ import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
private const val ON: Int = 1
private const val OFF: Int = 0
-/** Tests for [FontScalingDialog]. */
+/** Tests for [FontScalingDialogDelegate]. */
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class FontScalingDialogTest : SysuiTestCase() {
- private val MIN_UPDATE_INTERVAL_MS: Long = 800
- private val CHANGE_BY_SEEKBAR_DELAY_MS: Long = 100
- private val CHANGE_BY_BUTTON_DELAY_MS: Long = 300
- private lateinit var fontScalingDialog: FontScalingDialog
+class FontScalingDialogDelegateTest : SysuiTestCase() {
+ private lateinit var fontScalingDialogDelegate: FontScalingDialogDelegate
+ private lateinit var dialog: SystemUIDialog
private lateinit var systemSettings: SystemSettings
private lateinit var secureSettings: SecureSettings
private lateinit var systemClock: FakeSystemClock
@@ -69,9 +75,12 @@ class FontScalingDialogTest : SysuiTestCase() {
.getResources()
.getStringArray(com.android.settingslib.R.array.entryvalues_font_size)
+ @Mock private lateinit var dialogManager: SystemUIDialogManager
+ @Mock private lateinit var dialogFactory: SystemUIDialog.Factory
@Mock private lateinit var userTracker: UserTracker
- @Captor
- private lateinit var seekBarChangeCaptor: ArgumentCaptor<OnSeekBarWithIconButtonsChangeListener>
+ private val featureFlags = FakeFeatureFlags()
+ @Mock private lateinit var sysuiState: SysUiState
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
@Before
fun setUp() {
@@ -79,28 +88,46 @@ class FontScalingDialogTest : SysuiTestCase() {
testableLooper = TestableLooper.get(this)
val mainHandler = Handler(testableLooper.looper)
systemSettings = FakeSettings()
+ featureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM, true)
// Guarantee that the systemSettings always starts with the default font scale.
systemSettings.putFloatForUser(Settings.System.FONT_SCALE, 1.0f, userTracker.userId)
secureSettings = FakeSettings()
systemClock = FakeSystemClock()
backgroundDelayableExecutor = FakeExecutor(systemClock)
- fontScalingDialog =
- FontScalingDialog(
+ whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
+
+ fontScalingDialogDelegate = spy(FontScalingDialogDelegate(
mContext,
+ dialogFactory,
+ LayoutInflater.from(mContext),
systemSettings,
secureSettings,
systemClock,
userTracker,
mainHandler,
backgroundDelayableExecutor
- )
+ ))
+
+ dialog = SystemUIDialog(
+ mContext,
+ 0,
+ DEFAULT_DISMISS_ON_DEVICE_LOCK,
+ featureFlags,
+ dialogManager,
+ sysuiState,
+ fakeBroadcastDispatcher,
+ dialogLaunchAnimator,
+ fontScalingDialogDelegate
+ )
+
+ whenever(dialogFactory.create(any())).thenReturn(dialog)
}
@Test
fun showTheDialog_seekbarIsShowingCorrectProgress() {
- fontScalingDialog.show()
+ dialog.show()
- val seekBar: SeekBar = fontScalingDialog.findViewById<SeekBar>(R.id.seekbar)!!
+ val seekBar: SeekBar = dialog.findViewById<SeekBar>(R.id.seekbar)!!
val progress: Int = seekBar.getProgress()
val currentScale =
systemSettings.getFloatForUser(
@@ -111,17 +138,17 @@ class FontScalingDialogTest : SysuiTestCase() {
assertThat(currentScale).isEqualTo(fontSizeValueArray[progress].toFloat())
- fontScalingDialog.dismiss()
+ dialog.dismiss()
}
@Test
fun progressIsZero_clickIconEnd_seekBarProgressIncreaseOne_fontSizeScaled() {
- fontScalingDialog.show()
+ dialog.show()
- val iconEndFrame: ViewGroup = fontScalingDialog.findViewById(R.id.icon_end_frame)!!
+ val iconEndFrame: ViewGroup = dialog.findViewById(R.id.icon_end_frame)!!
val seekBarWithIconButtonsView: SeekBarWithIconButtonsView =
- fontScalingDialog.findViewById(R.id.font_scaling_slider)!!
- val seekBar: SeekBar = fontScalingDialog.findViewById(R.id.seekbar)!!
+ dialog.findViewById(R.id.font_scaling_slider)!!
+ val seekBar: SeekBar = dialog.findViewById(R.id.seekbar)!!
seekBarWithIconButtonsView.setProgress(0)
backgroundDelayableExecutor.runAllReady()
@@ -142,17 +169,17 @@ class FontScalingDialogTest : SysuiTestCase() {
assertThat(seekBar.getProgress()).isEqualTo(1)
assertThat(currentScale).isEqualTo(fontSizeValueArray[1].toFloat())
- fontScalingDialog.dismiss()
+ dialog.dismiss()
}
@Test
fun progressIsMax_clickIconStart_seekBarProgressDecreaseOne_fontSizeScaled() {
- fontScalingDialog.show()
+ dialog.show()
- val iconStartFrame: ViewGroup = fontScalingDialog.findViewById(R.id.icon_start_frame)!!
+ val iconStartFrame: ViewGroup = dialog.findViewById(R.id.icon_start_frame)!!
val seekBarWithIconButtonsView: SeekBarWithIconButtonsView =
- fontScalingDialog.findViewById(R.id.font_scaling_slider)!!
- val seekBar: SeekBar = fontScalingDialog.findViewById(R.id.seekbar)!!
+ dialog.findViewById(R.id.font_scaling_slider)!!
+ val seekBar: SeekBar = dialog.findViewById(R.id.seekbar)!!
seekBarWithIconButtonsView.setProgress(fontSizeValueArray.size - 1)
backgroundDelayableExecutor.runAllReady()
@@ -174,14 +201,14 @@ class FontScalingDialogTest : SysuiTestCase() {
assertThat(currentScale)
.isEqualTo(fontSizeValueArray[fontSizeValueArray.size - 2].toFloat())
- fontScalingDialog.dismiss()
+ dialog.dismiss()
}
@Test
fun progressChanged_keyWasNotSetBefore_fontScalingHasBeenChangedIsOn() {
- fontScalingDialog.show()
+ dialog.show()
- val iconStartFrame: ViewGroup = fontScalingDialog.findViewById(R.id.icon_start_frame)!!
+ val iconStartFrame: ViewGroup = dialog.findViewById(R.id.icon_start_frame)!!
secureSettings.putIntForUser(
Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
OFF,
@@ -202,24 +229,21 @@ class FontScalingDialogTest : SysuiTestCase() {
)
assertThat(currentSettings).isEqualTo(ON)
- fontScalingDialog.dismiss()
+ dialog.dismiss()
}
@Test
fun dragSeekbar_systemFontSizeSettingsDoesNotChange() {
- fontScalingDialog = spy(fontScalingDialog)
- val slider: SeekBarWithIconButtonsView = spy(SeekBarWithIconButtonsView(mContext))
- whenever(
- fontScalingDialog.findViewById<SeekBarWithIconButtonsView>(R.id.font_scaling_slider)
- )
- .thenReturn(slider)
- fontScalingDialog.show()
- verify(slider).setOnSeekBarWithIconButtonsChangeListener(capture(seekBarChangeCaptor))
+ dialog.show()
+
+ val slider = dialog.findViewById<SeekBarWithIconButtonsView>(R.id.font_scaling_slider)!!
+ val changeListener = slider.onSeekBarWithIconButtonsChangeListener
+
val seekBar: SeekBar = slider.findViewById(R.id.seekbar)!!
// Default seekbar progress for font size is 1, simulate dragging to 0 without
// releasing the finger.
- seekBarChangeCaptor.value.onStartTrackingTouch(seekBar)
+ changeListener.onStartTrackingTouch(seekBar)
// Update seekbar progress. This will trigger onProgressChanged in the
// OnSeekBarChangeListener and the seekbar could get updated progress value
// in onStopTrackingTouch.
@@ -238,13 +262,13 @@ class FontScalingDialogTest : SysuiTestCase() {
assertThat(systemScale).isEqualTo(1.0f)
// Simulate releasing the finger from the seekbar.
- seekBarChangeCaptor.value.onStopTrackingTouch(seekBar)
+ changeListener.onStopTrackingTouch(seekBar)
backgroundDelayableExecutor.runAllReady()
backgroundDelayableExecutor.advanceClockToNext()
backgroundDelayableExecutor.runAllReady()
// SeekBar interaction is finalized.
- seekBarChangeCaptor.value.onUserInteractionFinalized(
+ changeListener.onUserInteractionFinalized(
seekBar,
OnSeekBarWithIconButtonsChangeListener.ControlUnitType.SLIDER
)
@@ -261,25 +285,21 @@ class FontScalingDialogTest : SysuiTestCase() {
)
assertThat(systemScale).isEqualTo(fontSizeValueArray[0].toFloat())
- fontScalingDialog.dismiss()
+ dialog.dismiss()
}
@Test
fun dragSeekBar_createTextPreview() {
- fontScalingDialog = spy(fontScalingDialog)
- val slider: SeekBarWithIconButtonsView = spy(SeekBarWithIconButtonsView(mContext))
- whenever(
- fontScalingDialog.findViewById<SeekBarWithIconButtonsView>(R.id.font_scaling_slider)
- )
- .thenReturn(slider)
- fontScalingDialog.show()
- verify(slider).setOnSeekBarWithIconButtonsChangeListener(capture(seekBarChangeCaptor))
+ dialog.show()
+ val slider = dialog.findViewById<SeekBarWithIconButtonsView>(R.id.font_scaling_slider)!!
+ val changeListener = slider.onSeekBarWithIconButtonsChangeListener
+
val seekBar: SeekBar = slider.findViewById(R.id.seekbar)!!
// Default seekbar progress for font size is 1, simulate dragging to 0 without
// releasing the finger
- seekBarChangeCaptor.value.onStartTrackingTouch(seekBar)
- seekBarChangeCaptor.value.onProgressChanged(
+ changeListener.onStartTrackingTouch(seekBar)
+ changeListener.onProgressChanged(
seekBar,
/* progress= */ 0,
/* fromUser= */ false
@@ -287,16 +307,16 @@ class FontScalingDialogTest : SysuiTestCase() {
backgroundDelayableExecutor.advanceClockToNext()
backgroundDelayableExecutor.runAllReady()
- verify(fontScalingDialog).createTextPreview(/* index= */ 0)
- fontScalingDialog.dismiss()
+ verify(fontScalingDialogDelegate).createTextPreview(/* index= */ 0)
+ dialog.dismiss()
}
@Test
fun changeFontSize_buttonIsDisabledBeforeFontSizeChangeFinishes() {
- fontScalingDialog.show()
+ dialog.show()
- val iconEndFrame: ViewGroup = fontScalingDialog.findViewById(R.id.icon_end_frame)!!
- val doneButton: Button = fontScalingDialog.findViewById(com.android.internal.R.id.button1)!!
+ val iconEndFrame: ViewGroup = dialog.findViewById(R.id.icon_end_frame)!!
+ val doneButton: Button = dialog.findViewById(com.android.internal.R.id.button1)!!
iconEndFrame.performClick()
backgroundDelayableExecutor.runAllReady()
@@ -308,7 +328,7 @@ class FontScalingDialogTest : SysuiTestCase() {
val config = Configuration()
config.fontScale = 1.15f
- fontScalingDialog.onConfigurationChanged(config)
+ dialog.onConfigurationChanged(config)
testableLooper.processAllMessages()
assertThat(doneButton.isEnabled).isTrue()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
index d1d3c17be67f..77964527eaf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
@@ -24,6 +24,7 @@ import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.fontscaling.FontScalingDialogDelegate
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -31,13 +32,12 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
-import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.After
@@ -45,9 +45,9 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
@@ -64,8 +64,9 @@ class FontScalingTileTest : SysuiTestCase() {
@Mock private lateinit var qsLogger: QSLogger
@Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
@Mock private lateinit var uiEventLogger: QsEventLogger
- @Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var fontScalingDialogDelegate: FontScalingDialogDelegate
+ @Mock private lateinit var dialog: SystemUIDialog
private lateinit var testableLooper: TestableLooper
private lateinit var systemClock: FakeSystemClock
@@ -79,6 +80,7 @@ class FontScalingTileTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
`when`(qsHost.getContext()).thenReturn(mContext)
+ `when`(fontScalingDialogDelegate.createDialog()).thenReturn(dialog)
systemClock = FakeSystemClock()
backgroundDelayableExecutor = FakeExecutor(systemClock)
@@ -95,11 +97,7 @@ class FontScalingTileTest : SysuiTestCase() {
qsLogger,
keyguardStateController,
dialogLaunchAnimator,
- FakeSettings(),
- FakeSettings(),
- FakeSystemClock(),
- userTracker,
- backgroundDelayableExecutor,
+ { fontScalingDialogDelegate },
)
fontScalingTile.initialize()
testableLooper.processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index 56061f64c315..9fa173ab040a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -83,6 +83,7 @@ import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
+import android.graphics.Insets
import org.mockito.junit.MockitoJUnit
private val EMPTY_CHANGES = ConstraintsChanges()
@@ -930,12 +931,16 @@ class ShadeHeaderControllerTest : SysuiTestCase() {
return windowInsets
}
- private fun mockInsetsProvider(
- insets: Pair<Int, Int> = 0 to 0,
- cornerCutout: Boolean = false,
- ) {
+ private fun mockInsetsProvider(insets: Pair<Int, Int> = 0 to 0, cornerCutout: Boolean = false) {
whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(insets.toAndroidPair())
+ .thenReturn(
+ Insets.of(
+ /* left= */ insets.first,
+ /* top= */ 0,
+ /* right= */ insets.second,
+ /* bottom= */ 0
+ )
+ )
whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(cornerCutout)
}
@@ -980,7 +985,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() {
)
.thenReturn(EMPTY_CHANGES)
whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(Pair(0, 0).toAndroidPair())
+ .thenReturn(Insets.NONE)
whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(false)
setupCurrentInsets(null)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 186ae6b0c4f0..ee94cbbcfd79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -381,12 +381,15 @@ class ClockRegistryTest : SysuiTestCase() {
}
@Test
- fun knownPluginAttached_clockAndListChanged_notLoaded() {
- val lifecycle1 = FakeLifecycle("Metro", null).apply {
- mComponentName = ComponentName("com.android.systemui.clocks.metro", "MetroClock")
+ fun knownPluginAttached_clockAndListChanged_loadedCurrent() {
+ val metroLifecycle = FakeLifecycle("Metro", null).apply {
+ mComponentName = ComponentName("com.android.systemui.clocks.metro", "Metro")
}
- val lifecycle2 = FakeLifecycle("BigNum", null).apply {
- mComponentName = ComponentName("com.android.systemui.clocks.bignum", "BigNumClock")
+ val bignumLifecycle = FakeLifecycle("BigNum", null).apply {
+ mComponentName = ComponentName("com.android.systemui.clocks.bignum", "BigNum")
+ }
+ val calligraphyLifecycle = FakeLifecycle("Calligraphy", null).apply {
+ mComponentName = ComponentName("com.android.systemui.clocks.calligraphy", "Calligraphy")
}
var changeCallCount = 0
@@ -401,15 +404,21 @@ class ClockRegistryTest : SysuiTestCase() {
assertEquals(1, changeCallCount)
assertEquals(0, listChangeCallCount)
- assertEquals(false, pluginListener.onPluginAttached(lifecycle1))
+ assertEquals(false, pluginListener.onPluginAttached(metroLifecycle))
scheduler.runCurrent()
assertEquals(1, changeCallCount)
assertEquals(1, listChangeCallCount)
- assertEquals(false, pluginListener.onPluginAttached(lifecycle2))
+ assertEquals(false, pluginListener.onPluginAttached(bignumLifecycle))
scheduler.runCurrent()
assertEquals(1, changeCallCount)
assertEquals(2, listChangeCallCount)
+
+ // This returns true, but doesn't trigger onCurrentClockChanged yet
+ assertEquals(true, pluginListener.onPluginAttached(calligraphyLifecycle))
+ scheduler.runCurrent()
+ assertEquals(1, changeCallCount)
+ assertEquals(3, listChangeCallCount)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index b04d5d3d44e4..260bef8e98ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -503,10 +503,10 @@ public class CommandQueueTest extends SysuiTestCase {
}
@Test
- public void testRequestWindowMagnificationConnection() {
- mCommandQueue.requestWindowMagnificationConnection(true);
+ public void testRequestMagnificationConnection() {
+ mCommandQueue.requestMagnificationConnection(true);
waitForIdleSync();
- verify(mCallbacks).requestWindowMagnificationConnection(true);
+ verify(mCallbacks).requestMagnificationConnection(true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt
new file mode 100644
index 000000000000..2951fc0232b3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2023 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.systemui.statusbar.events
+
+import android.graphics.Point
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Display
+import android.view.DisplayAdjustments
+import android.view.View
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.FakeStatusBarStateController
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.test.TestScope
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class PrivacyDotViewControllerTest : SysuiTestCase() {
+
+ private val context = getContext().createDisplayContext(createMockDisplay())
+
+ private val testScope = TestScope()
+ private val executor = InstantExecutor()
+ private val statusBarStateController = FakeStatusBarStateController()
+ private val configurationController = FakeConfigurationController()
+ private val contentInsetsProvider = createMockContentInsetsProvider()
+
+ private val topLeftView = initDotView()
+ private val topRightView = initDotView()
+ private val bottomLeftView = initDotView()
+ private val bottomRightView = initDotView()
+
+ private val controller =
+ PrivacyDotViewController(
+ executor,
+ testScope.backgroundScope,
+ statusBarStateController,
+ configurationController,
+ contentInsetsProvider,
+ animationScheduler = mock<SystemStatusAnimationScheduler>(),
+ shadeInteractor = null
+ )
+ .also {
+ it.setUiExecutor(executor)
+ it.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+ }
+
+ @Test
+ fun topMargin_topLeftView_basedOnSeascapeArea() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+
+ assertThat(topLeftView.frameLayoutParams.topMargin)
+ .isEqualTo(CONTENT_AREA_ROTATION_SEASCAPE.top)
+ }
+
+ @Test
+ fun topMargin_topRightView_basedOnPortraitArea() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+
+ assertThat(topRightView.frameLayoutParams.topMargin)
+ .isEqualTo(CONTENT_AREA_ROTATION_NONE.top)
+ }
+
+ @Test
+ fun topMargin_bottomLeftView_basedOnUpsideDownArea() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+
+ assertThat(bottomLeftView.frameLayoutParams.topMargin)
+ .isEqualTo(CONTENT_AREA_ROTATION_UPSIDE_DOWN.top)
+ }
+
+ @Test
+ fun topMargin_bottomRightView_basedOnLandscapeArea() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+
+ assertThat(bottomRightView.frameLayoutParams.topMargin)
+ .isEqualTo(CONTENT_AREA_ROTATION_LANDSCAPE.top)
+ }
+
+ @Test
+ fun height_topLeftView_basedOnSeascapeAreaHeight() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+
+ assertThat(topLeftView.layoutParams.height)
+ .isEqualTo(CONTENT_AREA_ROTATION_SEASCAPE.height())
+ }
+
+ @Test
+ fun height_topRightView_basedOnPortraitAreaHeight() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+
+ assertThat(topRightView.layoutParams.height).isEqualTo(CONTENT_AREA_ROTATION_NONE.height())
+ }
+
+ @Test
+ fun height_bottomLeftView_basedOnUpsidedownAreaHeight() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+
+ assertThat(bottomLeftView.layoutParams.height)
+ .isEqualTo(CONTENT_AREA_ROTATION_UPSIDE_DOWN.height())
+ }
+
+ @Test
+ fun height_bottomRightView_basedOnLandscapeAreaHeight() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+
+ assertThat(bottomRightView.layoutParams.height)
+ .isEqualTo(CONTENT_AREA_ROTATION_LANDSCAPE.height())
+ }
+
+ @Test
+ fun width_topLeftView_ltr_basedOnDisplayHeightAndSeascapeArea() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+
+ assertThat(topLeftView.layoutParams.width)
+ .isEqualTo(DISPLAY_HEIGHT - CONTENT_AREA_ROTATION_SEASCAPE.right)
+ }
+
+ @Test
+ fun width_topLeftView_rtl_basedOnPortraitArea() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+ configurationController.notifyLayoutDirectionChanged(isRtl = true)
+
+ assertThat(topLeftView.layoutParams.width).isEqualTo(CONTENT_AREA_ROTATION_NONE.left)
+ }
+
+ @Test
+ fun width_topRightView_ltr_basedOnPortraitArea() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+
+ assertThat(topRightView.layoutParams.width)
+ .isEqualTo(DISPLAY_WIDTH - CONTENT_AREA_ROTATION_NONE.right)
+ }
+
+ @Test
+ fun width_topRightView_rtl_basedOnLandscapeArea() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+ configurationController.notifyLayoutDirectionChanged(isRtl = true)
+
+ assertThat(topRightView.layoutParams.width).isEqualTo(CONTENT_AREA_ROTATION_LANDSCAPE.left)
+ }
+
+ @Test
+ fun width_bottomRightView_ltr_basedOnDisplayHeightAndLandscapeArea() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+
+ assertThat(bottomRightView.layoutParams.width)
+ .isEqualTo(DISPLAY_HEIGHT - CONTENT_AREA_ROTATION_LANDSCAPE.right)
+ }
+
+ @Test
+ fun width_bottomRightView_rtl_basedOnUpsideDown() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+ configurationController.notifyLayoutDirectionChanged(isRtl = true)
+
+ assertThat(bottomRightView.layoutParams.width)
+ .isEqualTo(CONTENT_AREA_ROTATION_UPSIDE_DOWN.left)
+ }
+
+ @Test
+ fun width_bottomLeftView_ltr_basedOnDisplayWidthAndUpsideDownArea() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+
+ assertThat(bottomLeftView.layoutParams.width)
+ .isEqualTo(DISPLAY_WIDTH - CONTENT_AREA_ROTATION_UPSIDE_DOWN.right)
+ }
+
+ @Test
+ fun width_bottomLeftView_rtl_basedOnSeascapeArea() {
+ controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView)
+ configurationController.notifyLayoutDirectionChanged(isRtl = true)
+
+ assertThat(bottomLeftView.layoutParams.width).isEqualTo(CONTENT_AREA_ROTATION_SEASCAPE.left)
+ }
+
+ private fun initDotView(): View =
+ View(context).also {
+ it.layoutParams = FrameLayout.LayoutParams(/* width = */ 0, /* height = */ 0)
+ }
+}
+
+private const val DISPLAY_WIDTH = 1234
+private const val DISPLAY_HEIGHT = 2345
+private val CONTENT_AREA_ROTATION_SEASCAPE = Rect(left = 10, top = 40, right = 990, bottom = 100)
+private val CONTENT_AREA_ROTATION_NONE = Rect(left = 20, top = 30, right = 980, bottom = 100)
+private val CONTENT_AREA_ROTATION_LANDSCAPE = Rect(left = 30, top = 20, right = 970, bottom = 100)
+private val CONTENT_AREA_ROTATION_UPSIDE_DOWN = Rect(left = 40, top = 10, right = 960, bottom = 100)
+
+private class InstantExecutor : DelayableExecutor {
+ override fun execute(runnable: Runnable) {
+ runnable.run()
+ }
+
+ override fun executeDelayed(runnable: Runnable, delay: Long, unit: TimeUnit) =
+ runnable.apply { run() }
+
+ override fun executeAtTime(runnable: Runnable, uptimeMillis: Long, unit: TimeUnit) =
+ runnable.apply { run() }
+}
+
+private fun Rect(left: Int, top: Int, right: Int, bottom: Int) = Rect(left, top, right, bottom)
+
+private val View.frameLayoutParams
+ get() = layoutParams as FrameLayout.LayoutParams
+
+private fun createMockDisplay() =
+ mock<Display>().also { display ->
+ whenever(display.getRealSize(any(Point::class.java))).thenAnswer { invocation ->
+ val output = invocation.arguments[0] as Point
+ output.x = DISPLAY_WIDTH
+ output.y = DISPLAY_HEIGHT
+ return@thenAnswer Unit
+ }
+ whenever(display.displayAdjustments).thenReturn(DisplayAdjustments())
+ }
+
+private fun createMockContentInsetsProvider() =
+ mock<StatusBarContentInsetsProvider>().also {
+ whenever(it.getStatusBarContentAreaForRotation(ROTATION_SEASCAPE))
+ .thenReturn(CONTENT_AREA_ROTATION_SEASCAPE)
+ whenever(it.getStatusBarContentAreaForRotation(ROTATION_NONE))
+ .thenReturn(CONTENT_AREA_ROTATION_NONE)
+ whenever(it.getStatusBarContentAreaForRotation(ROTATION_LANDSCAPE))
+ .thenReturn(CONTENT_AREA_ROTATION_LANDSCAPE)
+ whenever(it.getStatusBarContentAreaForRotation(ROTATION_UPSIDE_DOWN))
+ .thenReturn(CONTENT_AREA_ROTATION_UPSIDE_DOWN)
+ }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
index df257ab113b3..8be2ef008039 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
@@ -17,10 +17,10 @@
package com.android.systemui.statusbar.events
import android.content.Context
+import android.graphics.Insets
import android.graphics.Rect
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
-import android.util.Pair
import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
@@ -79,7 +79,14 @@ class SystemEventChipAnimationControllerTest : SysuiTestCase() {
}
whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(Pair(insets, insets))
+ .thenReturn(
+ Insets.of(
+ /* left= */ insets,
+ /* top= */ insets,
+ /* right= */ insets,
+ /* bottom= */ 0
+ )
+ )
whenever(insetsProvider.getStatusBarContentAreaForCurrentRotation())
.thenReturn(portraitArea)
@@ -105,18 +112,18 @@ class SystemEventChipAnimationControllerTest : SysuiTestCase() {
controller.prepareChipAnimation(viewCreator)
val chipRect = controller.chipBounds
- // SB area = 10, 0, 990, 100
+ // SB area = 10, 10, 990, 100
// chip size = 0, 0, 100, 50
- assertThat(chipRect).isEqualTo(Rect(890, 25, 990, 75))
+ assertThat(chipRect).isEqualTo(Rect(890, 30, 990, 80))
}
@Test
fun prepareChipAnimation_rotation_repositionsChip() {
controller.prepareChipAnimation(viewCreator)
- // Chip has been prepared, and is located at (890, 25, 990, 75)
+ // Chip has been prepared, and is located at (890, 30, 990, 75)
// Rotation should put it into its landscape location:
- // SB area = 10, 0, 1990, 80
+ // SB area = 10, 10, 1990, 80
// chip size = 0, 0, 100, 50
whenever(insetsProvider.getStatusBarContentAreaForCurrentRotation())
@@ -124,7 +131,7 @@ class SystemEventChipAnimationControllerTest : SysuiTestCase() {
getInsetsListener().onStatusBarContentInsetsChanged()
val chipRect = controller.chipBounds
- assertThat(chipRect).isEqualTo(Rect(1890, 15, 1990, 65))
+ assertThat(chipRect).isEqualTo(Rect(1890, 20, 1990, 70))
}
/** regression test for (b/289378932) */
@@ -162,7 +169,7 @@ class SystemEventChipAnimationControllerTest : SysuiTestCase() {
// THEN it still aligns the chip to the content area provided by the insets provider
val chipRect = controller.chipBounds
- assertThat(chipRect).isEqualTo(Rect(890, 25, 990, 75))
+ assertThat(chipRect).isEqualTo(Rect(890, 30, 990, 80))
}
private class TestView(context: Context) : View(context), BackgroundAnimatableView {
@@ -185,9 +192,9 @@ class SystemEventChipAnimationControllerTest : SysuiTestCase() {
}
companion object {
- private val portraitArea = Rect(10, 0, 990, 100)
- private val landscapeArea = Rect(10, 0, 1990, 80)
- private val fullScreenSb = Rect(10, 0, 990, 2000)
+ private val portraitArea = Rect(10, 10, 990, 100)
+ private val landscapeArea = Rect(10, 10, 1990, 80)
+ private val fullScreenSb = Rect(10, 10, 990, 2000)
// 10px insets on both sides
private const val insets = 10
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index 5f01b5a56e4c..875fe58e5b50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.events
+import android.graphics.Insets
import android.graphics.Rect
import android.os.Process
import android.testing.AndroidTestingRunner
@@ -91,15 +92,19 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() {
// StatusBarContentInsetProvider is mocked. Ensure that it returns some mocked values.
whenever(statusBarContentInsetProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(android.util.Pair(10, 10))
+ .thenReturn(
+ Insets.of(/* left = */ 10, /* top = */ 10, /* right = */ 10, /* bottom = */ 0)
+ )
whenever(statusBarContentInsetProvider.getStatusBarContentAreaForCurrentRotation())
- .thenReturn(Rect(10, 0, 990, 100))
+ .thenReturn(
+ Rect(/* left = */ 10, /* top = */ 10, /* right = */ 990, /* bottom = */ 100)
+ )
// StatusBarWindowController is mocked. The addViewToWindow function needs to be mocked to
// ensure that the chip view is added to a parent view
whenever(statusBarWindowController.addViewToWindow(any(), any())).then {
val statusbarFake = FrameLayout(mContext)
- statusbarFake.layout(0, 0, 1000, 100)
+ statusbarFake.layout(/* l = */ 0, /* t = */ 0, /* r = */ 1000, /* b = */ 100)
statusbarFake.addView(
it.arguments[0] as View,
it.arguments[1] as FrameLayout.LayoutParams
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index 9c10131acae2..65d71f8b4540 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -16,33 +16,47 @@
package com.android.systemui.statusbar.phone
+import android.content.res.Configuration
+import android.graphics.Insets
+import android.graphics.Rect
+import android.testing.TestableLooper.RunWithLooper
+import android.view.DisplayCutout
+import android.view.DisplayShape
+import android.view.LayoutInflater
import android.view.MotionEvent
-import android.view.ViewGroup
+import android.view.PrivacyIndicatorBounds
+import android.view.RoundedCorners
+import android.view.WindowInsets
+import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.Gefingerpoken
import com.android.systemui.SysuiTestCase
-import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.res.R
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.spy
@SmallTest
+@RunWithLooper(setAsMainLooper = true)
class PhoneStatusBarViewTest : SysuiTestCase() {
- @Mock
- private lateinit var shadeViewController: ShadeViewController
- @Mock
- private lateinit var panelView: ViewGroup
-
private lateinit var view: PhoneStatusBarView
+ private val contentInsetsProvider = mock<StatusBarContentInsetsProvider>()
+
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- view = PhoneStatusBarView(mContext, null)
+ mDependency.injectTestDependency(
+ StatusBarContentInsetsProvider::class.java,
+ contentInsetsProvider
+ )
+ mDependency.injectTestDependency(DarkIconDispatcher::class.java, mock<DarkIconDispatcher>())
+ view = spy(createStatusBarView())
+ whenever(view.rootWindowInsets).thenReturn(emptyWindowInsets())
}
@Test
@@ -95,6 +109,48 @@ class PhoneStatusBarViewTest : SysuiTestCase() {
// No assert needed, just testing no crash
}
+ @Test
+ fun onAttachedToWindow_updatesLeftTopRightPaddingsBasedOnInsets() {
+ val insets = Insets.of(/* left = */ 10, /* top = */ 20, /* right = */ 30, /* bottom = */ 40)
+ whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
+ .thenReturn(insets)
+
+ view.onAttachedToWindow()
+
+ assertThat(view.paddingLeft).isEqualTo(insets.left)
+ assertThat(view.paddingTop).isEqualTo(insets.top)
+ assertThat(view.paddingRight).isEqualTo(insets.right)
+ assertThat(view.paddingBottom).isEqualTo(0)
+ }
+
+ @Test
+ fun onConfigurationChanged_updatesLeftTopRightPaddingsBasedOnInsets() {
+ val insets = Insets.of(/* left = */ 40, /* top = */ 30, /* right = */ 20, /* bottom = */ 10)
+ whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
+ .thenReturn(insets)
+
+ view.onConfigurationChanged(Configuration())
+
+ assertThat(view.paddingLeft).isEqualTo(insets.left)
+ assertThat(view.paddingTop).isEqualTo(insets.top)
+ assertThat(view.paddingRight).isEqualTo(insets.right)
+ assertThat(view.paddingBottom).isEqualTo(0)
+ }
+
+ @Test
+ fun onApplyWindowInsets_updatesLeftTopRightPaddingsBasedOnInsets() {
+ val insets = Insets.of(/* left = */ 90, /* top = */ 10, /* right = */ 45, /* bottom = */ 50)
+ whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
+ .thenReturn(insets)
+
+ view.onApplyWindowInsets(WindowInsets(Rect()))
+
+ assertThat(view.paddingLeft).isEqualTo(insets.left)
+ assertThat(view.paddingTop).isEqualTo(insets.top)
+ assertThat(view.paddingRight).isEqualTo(insets.right)
+ assertThat(view.paddingBottom).isEqualTo(0)
+ }
+
private class TestTouchEventHandler : Gefingerpoken {
var lastInterceptEvent: MotionEvent? = null
var lastEvent: MotionEvent? = null
@@ -110,4 +166,28 @@ class PhoneStatusBarViewTest : SysuiTestCase() {
return handleTouchReturnValue
}
}
+
+ private fun createStatusBarView() =
+ LayoutInflater.from(context)
+ .inflate(
+ R.layout.status_bar,
+ /* root= */ FrameLayout(context),
+ /* attachToRoot = */ false
+ ) as PhoneStatusBarView
+
+ private fun emptyWindowInsets() =
+ WindowInsets(
+ /* typeInsetsMap = */ arrayOf(),
+ /* typeMaxInsetsMap = */ arrayOf(),
+ /* typeVisibilityMap = */ booleanArrayOf(),
+ /* isRound = */ false,
+ /* forceConsumingTypes = */ 0,
+ /* suppressScrimTypes = */ 0,
+ /* displayCutout = */ DisplayCutout.NO_CUTOUT,
+ /* roundedCorners = */ RoundedCorners.NO_ROUNDED_CORNERS,
+ /* privacyIndicatorBounds = */ PrivacyIndicatorBounds(),
+ /* displayShape = */ DisplayShape.NONE,
+ /* compatInsetsTypes = */ 0,
+ /* compatIgnoreVisibility = */ false
+ )
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index 210c5ab28c42..5c5624692f07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
import com.android.systemui.util.leak.RotationUtils.Rotation
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertTrue
import org.junit.Before
@@ -62,7 +63,6 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
`when`(contextMock.createConfigurationContext(any())).thenAnswer {
context.createConfigurationContext(it.arguments[0] as Configuration)
}
-
configurationController = ConfigurationControllerImpl(contextMock)
}
@@ -76,6 +76,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val currentRotation = ROTATION_NONE
val chipWidth = 30
val dotWidth = 10
+ val statusBarContentHeight = 15
var isRtl = false
var targetRotation = ROTATION_NONE
@@ -88,7 +89,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
/* 1080 - 20 (rounded corner) - 30 (chip),
@@ -119,7 +122,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
/* 2160 - 20 (rounded corner) - 30 (chip),
@@ -141,6 +146,20 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
}
@Test
+ fun privacyChipBoundingRectForInsets_usesTopInset() {
+ val chipWidth = 30
+ val dotWidth = 10
+ val isRtl = false
+ val contentRect =
+ Rect(/* left = */ 0, /* top = */ 10, /* right = */ 1000, /* bottom = */ 100)
+
+ val chipBounds =
+ getPrivacyChipBoundingRectForInsets(contentRect, dotWidth, chipWidth, isRtl)
+
+ assertThat(chipBounds.top).isEqualTo(contentRect.top)
+ }
+
+ @Test
fun testCalculateInsetsForRotationWithRotatedResources_topLeftCutout() {
// GIVEN a device in portrait mode with width < height and a display cutout in the top-left
val screenBounds = Rect(0, 0, 1080, 2160)
@@ -152,6 +171,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val currentRotation = ROTATION_NONE
val isRtl = false
val dotWidth = 10
+ val statusBarContentHeight = 15
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
@@ -172,7 +192,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -191,7 +213,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -212,7 +236,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -232,12 +258,60 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@Test
+ fun calculateInsetsForRotationWithRotatedResources_bottomAlignedMarginDisabled_noTopInset() {
+ whenever(dc.boundingRects).thenReturn(emptyList())
+
+ val bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation = ROTATION_NONE,
+ targetRotation = ROTATION_NONE,
+ displayCutout = dc,
+ maxBounds = Rect(0, 0, 1080, 2160),
+ statusBarHeight = 100,
+ minLeft = 0,
+ minRight = 0,
+ isRtl = false,
+ dotWidth = 10,
+ bottomAlignedMargin = BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight = 15)
+
+ assertThat(bounds.top).isEqualTo(0)
+ }
+
+ @Test
+ fun calculateInsetsForRotationWithRotatedResources_bottomAlignedMargin_topBasedOnMargin() {
+ whenever(dc.boundingRects).thenReturn(emptyList())
+
+ val bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation = ROTATION_NONE,
+ targetRotation = ROTATION_NONE,
+ displayCutout = dc,
+ maxBounds = Rect(0, 0, 1080, 2160),
+ statusBarHeight = 100,
+ minLeft = 0,
+ minRight = 0,
+ isRtl = false,
+ dotWidth = 10,
+ bottomAlignedMargin = 5,
+ statusBarContentHeight = 15)
+
+ // Content in the status bar is centered vertically. To achieve the bottom margin we want,
+ // we need to "shrink" the height of the status bar until the centered content has the
+ // desired bottom margin. To achieve this shrinking, we use top inset/padding.
+ // "New" SB height = bottom margin * 2 + content height
+ // Top inset = SB height - "New" SB height
+ val expectedTopInset = 75
+ assertThat(bounds.top).isEqualTo(expectedTopInset)
+ }
+
+ @Test
fun testCalculateInsetsForRotationWithRotatedResources_nonCornerCutout() {
// GIVEN phone in portrait mode, where width < height and the cutout is not in the corner
// the assumption here is that if the cutout does NOT touch the corner then we have room to
@@ -253,6 +327,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val currentRotation = ROTATION_NONE
val isRtl = false
val dotWidth = 10
+ val statusBarContentHeight = 15
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
@@ -273,7 +348,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -292,7 +369,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -311,7 +390,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -330,7 +411,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -346,6 +429,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val sbHeightLandscape = 60
val isRtl = false
val dotWidth = 10
+ val statusBarContentHeight = 15
// THEN content insets should only use rounded corner padding
var targetRotation = ROTATION_NONE
@@ -363,7 +447,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
@@ -381,7 +467,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_UPSIDE_DOWN
@@ -399,7 +487,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
@@ -417,7 +507,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -433,17 +525,18 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val currentRotation = ROTATION_NONE
val isRtl = false
val dotWidth = 10
+ val statusBarContentHeight = 15
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
// THEN left should be set to the display cutout width, and right should use the minRight
- var targetRotation = ROTATION_NONE
- var expectedBounds = Rect(dcBounds.right,
+ val targetRotation = ROTATION_NONE
+ val expectedBounds = Rect(dcBounds.right,
0,
screenBounds.right - minRightPadding,
sbHeightPortrait)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ val bounds = calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
dc,
@@ -452,7 +545,9 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
minRightPadding,
isRtl,
- dotWidth)
+ dotWidth,
+ BOTTOM_ALIGNED_MARGIN_NONE,
+ statusBarContentHeight)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -577,4 +672,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
" expected=$expected actual=$actual",
expected.equals(actual))
}
+
+ companion object {
+ private const val BOTTOM_ALIGNED_MARGIN_NONE = -1
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
index 23477d86807c..c51de334c8ca 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
@@ -11,6 +11,7 @@ import javax.inject.Inject
class FakeConfigurationController @Inject constructor() : ConfigurationController {
private var listeners = mutableListOf<ConfigurationController.ConfigurationListener>()
+ private var isRtl = false
override fun addCallback(listener: ConfigurationController.ConfigurationListener) {
listeners += listener
@@ -36,7 +37,12 @@ class FakeConfigurationController @Inject constructor() : ConfigurationControlle
onConfigurationChanged(newConfiguration = null)
}
- override fun isLayoutRtl(): Boolean = false
+ fun notifyLayoutDirectionChanged(isRtl: Boolean) {
+ this.isRtl = isRtl
+ listeners.forEach { it.onLayoutDirectionChanged(isRtl) }
+ }
+
+ override fun isLayoutRtl(): Boolean = isRtl
}
@Module
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2eecb4d3a86c..5bffe80a5c69 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -24,7 +24,7 @@ import static android.accessibilityservice.AccessibilityTrace.FLAGS_FINGERPRINT;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_PACKAGE_BROADCAST_RECEIVER;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_USER_BROADCAST_RECEIVER;
-import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED;
import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID;
@@ -135,7 +135,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
-import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IMagnificationConnection;
import android.view.inputmethod.EditorInfo;
import com.android.internal.R;
@@ -3431,7 +3431,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void updateWindowMagnificationConnectionIfNeeded(AccessibilityUserState userState) {
+ private void updateMagnificationConnectionIfNeeded(AccessibilityUserState userState) {
if (!mMagnificationController.supportWindowMagnification()) {
return;
}
@@ -4110,12 +4110,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
@Override
- public void setWindowMagnificationConnection(
- IWindowMagnificationConnection connection) throws RemoteException {
+ public void setMagnificationConnection(
+ IMagnificationConnection connection) throws RemoteException {
if (mTraceManager.isA11yTracingEnabledForTypes(
- FLAGS_ACCESSIBILITY_MANAGER | FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
- mTraceManager.logTrace(LOG_TAG + ".setWindowMagnificationConnection",
- FLAGS_ACCESSIBILITY_MANAGER | FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_MAGNIFICATION_CONNECTION)) {
+ mTraceManager.logTrace(LOG_TAG + ".setMagnificationConnection",
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_MAGNIFICATION_CONNECTION,
"connection=" + connection);
}
@@ -4422,7 +4422,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
pw.append("visibleBgUserIds=").append(mVisibleBgUserIds.toString());
pw.println();
}
- pw.append("hasWindowMagnificationConnection=").append(
+ pw.append("hasMagnificationConnection=").append(
String.valueOf(getMagnificationConnectionManager().isConnected()));
pw.println();
mMagnificationProcessor.dump(pw, getValidDisplayList());
@@ -5132,7 +5132,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
updateMagnificationModeChangeSettingsLocked(userState, displayId);
}
}
- updateWindowMagnificationConnectionIfNeeded(userState);
+ updateMagnificationConnectionIfNeeded(userState);
// Remove magnification button UI when the magnification capability is not all mode or
// magnification is disabled.
if (!(userState.isMagnificationSingleFingerTripleTapEnabledLocked()
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
index 6114213ef58b..307b555b3b99 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
@@ -223,7 +223,7 @@ public class AccessibilityTraceManager implements AccessibilityTrace {
pw.println(" IAccessibilityInteractionConnection");
pw.println(" IAccessibilityInteractionConnectionCallback");
pw.println(" IRemoteMagnificationAnimationCallback");
- pw.println(" IWindowMagnificationConnection");
+ pw.println(" IMagnificationConnection");
pw.println(" IWindowMagnificationConnectionCallback");
pw.println(" WindowManagerInternal");
pw.println(" WindowsForAccessibilityCallback");
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
index 5a3c070819bd..eff6488bc032 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
@@ -16,7 +16,7 @@
package com.android.server.accessibility.magnification;
-import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
@@ -42,7 +42,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.MotionEvent;
-import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
@@ -61,8 +61,8 @@ import java.util.concurrent.atomic.AtomicLongFieldUpdater;
/**
* A class to manipulate magnification through {@link MagnificationConnectionWrapper}
- * create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with
- * SysUI, call {@code StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)}.
+ * create by {@link #setConnection(IMagnificationConnection)}. To set the connection with
+ * SysUI, call {@code StatusBarManagerInternal#requestMagnificationConnection(boolean)}.
* The applied magnification scale is constrained by
* {@link MagnificationScaleProvider#constrainScale(float)}
*/
@@ -93,13 +93,13 @@ public class MagnificationConnectionManager implements
})
public @interface WindowPosition {}
- /** Window magnification connection is connecting. */
+ /** Magnification connection is connecting. */
private static final int CONNECTING = 0;
- /** Window magnification connection is connected. */
+ /** Magnification connection is connected. */
private static final int CONNECTED = 1;
- /** Window magnification connection is disconnecting. */
+ /** Magnification connection is disconnecting. */
private static final int DISCONNECTING = 2;
- /** Window magnification connection is disconnected. */
+ /** Magnification connection is disconnected. */
private static final int DISCONNECTED = 3;
@Retention(RetentionPolicy.SOURCE)
@@ -195,7 +195,7 @@ public class MagnificationConnectionManager implements
void onSourceBoundsChanged(int displayId, Rect bounds);
/**
- * Called from {@link IWindowMagnificationConnection} to request changing the magnification
+ * Called from {@link IMagnificationConnection} to request changing the magnification
* mode on the given display.
*
* @param displayId the logical display id
@@ -218,11 +218,11 @@ public class MagnificationConnectionManager implements
}
/**
- * Sets {@link IWindowMagnificationConnection}.
+ * Sets {@link IMagnificationConnection}.
*
- * @param connection {@link IWindowMagnificationConnection}
+ * @param connection {@link IMagnificationConnection}
*/
- public void setConnection(@Nullable IWindowMagnificationConnection connection) {
+ public void setConnection(@Nullable IMagnificationConnection connection) {
if (DBG) {
Slog.d(TAG, "setConnection :" + connection + ", mConnectionState="
+ connectionStateToString(mConnectionState));
@@ -266,7 +266,7 @@ public class MagnificationConnectionManager implements
}
/**
- * @return {@code true} if {@link IWindowMagnificationConnection} is available
+ * @return {@code true} if {@link IMagnificationConnection} is available
*/
public boolean isConnected() {
synchronized (mLock) {
@@ -275,21 +275,21 @@ public class MagnificationConnectionManager implements
}
/**
- * Requests {@link IWindowMagnificationConnection} through
- * {@link StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)} and
+ * Requests {@link IMagnificationConnection} through
+ * {@link StatusBarManagerInternal#requestMagnificationConnection(boolean)} and
* destroys all window magnifications if necessary.
*
* @param connect {@code true} if needs connection, otherwise set the connection to null and
* destroy all window magnifications.
- * @return {@code true} if {@link IWindowMagnificationConnection} state is going to change.
+ * @return {@code true} if {@link IMagnificationConnection} state is going to change.
*/
public boolean requestConnection(boolean connect) {
if (DBG) {
Slog.d(TAG, "requestConnection :" + connect);
}
- if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
- mTrace.logTrace(TAG + ".requestWindowMagnificationConnection",
- FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "connect=" + connect);
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".requestMagnificationConnection",
+ FLAGS_MAGNIFICATION_CONNECTION, "connect=" + connect);
}
synchronized (mLock) {
if ((connect && (mConnectionState == CONNECTED || mConnectionState == CONNECTING))
@@ -329,7 +329,7 @@ public class MagnificationConnectionManager implements
final StatusBarManagerInternal service = LocalServices.getService(
StatusBarManagerInternal.class);
if (service != null) {
- return service.requestWindowMagnificationConnection(connect);
+ return service.requestMagnificationConnection(connect);
}
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
index 20538f167656..d7098a78d248 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
@@ -16,8 +16,8 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK;
-import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
import static android.os.IBinder.DeathRecipient;
@@ -25,25 +25,25 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
import android.util.Slog;
+import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
import com.android.server.accessibility.AccessibilityTraceManager;
/**
- * A wrapper of {@link IWindowMagnificationConnection}.
+ * A wrapper of {@link IMagnificationConnection}.
*/
class MagnificationConnectionWrapper {
private static final boolean DBG = false;
private static final String TAG = "MagnificationConnectionWrapper";
- private final @NonNull IWindowMagnificationConnection mConnection;
+ private final @NonNull IMagnificationConnection mConnection;
private final @NonNull AccessibilityTraceManager mTrace;
- MagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection,
+ MagnificationConnectionWrapper(@NonNull IMagnificationConnection connection,
@NonNull AccessibilityTraceManager trace) {
mConnection = connection;
mTrace = trace;
@@ -61,9 +61,9 @@ class MagnificationConnectionWrapper {
boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
@Nullable MagnificationAnimationCallback callback) {
- if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".enableWindowMagnification",
- FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ FLAGS_MAGNIFICATION_CONNECTION,
"displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX
+ ";centerY=" + centerY + ";magnificationFrameOffsetRatioX="
+ magnificationFrameOffsetRatioX + ";magnificationFrameOffsetRatioY="
@@ -83,8 +83,8 @@ class MagnificationConnectionWrapper {
}
boolean setScaleForWindowMagnification(int displayId, float scale) {
- if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
- mTrace.logTrace(TAG + ".setScale", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".setScale", FLAGS_MAGNIFICATION_CONNECTION,
"displayId=" + displayId + ";scale=" + scale);
}
try {
@@ -100,9 +100,9 @@ class MagnificationConnectionWrapper {
boolean disableWindowMagnification(int displayId,
@Nullable MagnificationAnimationCallback callback) {
- if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".disableWindowMagnification",
- FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ FLAGS_MAGNIFICATION_CONNECTION,
"displayId=" + displayId + ";callback=" + callback);
}
try {
@@ -118,8 +118,8 @@ class MagnificationConnectionWrapper {
}
boolean moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
- if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
- mTrace.logTrace(TAG + ".moveWindowMagnifier", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".moveWindowMagnifier", FLAGS_MAGNIFICATION_CONNECTION,
"displayId=" + displayId + ";offsetX=" + offsetX + ";offsetY=" + offsetY);
}
try {
@@ -135,9 +135,9 @@ class MagnificationConnectionWrapper {
boolean moveWindowMagnifierToPosition(int displayId, float positionX, float positionY,
@Nullable MagnificationAnimationCallback callback) {
- if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".moveWindowMagnifierToPosition",
- FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "displayId=" + displayId
+ FLAGS_MAGNIFICATION_CONNECTION, "displayId=" + displayId
+ ";positionX=" + positionX + ";positionY=" + positionY);
}
try {
@@ -153,9 +153,9 @@ class MagnificationConnectionWrapper {
}
boolean showMagnificationButton(int displayId, int magnificationMode) {
- if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".showMagnificationButton",
- FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ FLAGS_MAGNIFICATION_CONNECTION,
"displayId=" + displayId + ";mode=" + magnificationMode);
}
try {
@@ -170,9 +170,9 @@ class MagnificationConnectionWrapper {
}
boolean removeMagnificationButton(int displayId) {
- if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".removeMagnificationButton",
- FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "displayId=" + displayId);
+ FLAGS_MAGNIFICATION_CONNECTION, "displayId=" + displayId);
}
try {
mConnection.removeMagnificationButton(displayId);
@@ -186,9 +186,9 @@ class MagnificationConnectionWrapper {
}
boolean removeMagnificationSettingsPanel(int displayId) {
- if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".removeMagnificationSettingsPanel",
- FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "displayId=" + displayId);
+ FLAGS_MAGNIFICATION_CONNECTION, "displayId=" + displayId);
}
try {
mConnection.removeMagnificationSettingsPanel(displayId);
@@ -202,9 +202,9 @@ class MagnificationConnectionWrapper {
}
boolean onUserMagnificationScaleChanged(int userId, int displayId, float scale) {
- if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".onMagnificationScaleUpdated",
- FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "displayId=" + displayId);
+ FLAGS_MAGNIFICATION_CONNECTION, "displayId=" + displayId);
}
try {
mConnection.onUserMagnificationScaleChanged(userId, displayId, scale);
@@ -219,10 +219,10 @@ class MagnificationConnectionWrapper {
boolean setConnectionCallback(IWindowMagnificationConnectionCallback connectionCallback) {
if (mTrace.isA11yTracingEnabledForTypes(
- FLAGS_WINDOW_MAGNIFICATION_CONNECTION
+ FLAGS_MAGNIFICATION_CONNECTION
| FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
mTrace.logTrace(TAG + ".setConnectionCallback",
- FLAGS_WINDOW_MAGNIFICATION_CONNECTION
+ FLAGS_MAGNIFICATION_CONNECTION
| FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
"callback=" + connectionCallback);
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 659112e90203..8ed3fd696bda 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -154,6 +154,7 @@ java_library_static {
static_libs: [
"android.frameworks.location.altitude-V1-java", // AIDL
+ "android.frameworks.vibrator-V1-java", // AIDL
"android.hardware.authsecret-V1.0-java",
"android.hardware.authsecret-V1-java",
"android.hardware.boot-V1.0-java", // HIDL
@@ -193,7 +194,6 @@ java_library_static {
"overlayable_policy_aidl-java",
"SurfaceFlingerProperties",
"com.android.sysprop.watchdog",
- "ImmutabilityAnnotation",
"securebox",
"apache-commons-math",
"backstage_power_flags_lib",
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b87d02d86c22..6ec4fbc21626 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -429,10 +429,10 @@ import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
-import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.AlarmManagerInternal;
import com.android.server.BootReceiver;
@@ -20149,20 +20149,21 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public int checkOperation(int code, int uid, String packageName,
- String attributionTag, boolean raw,
- QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl) {
+ public int checkOperation(int code, int uid, String packageName, String attributionTag,
+ int virtualDeviceId, boolean raw, HexFunction<Integer, Integer, String, String,
+ Integer, Boolean, Integer> superImpl) {
if (uid == mTargetUid && isTargetOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(code, shellUid, "com.android.shell", null, raw);
+ return superImpl.apply(code, shellUid, "com.android.shell", null,
+ virtualDeviceId, raw);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(code, uid, packageName, attributionTag, raw);
+ return superImpl.apply(code, uid, packageName, attributionTag, virtualDeviceId, raw);
}
@Override
@@ -20183,23 +20184,24 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
- @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
+ @Nullable String featureId, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage,
- @NonNull HeptFunction<Integer, Integer, String, String, Boolean, String, Boolean,
- SyncNotedAppOp> superImpl) {
+ @NonNull OctFunction<Integer, Integer, String, String, Integer, Boolean, String,
+ Boolean, SyncNotedAppOp> superImpl) {
if (uid == mTargetUid && isTargetOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
return superImpl.apply(code, shellUid, "com.android.shell", featureId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ virtualDeviceId, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage);
+ return superImpl.apply(code, uid, packageName, featureId, virtualDeviceId,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
@Override
@@ -20230,11 +20232,11 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
- @Nullable String packageName, @Nullable String attributionTag,
+ @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage,
@AttributionFlags int attributionFlags, int attributionChainId,
- @NonNull UndecFunction<IBinder, Integer, Integer, String, String, Boolean,
+ @NonNull DodecFunction<IBinder, Integer, Integer, String, String, Integer, Boolean,
Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl) {
if (uid == mTargetUid && isTargetOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
@@ -20242,13 +20244,14 @@ public class ActivityManagerService extends IActivityManager.Stub
final long identity = Binder.clearCallingIdentity();
try {
return superImpl.apply(token, code, shellUid, "com.android.shell",
- attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, attributionFlags, attributionChainId);
+ attributionTag, virtualDeviceId, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ attributionFlags, attributionChainId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(token, code, uid, packageName, attributionTag,
+ return superImpl.apply(token, code, uid, packageName, attributionTag, virtualDeviceId,
startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
attributionFlags, attributionChainId);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7780b3906b9f..d80638af697e 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2580,17 +2580,30 @@ public class AppOpsService extends IAppOpsService.Stub {
public int checkOperationRaw(int code, int uid, String packageName,
@Nullable String attributionTag) {
return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag,
- true /*raw*/);
+ Context.DEVICE_ID_DEFAULT, true /*raw*/);
+ }
+
+ @Override
+ public int checkOperationRawForDevice(int code, int uid, @Nullable String packageName,
+ @Nullable String attributionTag, int virtualDeviceId) {
+ return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag,
+ virtualDeviceId, true /*raw*/);
}
@Override
public int checkOperation(int code, int uid, String packageName) {
return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, null,
- false /*raw*/);
+ Context.DEVICE_ID_DEFAULT, false /*raw*/);
+ }
+
+ @Override
+ public int checkOperationForDevice(int code, int uid, String packageName, int virtualDeviceId) {
+ return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, null,
+ virtualDeviceId, false /*raw*/);
}
private int checkOperationImpl(int code, int uid, String packageName,
- @Nullable String attributionTag, boolean raw) {
+ @Nullable String attributionTag, int virtualDeviceId, boolean raw) {
verifyIncomingOp(code);
if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
return AppOpsManager.opToDefaultMode(code);
@@ -2816,12 +2829,23 @@ public class AppOpsService extends IAppOpsService.Stub {
String attributionTag, boolean shouldCollectAsyncNotedOp, String message,
boolean shouldCollectMessage) {
return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
- attributionTag, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ attributionTag, Context.DEVICE_ID_DEFAULT, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage);
+ }
+
+ @Override
+ public SyncNotedAppOp noteOperationForDevice(int code, int uid, @Nullable String packageName,
+ @Nullable String attributionTag, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
+ String message, boolean shouldCollectMessage) {
+ return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
+ attributionTag, virtualDeviceId, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage);
}
private SyncNotedAppOp noteOperationImpl(int code, int uid, @Nullable String packageName,
- @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp,
- @Nullable String message, boolean shouldCollectMessage) {
+ @Nullable String attributionTag, int virtualDeviceId,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
@@ -2840,10 +2864,10 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private SyncNotedAppOp noteOperationUnchecked(int code, int uid, @NonNull String packageName,
- @Nullable String attributionTag, int proxyUid, String proxyPackageName,
- @Nullable String proxyAttributionTag, @OpFlags int flags,
- boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage) {
+ @Nullable String attributionTag, int proxyUid, String proxyPackageName,
+ @Nullable String proxyAttributionTag, @OpFlags int flags,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage) {
PackageVerificationResult pvr;
try {
pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
@@ -3238,12 +3262,26 @@ public class AppOpsService extends IAppOpsService.Stub {
String message, boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
int attributionChainId) {
return mCheckOpsDelegateDispatcher.startOperation(token, code, uid, packageName,
- attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, attributionFlags, attributionChainId);
+ attributionTag, Context.DEVICE_ID_DEFAULT, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, attributionFlags,
+ attributionChainId
+ );
+ }
+
+ @Override
+ public SyncNotedAppOp startOperationForDevice(IBinder token, int code, int uid,
+ @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId,
+ boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
+ boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
+ int attributionChainId) {
+ return mCheckOpsDelegateDispatcher.startOperation(token, code, uid, packageName,
+ attributionTag, virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp,
+ message, shouldCollectMessage, attributionFlags, attributionChainId
+ );
}
private SyncNotedAppOp startOperationImpl(@NonNull IBinder clientId, int code, int uid,
- @Nullable String packageName, @Nullable String attributionTag,
+ @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @NonNull String message,
boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
int attributionChainId) {
@@ -3614,11 +3652,18 @@ public class AppOpsService extends IAppOpsService.Stub {
public void finishOperation(IBinder clientId, int code, int uid, String packageName,
String attributionTag) {
mCheckOpsDelegateDispatcher.finishOperation(clientId, code, uid, packageName,
- attributionTag);
+ attributionTag, Context.DEVICE_ID_DEFAULT);
+ }
+
+ @Override
+ public void finishOperationForDevice(IBinder clientId, int code, int uid,
+ @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId) {
+ mCheckOpsDelegateDispatcher.finishOperation(clientId, code, uid, packageName,
+ attributionTag, virtualDeviceId);
}
private void finishOperationImpl(IBinder clientId, int code, int uid, String packageName,
- String attributionTag) {
+ String attributionTag, int virtualDeviceId) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
@@ -6800,25 +6845,28 @@ public class AppOpsService extends IAppOpsService.Stub {
}
public int checkOperation(int code, int uid, String packageName,
- @Nullable String attributionTag, boolean raw) {
+ @Nullable String attributionTag, int virtualDeviceId, boolean raw) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
- return mPolicy.checkOperation(code, uid, packageName, attributionTag, raw,
- this::checkDelegateOperationImpl);
+ return mPolicy.checkOperation(code, uid, packageName, attributionTag,
+ virtualDeviceId, raw, this::checkDelegateOperationImpl
+ );
} else {
- return mPolicy.checkOperation(code, uid, packageName, attributionTag, raw,
- AppOpsService.this::checkOperationImpl);
+ return mPolicy.checkOperation(code, uid, packageName, attributionTag,
+ virtualDeviceId, raw, AppOpsService.this::checkOperationImpl
+ );
}
} else if (mCheckOpsDelegate != null) {
- return checkDelegateOperationImpl(code, uid, packageName, attributionTag, raw);
+ return checkDelegateOperationImpl(code, uid, packageName, attributionTag,
+ virtualDeviceId, raw);
}
- return checkOperationImpl(code, uid, packageName, attributionTag, raw);
+ return checkOperationImpl(code, uid, packageName, attributionTag, virtualDeviceId, raw);
}
private int checkDelegateOperationImpl(int code, int uid, String packageName,
- @Nullable String attributionTag, boolean raw) {
- return mCheckOpsDelegate.checkOperation(code, uid, packageName, attributionTag, raw,
- AppOpsService.this::checkOperationImpl);
+ @Nullable String attributionTag, int virtualDeviceId, boolean raw) {
+ return mCheckOpsDelegate.checkOperation(code, uid, packageName, attributionTag,
+ virtualDeviceId, raw, AppOpsService.this::checkOperationImpl);
}
public int checkAudioOperation(int code, int usage, int uid, String packageName) {
@@ -6843,33 +6891,36 @@ public class AppOpsService extends IAppOpsService.Stub {
}
public SyncNotedAppOp noteOperation(int code, int uid, String packageName,
- String attributionTag, boolean shouldCollectAsyncNotedOp, String message,
- boolean shouldCollectMessage) {
+ String attributionTag, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
+ String message, boolean shouldCollectMessage) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
return mPolicy.noteOperation(code, uid, packageName, attributionTag,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- this::noteDelegateOperationImpl);
+ virtualDeviceId, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, this::noteDelegateOperationImpl
+ );
} else {
return mPolicy.noteOperation(code, uid, packageName, attributionTag,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- AppOpsService.this::noteOperationImpl);
+ virtualDeviceId, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, AppOpsService.this::noteOperationImpl
+ );
}
} else if (mCheckOpsDelegate != null) {
- return noteDelegateOperationImpl(code, uid, packageName,
- attributionTag, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ return noteDelegateOperationImpl(code, uid, packageName, attributionTag,
+ virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
return noteOperationImpl(code, uid, packageName, attributionTag,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
private SyncNotedAppOp noteDelegateOperationImpl(int code, int uid,
- @Nullable String packageName, @Nullable String featureId,
+ @Nullable String packageName, @Nullable String featureId, int virtualDeviceId,
boolean shouldCollectAsyncNotedOp, @Nullable String message,
boolean shouldCollectMessage) {
return mCheckOpsDelegate.noteOperation(code, uid, packageName, featureId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- AppOpsService.this::noteOperationImpl);
+ virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ AppOpsService.this::noteOperationImpl
+ );
}
public SyncNotedAppOp noteProxyOperation(int code, AttributionSource attributionSource,
@@ -6904,40 +6955,45 @@ public class AppOpsService extends IAppOpsService.Stub {
}
public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
- @Nullable String packageName, @NonNull String attributionTag,
+ @Nullable String packageName, @NonNull String attributionTag, int virtualDeviceId,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage,
@AttributionFlags int attributionFlags, int attributionChainId) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
- return mPolicy.startOperation(token, code, uid, packageName,
- attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ return mPolicy.startOperation(token, code, uid, packageName, attributionTag,
+ virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
shouldCollectMessage, attributionFlags, attributionChainId,
- this::startDelegateOperationImpl);
+ this::startDelegateOperationImpl
+ );
} else {
return mPolicy.startOperation(token, code, uid, packageName, attributionTag,
- startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
shouldCollectMessage, attributionFlags, attributionChainId,
- AppOpsService.this::startOperationImpl);
+ AppOpsService.this::startOperationImpl
+ );
}
} else if (mCheckOpsDelegate != null) {
return startDelegateOperationImpl(token, code, uid, packageName, attributionTag,
- startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, attributionFlags, attributionChainId);
+ virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, attributionFlags, attributionChainId
+ );
}
return startOperationImpl(token, code, uid, packageName, attributionTag,
- startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- attributionFlags, attributionChainId);
+ virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, attributionFlags, attributionChainId
+ );
}
private SyncNotedAppOp startDelegateOperationImpl(IBinder token, int code, int uid,
@Nullable String packageName, @Nullable String attributionTag,
- boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
- boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
- int attributionChainId) {
+ int virtualDeviceId, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
+ String message, boolean shouldCollectMessage,
+ @AttributionFlags int attributionFlags, int attributionChainId) {
return mCheckOpsDelegate.startOperation(token, code, uid, packageName, attributionTag,
- startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- attributionFlags, attributionChainId, AppOpsService.this::startOperationImpl);
+ virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, attributionFlags, attributionChainId,
+ AppOpsService.this::startOperationImpl);
}
public SyncNotedAppOp startProxyOperation(@NonNull IBinder clientId, int code,
@@ -6982,26 +7038,28 @@ public class AppOpsService extends IAppOpsService.Stub {
}
public void finishOperation(IBinder clientId, int code, int uid, String packageName,
- String attributionTag) {
+ String attributionTag, int virtualDeviceId) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
mPolicy.finishOperation(clientId, code, uid, packageName, attributionTag,
- this::finishDelegateOperationImpl);
+ virtualDeviceId, this::finishDelegateOperationImpl);
} else {
mPolicy.finishOperation(clientId, code, uid, packageName, attributionTag,
- AppOpsService.this::finishOperationImpl);
+ virtualDeviceId, AppOpsService.this::finishOperationImpl);
}
} else if (mCheckOpsDelegate != null) {
- finishDelegateOperationImpl(clientId, code, uid, packageName, attributionTag);
+ finishDelegateOperationImpl(clientId, code, uid, packageName, attributionTag,
+ virtualDeviceId);
} else {
- finishOperationImpl(clientId, code, uid, packageName, attributionTag);
+ finishOperationImpl(clientId, code, uid, packageName, attributionTag,
+ virtualDeviceId);
}
}
private void finishDelegateOperationImpl(IBinder clientId, int code, int uid,
- String packageName, String attributionTag) {
+ String packageName, String attributionTag, int virtualDeviceId) {
mCheckOpsDelegate.finishOperation(clientId, code, uid, packageName, attributionTag,
- AppOpsService.this::finishOperationImpl);
+ virtualDeviceId, AppOpsService.this::finishOperationImpl);
}
public void finishProxyOperation(@NonNull IBinder clientId, int code,
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
index 5c8dd0d427f9..b91e633bd3de 100644
--- a/services/core/java/com/android/server/audio/AdiDeviceState.java
+++ b/services/core/java/com/android/server/audio/AdiDeviceState.java
@@ -19,6 +19,7 @@ package com.android.server.audio;
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN;
import static android.media.AudioSystem.DEVICE_NONE;
import static android.media.AudioSystem.isBluetoothDevice;
+import static android.media.audio.Flags.automaticBtDeviceType;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -55,6 +56,8 @@ import java.util.Objects;
@AudioManager.AudioDeviceCategory
private int mAudioDeviceCategory = AUDIO_DEVICE_CATEGORY_UNKNOWN;
+ private boolean mAutoBtCategorySet = false;
+
private boolean mSAEnabled;
private boolean mHasHeadTracker = false;
private boolean mHeadTrackerEnabled;
@@ -84,58 +87,94 @@ import java.util.Objects;
mDeviceId = new Pair<>(mInternalDeviceType, mDeviceAddress);
}
- public Pair<Integer, String> getDeviceId() {
+ public synchronized Pair<Integer, String> getDeviceId() {
return mDeviceId;
}
@AudioDeviceInfo.AudioDeviceType
- public int getDeviceType() {
+ public synchronized int getDeviceType() {
return mDeviceType;
}
- public int getInternalDeviceType() {
+ public synchronized int getInternalDeviceType() {
return mInternalDeviceType;
}
@NonNull
- public String getDeviceAddress() {
+ public synchronized String getDeviceAddress() {
return mDeviceAddress;
}
- public void setSAEnabled(boolean sAEnabled) {
+ public synchronized void setSAEnabled(boolean sAEnabled) {
mSAEnabled = sAEnabled;
}
- public boolean isSAEnabled() {
+ public synchronized boolean isSAEnabled() {
return mSAEnabled;
}
- public void setHeadTrackerEnabled(boolean headTrackerEnabled) {
+ public synchronized void setHeadTrackerEnabled(boolean headTrackerEnabled) {
mHeadTrackerEnabled = headTrackerEnabled;
}
- public boolean isHeadTrackerEnabled() {
+ public synchronized boolean isHeadTrackerEnabled() {
return mHeadTrackerEnabled;
}
- public void setHasHeadTracker(boolean hasHeadTracker) {
+ public synchronized void setHasHeadTracker(boolean hasHeadTracker) {
mHasHeadTracker = hasHeadTracker;
}
- public boolean hasHeadTracker() {
+ public synchronized boolean hasHeadTracker() {
return mHasHeadTracker;
}
@AudioDeviceInfo.AudioDeviceType
- public int getAudioDeviceCategory() {
+ public synchronized int getAudioDeviceCategory() {
return mAudioDeviceCategory;
}
- public void setAudioDeviceCategory(@AudioDeviceInfo.AudioDeviceType int audioDeviceCategory) {
+ public synchronized void setAudioDeviceCategory(
+ @AudioDeviceInfo.AudioDeviceType int audioDeviceCategory) {
mAudioDeviceCategory = audioDeviceCategory;
}
+ public synchronized boolean isBtDeviceCategoryFixed() {
+ if (!automaticBtDeviceType()) {
+ // do nothing
+ return false;
+ }
+
+ updateAudioDeviceCategory();
+ return mAutoBtCategorySet;
+ }
+
+ public synchronized boolean updateAudioDeviceCategory() {
+ if (!automaticBtDeviceType()) {
+ // do nothing
+ return false;
+ }
+ if (!isBluetoothDevice(mInternalDeviceType)) {
+ return false;
+ }
+ if (mAutoBtCategorySet) {
+ // no need to update. The auto value is already set.
+ return false;
+ }
+
+ int newAudioDeviceCategory = BtHelper.getBtDeviceCategory(mDeviceAddress);
+ if (newAudioDeviceCategory == AUDIO_DEVICE_CATEGORY_UNKNOWN) {
+ // no info provided by the BtDevice metadata
+ return false;
+ }
+
+ mAudioDeviceCategory = newAudioDeviceCategory;
+ mAutoBtCategorySet = true;
+ return true;
+
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -175,7 +214,7 @@ import java.util.Objects;
+ " HTenabled: " + mHeadTrackerEnabled;
}
- public String toPersistableString() {
+ public synchronized String toPersistableString() {
return (new StringBuilder().append(mDeviceType)
.append(SETTING_FIELD_SEPARATOR).append(mDeviceAddress)
.append(SETTING_FIELD_SEPARATOR).append(mSAEnabled ? "1" : "0")
@@ -228,6 +267,8 @@ import java.util.Objects;
deviceState.setHasHeadTracker(Integer.parseInt(fields[3]) == 1);
deviceState.setHeadTrackerEnabled(Integer.parseInt(fields[4]) == 1);
deviceState.setAudioDeviceCategory(audioDeviceCategory);
+ // update in case we can automatically determine the category
+ deviceState.updateAudioDeviceCategory();
return deviceState;
} catch (NumberFormatException e) {
Log.e(TAG, "unable to parse setting for AdiDeviceState: " + persistedString, e);
@@ -235,7 +276,7 @@ import java.util.Objects;
}
}
- public AudioDeviceAttributes getAudioDeviceAttributes() {
+ public synchronized AudioDeviceAttributes getAudioDeviceAttributes() {
return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
mDeviceType, mDeviceAddress);
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 2f7d99fcbc4b..865c2ab762ff 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -30,6 +30,7 @@ import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
+import android.media.AudioManager.AudioDeviceCategory;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
@@ -1483,8 +1484,12 @@ public class AudioDeviceBroker {
MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES, SENDMSG_QUEUE, groupId);
}
- /*package*/ void postSynchronizeLeDevicesInInventory(AdiDeviceState deviceState) {
- sendLMsgNoDelay(MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY, SENDMSG_QUEUE, deviceState);
+ /*package*/ void postSynchronizeAdiDevicesInInventory(AdiDeviceState deviceState) {
+ sendLMsgNoDelay(MSG_L_SYNCHRONIZE_ADI_DEVICES_IN_INVENTORY, SENDMSG_QUEUE, deviceState);
+ }
+
+ /*package*/ void postUpdatedAdiDeviceState(AdiDeviceState deviceState) {
+ sendLMsgNoDelay(MSG_L_UPDATED_ADI_DEVICE_STATE, SENDMSG_QUEUE, deviceState);
}
/*package*/ static final class CommunicationDeviceInfo {
@@ -2007,14 +2012,19 @@ public class AudioDeviceBroker {
}
} break;
- case MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY:
+ case MSG_L_SYNCHRONIZE_ADI_DEVICES_IN_INVENTORY:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- mDeviceInventory.onSynchronizeLeDevicesInInventory(
+ mDeviceInventory.onSynchronizeAdiDevicesInInventory(
(AdiDeviceState) msg.obj);
}
} break;
+ case MSG_L_UPDATED_ADI_DEVICE_STATE:
+ synchronized (mDeviceStateLock) {
+ mAudioService.onUpdatedAdiDeviceState((AdiDeviceState) msg.obj);
+ } break;
+
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -2098,7 +2108,8 @@ public class AudioDeviceBroker {
private static final int MSG_CHECK_COMMUNICATION_ROUTE_CLIENT_STATE = 56;
private static final int MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES = 57;
- private static final int MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY = 58;
+ private static final int MSG_L_SYNCHRONIZE_ADI_DEVICES_IN_INVENTORY = 58;
+ private static final int MSG_L_UPDATED_ADI_DEVICE_STATE = 59;
@@ -2745,6 +2756,21 @@ public class AudioDeviceBroker {
return mDeviceInventory.findBtDeviceStateForAddress(address, deviceType);
}
+ void addAudioDeviceWithCategoryInInventoryIfNeeded(@NonNull String address,
+ @AudioDeviceCategory int btAudioDeviceCategory) {
+ mDeviceInventory.addAudioDeviceWithCategoryInInventoryIfNeeded(address,
+ btAudioDeviceCategory);
+ }
+
+ @AudioDeviceCategory
+ int getAndUpdateBtAdiDeviceStateCategoryForAddress(@NonNull String address) {
+ return mDeviceInventory.getAndUpdateBtAdiDeviceStateCategoryForAddress(address);
+ }
+
+ boolean isBluetoothAudioDeviceCategoryFixed(@NonNull String address) {
+ return mDeviceInventory.isBluetoothAudioDeviceCategoryFixed(address);
+ }
+
//------------------------------------------------
// for testing purposes only
void clearDeviceInventory() {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index e503f1f2c8c2..5499fd556c0a 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -15,16 +15,20 @@
*/
package com.android.server.audio;
+import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN;
import static android.media.AudioSystem.DEVICE_IN_ALL_SCO_SET;
import static android.media.AudioSystem.DEVICE_OUT_ALL_A2DP_SET;
import static android.media.AudioSystem.DEVICE_OUT_ALL_BLE_SET;
import static android.media.AudioSystem.DEVICE_OUT_ALL_SCO_SET;
+import static android.media.AudioSystem.DEVICE_OUT_BLE_HEADSET;
+import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
import static android.media.AudioSystem.isBluetoothA2dpOutDevice;
import static android.media.AudioSystem.isBluetoothDevice;
import static android.media.AudioSystem.isBluetoothLeOutDevice;
import static android.media.AudioSystem.isBluetoothOutDevice;
import static android.media.AudioSystem.isBluetoothScoOutDevice;
+import static android.media.audio.Flags.automaticBtDeviceType;
import android.annotation.NonNull;
@@ -39,6 +43,7 @@ import android.media.AudioDeviceInfo;
import android.media.AudioDevicePort;
import android.media.AudioFormat;
import android.media.AudioManager;
+import android.media.AudioManager.AudioDeviceCategory;
import android.media.AudioPort;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
@@ -82,6 +87,7 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;
/**
@@ -108,9 +114,11 @@ public class AudioDeviceInventory {
private final HashMap<Pair<Integer, String>, AdiDeviceState> mDeviceInventory = new HashMap<>();
Collection<AdiDeviceState> getImmutableDeviceInventory() {
+ final List<AdiDeviceState> newList;
synchronized (mDeviceInventoryLock) {
- return mDeviceInventory.values();
+ newList = new ArrayList<>(mDeviceInventory.values());
}
+ return newList;
}
/**
@@ -127,30 +135,43 @@ public class AudioDeviceInventory {
return oldState;
});
}
- mDeviceBroker.postSynchronizeLeDevicesInInventory(deviceState);
+ mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState);
}
/**
- * Adds a new entry in mDeviceInventory if the AudioDeviceAttributes passed is an sink
+ * Adds a new entry in mDeviceInventory if the attributes passed represent a sink
* Bluetooth device and no corresponding entry already exists.
- * @param ada the device to add if needed
+ *
+ * <p>This method will reconcile all BT devices connected with different profiles
+ * that share the same MAC address and will also synchronize the devices to their
+ * corresponding peers in case of BLE
*/
- void addAudioDeviceInInventoryIfNeeded(int deviceType, String address, String peerAddres) {
+ void addAudioDeviceInInventoryIfNeeded(int deviceType, String address, String peerAddress,
+ @AudioDeviceCategory int category) {
if (!isBluetoothOutDevice(deviceType)) {
return;
}
synchronized (mDeviceInventoryLock) {
AdiDeviceState ads = findBtDeviceStateForAddress(address, deviceType);
- if (ads == null) {
- ads = findBtDeviceStateForAddress(peerAddres, deviceType);
+ if (ads == null && peerAddress != null) {
+ ads = findBtDeviceStateForAddress(peerAddress, deviceType);
}
if (ads != null) {
- mDeviceBroker.postSynchronizeLeDevicesInInventory(ads);
+ if (ads.getAudioDeviceCategory() != category
+ && category != AUDIO_DEVICE_CATEGORY_UNKNOWN) {
+ ads.setAudioDeviceCategory(category);
+ mDeviceBroker.postUpdatedAdiDeviceState(ads);
+ mDeviceBroker.postPersistAudioDeviceSettings();
+ }
+ mDeviceBroker.postSynchronizeAdiDevicesInInventory(ads);
return;
}
ads = new AdiDeviceState(AudioDeviceInfo.convertInternalDeviceToDeviceType(deviceType),
deviceType, address);
+ ads.setAudioDeviceCategory(category);
+
mDeviceInventory.put(ads.getDeviceId(), ads);
+ mDeviceBroker.postUpdatedAdiDeviceState(ads);
mDeviceBroker.postPersistAudioDeviceSettings();
}
}
@@ -161,69 +182,160 @@ public class AudioDeviceInventory {
* @param deviceState the device to update
*/
void addOrUpdateAudioDeviceCategoryInInventory(AdiDeviceState deviceState) {
+ AtomicBoolean updatedCategory = new AtomicBoolean(false);
synchronized (mDeviceInventoryLock) {
- mDeviceInventory.merge(deviceState.getDeviceId(), deviceState, (oldState, newState) -> {
- oldState.setAudioDeviceCategory(newState.getAudioDeviceCategory());
- return oldState;
- });
+ if (automaticBtDeviceType()) {
+ if (deviceState.updateAudioDeviceCategory()) {
+ updatedCategory.set(true);
+ }
+ }
+ deviceState = mDeviceInventory.merge(deviceState.getDeviceId(),
+ deviceState, (oldState, newState) -> {
+ if (oldState.getAudioDeviceCategory()
+ != newState.getAudioDeviceCategory()) {
+ oldState.setAudioDeviceCategory(newState.getAudioDeviceCategory());
+ updatedCategory.set(true);
+ }
+ return oldState;
+ });
+ }
+ if (updatedCategory.get()) {
+ mDeviceBroker.postUpdatedAdiDeviceState(deviceState);
+ }
+ mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState);
+ }
+
+ void addAudioDeviceWithCategoryInInventoryIfNeeded(@NonNull String address,
+ @AudioDeviceCategory int btAudioDeviceCategory) {
+ addAudioDeviceInInventoryIfNeeded(DEVICE_OUT_BLE_HEADSET,
+ address, "", btAudioDeviceCategory);
+ addAudioDeviceInInventoryIfNeeded(DEVICE_OUT_BLUETOOTH_A2DP,
+ address, "", btAudioDeviceCategory);
+
+ }
+ @AudioDeviceCategory
+ int getAndUpdateBtAdiDeviceStateCategoryForAddress(@NonNull String address) {
+ int btCategory = AUDIO_DEVICE_CATEGORY_UNKNOWN;
+ boolean bleCategoryFound = false;
+ AdiDeviceState deviceState = findBtDeviceStateForAddress(address, DEVICE_OUT_BLE_HEADSET);
+ if (deviceState != null) {
+ addOrUpdateAudioDeviceCategoryInInventory(deviceState);
+ btCategory = deviceState.getAudioDeviceCategory();
+ bleCategoryFound = true;
}
- mDeviceBroker.postSynchronizeLeDevicesInInventory(deviceState);
+
+ deviceState = findBtDeviceStateForAddress(address, DEVICE_OUT_BLUETOOTH_A2DP);
+ if (deviceState != null) {
+ addOrUpdateAudioDeviceCategoryInInventory(deviceState);
+ int a2dpCategory = deviceState.getAudioDeviceCategory();
+ if (bleCategoryFound && a2dpCategory != btCategory) {
+ Log.w(TAG, "Found different audio device category for A2DP and BLE profiles with "
+ + "address " + address);
+ }
+ btCategory = a2dpCategory;
+ }
+
+ return btCategory;
+ }
+
+ boolean isBluetoothAudioDeviceCategoryFixed(@NonNull String address) {
+ AdiDeviceState deviceState = findBtDeviceStateForAddress(address, DEVICE_OUT_BLE_HEADSET);
+ if (deviceState != null) {
+ return deviceState.isBtDeviceCategoryFixed();
+ }
+
+ deviceState = findBtDeviceStateForAddress(address, DEVICE_OUT_BLUETOOTH_A2DP);
+ if (deviceState != null) {
+ return deviceState.isBtDeviceCategoryFixed();
+ }
+
+ return false;
}
/**
* synchronize AdiDeviceState for LE devices in the same group
*/
- void onSynchronizeLeDevicesInInventory(AdiDeviceState updatedDevice) {
+ void onSynchronizeAdiDevicesInInventory(AdiDeviceState updatedDevice) {
synchronized (mDevicesLock) {
synchronized (mDeviceInventoryLock) {
boolean found = false;
- for (DeviceInfo di : mConnectedDevices.values()) {
- if (di.mDeviceType != updatedDevice.getInternalDeviceType()) {
+ found |= synchronizeBleDeviceInInventory(updatedDevice);
+ if (automaticBtDeviceType()) {
+ found |= synchronizeDeviceProfilesInInventory(updatedDevice);
+ }
+ if (found) {
+ mDeviceBroker.postPersistAudioDeviceSettings();
+ }
+ }
+ }
+ }
+
+ @GuardedBy({"mDevicesLock", "mDeviceInventoryLock"})
+ private boolean synchronizeBleDeviceInInventory(AdiDeviceState updatedDevice) {
+ for (DeviceInfo di : mConnectedDevices.values()) {
+ if (di.mDeviceType != updatedDevice.getInternalDeviceType()) {
+ continue;
+ }
+ if (di.mDeviceAddress.equals(updatedDevice.getDeviceAddress())) {
+ for (AdiDeviceState ads2 : mDeviceInventory.values()) {
+ if (!(di.mDeviceType == ads2.getInternalDeviceType()
+ && di.mPeerDeviceAddress.equals(ads2.getDeviceAddress()))) {
continue;
}
- if (di.mDeviceAddress.equals(updatedDevice.getDeviceAddress())) {
- for (AdiDeviceState ads2 : mDeviceInventory.values()) {
- if (!(di.mDeviceType == ads2.getInternalDeviceType()
- && di.mPeerDeviceAddress.equals(ads2.getDeviceAddress()))) {
- continue;
- }
- ads2.setHasHeadTracker(updatedDevice.hasHeadTracker());
- ads2.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled());
- ads2.setSAEnabled(updatedDevice.isSAEnabled());
- ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
- found = true;
- AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "onSynchronizeLeDevicesInInventory synced device pair ads1="
+ ads2.setHasHeadTracker(updatedDevice.hasHeadTracker());
+ ads2.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled());
+ ads2.setSAEnabled(updatedDevice.isSAEnabled());
+ ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
+
+ mDeviceBroker.postUpdatedAdiDeviceState(ads2);
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "synchronizeBleDeviceInInventory synced device pair ads1="
+ updatedDevice + " ads2=" + ads2).printLog(TAG));
- break;
- }
+ return true;
+ }
+ }
+ if (di.mPeerDeviceAddress.equals(updatedDevice.getDeviceAddress())) {
+ for (AdiDeviceState ads2 : mDeviceInventory.values()) {
+ if (!(di.mDeviceType == ads2.getInternalDeviceType()
+ && di.mDeviceAddress.equals(ads2.getDeviceAddress()))) {
+ continue;
}
- if (di.mPeerDeviceAddress.equals(updatedDevice.getDeviceAddress())) {
- for (AdiDeviceState ads2 : mDeviceInventory.values()) {
- if (!(di.mDeviceType == ads2.getInternalDeviceType()
- && di.mDeviceAddress.equals(ads2.getDeviceAddress()))) {
- continue;
- }
- ads2.setHasHeadTracker(updatedDevice.hasHeadTracker());
- ads2.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled());
- ads2.setSAEnabled(updatedDevice.isSAEnabled());
- ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
- found = true;
- AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "onSynchronizeLeDevicesInInventory synced device pair ads1="
+ ads2.setHasHeadTracker(updatedDevice.hasHeadTracker());
+ ads2.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled());
+ ads2.setSAEnabled(updatedDevice.isSAEnabled());
+ ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
+
+ mDeviceBroker.postUpdatedAdiDeviceState(ads2);
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "synchronizeBleDeviceInInventory synced device pair ads1="
+ updatedDevice + " peer ads2=" + ads2).printLog(TAG));
- break;
- }
- }
- if (found) {
- break;
- }
- }
- if (found) {
- mDeviceBroker.postPersistAudioDeviceSettings();
+ return true;
}
}
}
+ return false;
+ }
+
+ @GuardedBy("mDeviceInventoryLock")
+ private boolean synchronizeDeviceProfilesInInventory(AdiDeviceState updatedDevice) {
+ for (AdiDeviceState ads : mDeviceInventory.values()) {
+ if (updatedDevice.getInternalDeviceType() == ads.getInternalDeviceType()
+ || !updatedDevice.getDeviceAddress().equals(ads.getDeviceAddress())) {
+ continue;
+ }
+
+ ads.setHasHeadTracker(updatedDevice.hasHeadTracker());
+ ads.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled());
+ ads.setSAEnabled(updatedDevice.isSAEnabled());
+ ads.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
+
+ mDeviceBroker.postUpdatedAdiDeviceState(ads);
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "synchronizeDeviceProfilesInInventory synced device pair ads1="
+ + updatedDevice + " ads2=" + ads).printLog(TAG));
+ return true;
+ }
+ return false;
}
/**
@@ -231,7 +343,7 @@ public class AudioDeviceInventory {
* returns a valid device for A2DP and BLE devices.
*
* @param address MAC address of BT device
- * @param isBle true if the device is BLE, false for A2DP
+ * @param deviceType internal device type to identify the BT device
* @return the found {@link AdiDeviceState} or {@code null} otherwise.
*/
@Nullable
@@ -1547,7 +1659,8 @@ public class AudioDeviceInventory {
if (!connect) {
purgeDevicesRoles_l();
} else {
- addAudioDeviceInInventoryIfNeeded(device, address, "");
+ addAudioDeviceInInventoryIfNeeded(device, address, "",
+ BtHelper.getBtDeviceCategory(address));
}
}
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
@@ -1829,7 +1942,9 @@ public class AudioDeviceInventory {
setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/);
- addAudioDeviceInInventoryIfNeeded(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, "");
+
+ addAudioDeviceInInventoryIfNeeded(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, "",
+ BtHelper.getBtDeviceCategory(address));
}
static final int[] CAPTURE_PRESETS = new int[] {AudioSource.MIC, AudioSource.CAMCORDER,
@@ -2149,7 +2264,8 @@ public class AudioDeviceInventory {
mDeviceBroker.postApplyVolumeOnDevice(streamType,
DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
setCurrentAudioRouteNameIfPossible(name, false /*fromA2dp*/);
- addAudioDeviceInInventoryIfNeeded(DEVICE_OUT_HEARING_AID, address, "");
+ addAudioDeviceInInventoryIfNeeded(DEVICE_OUT_HEARING_AID, address, "",
+ BtHelper.getBtDeviceCategory(address));
new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceAvailable")
.set(MediaMetrics.Property.ADDRESS, address != null ? address : "")
.set(MediaMetrics.Property.DEVICE,
@@ -2274,7 +2390,8 @@ public class AudioDeviceInventory {
peerAddress, groupId));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
- addAudioDeviceInInventoryIfNeeded(device, address, peerAddress);
+ addAudioDeviceInInventoryIfNeeded(device, address, peerAddress,
+ BtHelper.getBtDeviceCategory(address));
}
if (streamType == AudioSystem.STREAM_DEFAULT) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 4f6c6d61ee6f..f1496361fc60 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -19,6 +19,7 @@ package com.android.server.audio;
import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED;
import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT;
import static android.media.audio.Flags.autoPublicVolumeApiHardening;
+import static android.media.audio.Flags.automaticBtDeviceType;
import static android.media.audio.Flags.focusFreezeTestApi;
import static android.media.AudioDeviceInfo.TYPE_BLE_HEADSET;
import static android.media.AudioDeviceInfo.TYPE_BLE_SPEAKER;
@@ -11148,9 +11149,13 @@ public class AudioService extends IAudioService.Stub
@Override
@android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
- public void setBluetoothAudioDeviceCategory(@NonNull String address, boolean isBle,
+ public void setBluetoothAudioDeviceCategory_legacy(@NonNull String address, boolean isBle,
@AudioDeviceCategory int btAudioDeviceCategory) {
- super.setBluetoothAudioDeviceCategory_enforcePermission();
+ super.setBluetoothAudioDeviceCategory_legacy_enforcePermission();
+ if (automaticBtDeviceType()) {
+ // do nothing
+ return;
+ }
final String addr = Objects.requireNonNull(address);
@@ -11182,8 +11187,11 @@ public class AudioService extends IAudioService.Stub
@Override
@android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
@AudioDeviceCategory
- public int getBluetoothAudioDeviceCategory(@NonNull String address, boolean isBle) {
- super.getBluetoothAudioDeviceCategory_enforcePermission();
+ public int getBluetoothAudioDeviceCategory_legacy(@NonNull String address, boolean isBle) {
+ super.getBluetoothAudioDeviceCategory_legacy_enforcePermission();
+ if (automaticBtDeviceType()) {
+ return AUDIO_DEVICE_CATEGORY_UNKNOWN;
+ }
final AdiDeviceState deviceState = mDeviceBroker.findBtDeviceStateForAddress(
Objects.requireNonNull(address), (isBle ? AudioSystem.DEVICE_OUT_BLE_HEADSET
@@ -11195,6 +11203,61 @@ public class AudioService extends IAudioService.Stub
return deviceState.getAudioDeviceCategory();
}
+ @Override
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public boolean setBluetoothAudioDeviceCategory(@NonNull String address,
+ @AudioDeviceCategory int btAudioDeviceCategory) {
+ super.setBluetoothAudioDeviceCategory_enforcePermission();
+ if (!automaticBtDeviceType()) {
+ return false;
+ }
+
+ final String addr = Objects.requireNonNull(address);
+ if (isBluetoothAudioDeviceCategoryFixed(addr)) {
+ Log.w(TAG, "Cannot set fixed audio device type for address "
+ + Utils.anonymizeBluetoothAddress(address));
+ return false;
+ }
+
+ mDeviceBroker.addAudioDeviceWithCategoryInInventoryIfNeeded(address, btAudioDeviceCategory);
+
+ return true;
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ @AudioDeviceCategory
+ public int getBluetoothAudioDeviceCategory(@NonNull String address) {
+ super.getBluetoothAudioDeviceCategory_enforcePermission();
+ if (!automaticBtDeviceType()) {
+ return AUDIO_DEVICE_CATEGORY_UNKNOWN;
+ }
+
+ return mDeviceBroker.getAndUpdateBtAdiDeviceStateCategoryForAddress(address);
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ @AudioDeviceCategory
+ public boolean isBluetoothAudioDeviceCategoryFixed(@NonNull String address) {
+ super.isBluetoothAudioDeviceCategoryFixed_enforcePermission();
+ if (!automaticBtDeviceType()) {
+ return false;
+ }
+
+ return mDeviceBroker.isBluetoothAudioDeviceCategoryFixed(address);
+ }
+
+ /*package*/void onUpdatedAdiDeviceState(AdiDeviceState deviceState) {
+ if (deviceState == null) {
+ return;
+ }
+ mSpatializerHelper.refreshDevice(deviceState.getAudioDeviceAttributes());
+ mSoundDoseHelper.setAudioDeviceCategory(deviceState.getDeviceAddress(),
+ deviceState.getInternalDeviceType(),
+ deviceState.getAudioDeviceCategory() == AUDIO_DEVICE_CATEGORY_HEADPHONES);
+ }
+
//==========================================================================================
// Hdmi CEC:
// - System audio mode:
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index a078d08a2c8f..401dc88669ec 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -15,6 +15,22 @@
*/
package com.android.server.audio;
+import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_CARKIT;
+import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_DEFAULT;
+import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_HEADSET;
+import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_HEARING_AID;
+import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_SPEAKER;
+import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET;
+import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_WATCH;
+import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_CARKIT;
+import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES;
+import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEARING_AID;
+import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_RECEIVER;
+import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER;
+import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN;
+import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_WATCH;
+import static android.media.audio.Flags.automaticBtDeviceType;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothA2dp;
@@ -33,6 +49,7 @@ import android.content.Context;
import android.content.Intent;
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
+import android.media.AudioManager.AudioDeviceCategory;
import android.media.AudioSystem;
import android.media.BluetoothProfileConnectionInfo;
import android.os.Binder;
@@ -1115,6 +1132,71 @@ public class BtHelper {
return adapter.getPreferredAudioProfiles(adapter.getRemoteDevice(address));
}
+ @Nullable
+ /*package */ static BluetoothDevice getBluetoothDevice(String address) {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter == null || !BluetoothAdapter.checkBluetoothAddress(address)) {
+ return null;
+ }
+
+ return adapter.getRemoteDevice(address);
+ }
+
+ @AudioDeviceCategory
+ /*package*/ static int getBtDeviceCategory(String address) {
+ if (!automaticBtDeviceType()) {
+ return AUDIO_DEVICE_CATEGORY_UNKNOWN;
+ }
+
+ BluetoothDevice device = BtHelper.getBluetoothDevice(address);
+ if (device == null) {
+ return AUDIO_DEVICE_CATEGORY_UNKNOWN;
+ }
+
+ byte[] deviceType = device.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE);
+ if (deviceType == null) {
+ return AUDIO_DEVICE_CATEGORY_UNKNOWN;
+ }
+ String deviceCategory = new String(deviceType);
+ switch (deviceCategory) {
+ case DEVICE_TYPE_HEARING_AID:
+ return AUDIO_DEVICE_CATEGORY_HEARING_AID;
+ case DEVICE_TYPE_CARKIT:
+ return AUDIO_DEVICE_CATEGORY_CARKIT;
+ case DEVICE_TYPE_HEADSET:
+ case DEVICE_TYPE_UNTETHERED_HEADSET:
+ return AUDIO_DEVICE_CATEGORY_HEADPHONES;
+ case DEVICE_TYPE_SPEAKER:
+ return AUDIO_DEVICE_CATEGORY_SPEAKER;
+ case DEVICE_TYPE_WATCH:
+ return AUDIO_DEVICE_CATEGORY_WATCH;
+ case DEVICE_TYPE_DEFAULT:
+ default:
+ // fall through
+ }
+
+ BluetoothClass deviceClass = device.getBluetoothClass();
+ if (deviceClass == null) {
+ return AUDIO_DEVICE_CATEGORY_UNKNOWN;
+ }
+
+ switch (deviceClass.getDeviceClass()) {
+ case BluetoothClass.Device.WEARABLE_WRIST_WATCH:
+ return AUDIO_DEVICE_CATEGORY_WATCH;
+ case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
+ case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER:
+ case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
+ return AUDIO_DEVICE_CATEGORY_SPEAKER;
+ case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
+ case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
+ return AUDIO_DEVICE_CATEGORY_HEADPHONES;
+ case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
+ return AUDIO_DEVICE_CATEGORY_RECEIVER;
+ default:
+ return AUDIO_DEVICE_CATEGORY_UNKNOWN;
+ }
+ }
+
/**
* Notifies Bluetooth framework that new preferred audio profiles for Bluetooth devices
* have been applied.
diff --git a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
index bbe819f22e3a..9b0afc4282a2 100644
--- a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
+++ b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
@@ -26,10 +26,12 @@ import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_T
import static android.media.MediaFormat.KEY_AAC_DRC_EFFECT_TYPE;
import static android.media.MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION;
import static android.media.MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL;
+import static android.media.audio.Flags.automaticBtDeviceType;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.media.AudioDeviceInfo;
+import android.media.AudioManager.AudioDeviceCategory;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioSystem;
import android.media.ILoudnessCodecUpdatesDispatcher;
@@ -552,6 +554,13 @@ public class LoudnessCodecHelper {
@DeviceSplRange
private int getDeviceSplRange(AudioDeviceInfo deviceInfo) {
final int internalDeviceType = deviceInfo.getInternalType();
+ final @AudioDeviceCategory int deviceCategory;
+ if (automaticBtDeviceType()) {
+ deviceCategory = mAudioService.getBluetoothAudioDeviceCategory(deviceInfo.getAddress());
+ } else {
+ deviceCategory = mAudioService.getBluetoothAudioDeviceCategory_legacy(
+ deviceInfo.getAddress(), AudioSystem.isBluetoothLeDevice(internalDeviceType));
+ }
if (internalDeviceType == AudioSystem.DEVICE_OUT_SPEAKER) {
final String splRange = SystemProperties.get(
SYSTEM_PROPERTY_SPEAKER_SPL_RANGE_SIZE, "unknown");
@@ -569,18 +578,14 @@ public class LoudnessCodecHelper {
|| internalDeviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE
|| internalDeviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET
|| (AudioSystem.isBluetoothDevice(internalDeviceType)
- && mAudioService.getBluetoothAudioDeviceCategory(deviceInfo.getAddress(),
- AudioSystem.isBluetoothLeDevice(internalDeviceType))
- == AUDIO_DEVICE_CATEGORY_HEADPHONES)) {
+ && deviceCategory == AUDIO_DEVICE_CATEGORY_HEADPHONES)) {
return SPL_RANGE_LARGE;
} else if (AudioSystem.isBluetoothDevice(internalDeviceType)) {
- final int audioDeviceType = mAudioService.getBluetoothAudioDeviceCategory(
- deviceInfo.getAddress(), AudioSystem.isBluetoothLeDevice(internalDeviceType));
- if (audioDeviceType == AUDIO_DEVICE_CATEGORY_CARKIT) {
+ if (deviceCategory == AUDIO_DEVICE_CATEGORY_CARKIT) {
return SPL_RANGE_MEDIUM;
- } else if (audioDeviceType == AUDIO_DEVICE_CATEGORY_WATCH) {
+ } else if (deviceCategory == AUDIO_DEVICE_CATEGORY_WATCH) {
return SPL_RANGE_SMALL;
- } else if (audioDeviceType == AUDIO_DEVICE_CATEGORY_HEARING_AID) {
+ } else if (deviceCategory == AUDIO_DEVICE_CATEGORY_HEARING_AID) {
return SPL_RANGE_SMALL;
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a0bc7c27ff4a..98f627ce3c1a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -741,7 +741,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
*/
int mImeWindowVis;
- private LocaleList mLastSystemLocales;
private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
private final String mSlotIme;
@@ -1199,9 +1198,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (Intent.ACTION_USER_ADDED.equals(action)
|| Intent.ACTION_USER_REMOVED.equals(action)) {
updateCurrentProfileIds();
- return;
- } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
- onActionLocaleChanged();
} else {
Slog.w(TAG, "Unexpected intent " + intent);
}
@@ -1240,20 +1236,19 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
*
* <p>Note: For historical reasons, {@link Intent#ACTION_LOCALE_CHANGED} has been sent to all
* the users. We should ignore this event if this is about any background user's locale.</p>
- *
- * <p>Caution: This method must not be called when system is not ready.</p>
*/
- void onActionLocaleChanged() {
+ void onActionLocaleChanged(@NonNull LocaleList prevLocales, @NonNull LocaleList newLocales) {
+ if (DEBUG) {
+ Slog.d(TAG, "onActionLocaleChanged prev=" + prevLocales + " new=" + newLocales);
+ }
synchronized (ImfLock.class) {
- final LocaleList possibleNewLocale = mRes.getConfiguration().getLocales();
- if (possibleNewLocale != null && possibleNewLocale.equals(mLastSystemLocales)) {
+ if (!mSystemReady) {
return;
}
buildInputMethodListLocked(true);
// If the locale is changed, needs to reset the default ime
resetDefaultImeLocked(mContext);
updateFromSettingsLocked(true);
- mLastSystemLocales = possibleNewLocale;
}
}
@@ -1681,6 +1676,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
true /* allowIo */);
thread.start();
mHandler = Handler.createAsync(thread.getLooper(), this);
+ SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mHandler);
mImeTrackerService = new ImeTrackerService(serviceThreadForTesting != null
? serviceThreadForTesting.getLooper() : Looper.getMainLooper());
// Note: SettingsObserver doesn't register observers in its constructor.
@@ -1838,7 +1834,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// Even in such cases, IMMS works fine because it will find the most applicable
// IME for that user.
final boolean initialUserSwitch = TextUtils.isEmpty(defaultImiId);
- mLastSystemLocales = mRes.getConfiguration().getLocales();
// The mSystemReady flag is set during boot phase,
// and user switch would not happen at that time.
@@ -1890,7 +1885,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
if (!mSystemReady) {
mSystemReady = true;
- mLastSystemLocales = mRes.getConfiguration().getLocales();
final int currentUserId = mSettings.getCurrentUserId();
mSettings.switchCurrentUser(currentUserId,
!mUserManagerInternal.isUserUnlockingOrUnlocked(currentUserId));
@@ -1930,7 +1924,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final IntentFilter broadcastFilterForSystemUser = new IntentFilter();
broadcastFilterForSystemUser.addAction(Intent.ACTION_USER_ADDED);
broadcastFilterForSystemUser.addAction(Intent.ACTION_USER_REMOVED);
- broadcastFilterForSystemUser.addAction(Intent.ACTION_LOCALE_CHANGED);
mContext.registerReceiver(new ImmsBroadcastReceiverForSystemUser(),
broadcastFilterForSystemUser);
@@ -4073,14 +4066,19 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
if (enabled != null) {
final int enabledCount = enabled.size();
- final String locale = mCurrentSubtype == null
- ? mRes.getConfiguration().locale.toString()
- : mCurrentSubtype.getLocale();
+ final String locale;
+ if (mCurrentSubtype != null
+ && !TextUtils.isEmpty(mCurrentSubtype.getLocale())) {
+ locale = mCurrentSubtype.getLocale();
+ } else {
+ locale = SystemLocaleWrapper.get(mSettings.getCurrentUserId()).get(0)
+ .toString();
+ }
for (int i = 0; i < enabledCount; ++i) {
final InputMethodInfo imi = enabled.get(i);
if (imi.getSubtypeCount() > 0 && imi.isSystem()) {
InputMethodSubtype keyboardSubtype =
- SubtypeUtils.findLastResortApplicableSubtypeLocked(mRes,
+ SubtypeUtils.findLastResortApplicableSubtypeLocked(
SubtypeUtils.getSubtypes(imi),
SubtypeUtils.SUBTYPE_MODE_KEYBOARD, locale, true);
if (keyboardSubtype != null) {
@@ -5430,12 +5428,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0);
} else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) {
+ final String locale = SystemLocaleWrapper.get(mSettings.getCurrentUserId())
+ .get(0).toString();
mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtypeLocked(
- mRes, explicitlyOrImplicitlyEnabledSubtypes,
- SubtypeUtils.SUBTYPE_MODE_KEYBOARD, null, true);
+ explicitlyOrImplicitlyEnabledSubtypes,
+ SubtypeUtils.SUBTYPE_MODE_KEYBOARD, locale, true);
if (mCurrentSubtype == null) {
mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtypeLocked(
- mRes, explicitlyOrImplicitlyEnabledSubtypes, null, null, true);
+ explicitlyOrImplicitlyEnabledSubtypes, null, locale, true);
}
}
} else {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 984ae1f06711..c661c864b3ee 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -29,6 +29,7 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Build;
+import android.os.LocaleList;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -218,7 +219,6 @@ final class InputMethodUtils {
@NonNull
private Context mUserAwareContext;
- private Resources mRes;
private ContentResolver mResolver;
private final ArrayMap<String, InputMethodInfo> mMethodMap;
@@ -281,7 +281,6 @@ final class InputMethodUtils {
mUserAwareContext = context.getUserId() == userId
? context
: context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
- mRes = mUserAwareContext.getResources();
mResolver = mUserAwareContext.getContentResolver();
}
@@ -397,7 +396,8 @@ final class InputMethodUtils {
List<InputMethodSubtype> enabledSubtypes =
getEnabledInputMethodSubtypeListLocked(imi);
if (allowsImplicitlyEnabledSubtypes && enabledSubtypes.isEmpty()) {
- enabledSubtypes = SubtypeUtils.getImplicitlyApplicableSubtypesLocked(mRes, imi);
+ enabledSubtypes = SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
+ SystemLocaleWrapper.get(mCurrentUserId), imi);
}
return InputMethodSubtype.sort(imi, enabledSubtypes);
}
@@ -646,6 +646,7 @@ final class InputMethodUtils {
private String getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(List<Pair<String,
ArrayList<String>>> enabledImes, String imeId, String subtypeHashCode) {
+ final LocaleList localeList = SystemLocaleWrapper.get(mCurrentUserId);
for (Pair<String, ArrayList<String>> enabledIme: enabledImes) {
if (enabledIme.first.equals(imeId)) {
final ArrayList<String> explicitlyEnabledSubtypes = enabledIme.second;
@@ -657,7 +658,8 @@ final class InputMethodUtils {
// are enabled implicitly, so needs to treat them to be enabled.
if (imi != null && imi.getSubtypeCount() > 0) {
List<InputMethodSubtype> implicitlyEnabledSubtypes =
- SubtypeUtils.getImplicitlyApplicableSubtypesLocked(mRes, imi);
+ SubtypeUtils.getImplicitlyApplicableSubtypesLocked(localeList,
+ imi);
final int numSubtypes = implicitlyEnabledSubtypes.size();
for (int i = 0; i < numSubtypes; ++i) {
final InputMethodSubtype st = implicitlyEnabledSubtypes.get(i);
@@ -847,14 +849,15 @@ final class InputMethodUtils {
if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
return explicitlyOrImplicitlyEnabledSubtypes.get(0);
}
+ final String locale = SystemLocaleWrapper.get(mCurrentUserId).get(0).toString();
final InputMethodSubtype subtype = SubtypeUtils.findLastResortApplicableSubtypeLocked(
- mRes, explicitlyOrImplicitlyEnabledSubtypes, SubtypeUtils.SUBTYPE_MODE_KEYBOARD,
- null, true);
+ explicitlyOrImplicitlyEnabledSubtypes, SubtypeUtils.SUBTYPE_MODE_KEYBOARD,
+ locale, true);
if (subtype != null) {
return subtype;
}
- return SubtypeUtils.findLastResortApplicableSubtypeLocked(mRes,
- explicitlyOrImplicitlyEnabledSubtypes, null, null, true);
+ return SubtypeUtils.findLastResortApplicableSubtypeLocked(
+ explicitlyOrImplicitlyEnabledSubtypes, null, locale, true);
}
boolean setAdditionalInputMethodSubtypes(@NonNull String imeId,
diff --git a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
index 0185190521a3..95df99855dcf 100644
--- a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
+++ b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
@@ -18,7 +18,6 @@ package com.android.server.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.res.Resources;
import android.os.LocaleList;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -125,9 +124,7 @@ final class SubtypeUtils {
@VisibleForTesting
@NonNull
static ArrayList<InputMethodSubtype> getImplicitlyApplicableSubtypesLocked(
- Resources res, InputMethodInfo imi) {
- final LocaleList systemLocales = res.getConfiguration().getLocales();
-
+ @NonNull LocaleList systemLocales, InputMethodInfo imi) {
synchronized (sCacheLock) {
// We intentionally do not use InputMethodInfo#equals(InputMethodInfo) here because
// it does not check if subtypes are also identical.
@@ -140,7 +137,7 @@ final class SubtypeUtils {
// TODO: Refactor getImplicitlyApplicableSubtypesLockedImpl() so that it can receive
// LocaleList rather than Resource.
final ArrayList<InputMethodSubtype> result =
- getImplicitlyApplicableSubtypesLockedImpl(res, imi);
+ getImplicitlyApplicableSubtypesLockedImpl(systemLocales, imi);
synchronized (sCacheLock) {
// Both LocaleList and InputMethodInfo are immutable. No need to copy them here.
sCachedSystemLocales = systemLocales;
@@ -151,9 +148,8 @@ final class SubtypeUtils {
}
private static ArrayList<InputMethodSubtype> getImplicitlyApplicableSubtypesLockedImpl(
- Resources res, InputMethodInfo imi) {
+ @NonNull LocaleList systemLocales, InputMethodInfo imi) {
final List<InputMethodSubtype> subtypes = getSubtypes(imi);
- final LocaleList systemLocales = res.getConfiguration().getLocales();
final String systemLocale = systemLocales.get(0).toString();
if (TextUtils.isEmpty(systemLocale)) return new ArrayList<>();
final int numSubtypes = subtypes.size();
@@ -220,7 +216,7 @@ final class SubtypeUtils {
if (applicableSubtypes.isEmpty()) {
InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked(
- res, subtypes, SUBTYPE_MODE_KEYBOARD, systemLocale, true);
+ subtypes, SUBTYPE_MODE_KEYBOARD, systemLocale, true);
if (lastResortKeyboardSubtype != null) {
applicableSubtypes.add(lastResortKeyboardSubtype);
}
@@ -249,14 +245,11 @@ final class SubtypeUtils {
* @return the most applicable subtypeId
*/
static InputMethodSubtype findLastResortApplicableSubtypeLocked(
- Resources res, List<InputMethodSubtype> subtypes, String mode, String locale,
+ List<InputMethodSubtype> subtypes, String mode, @NonNull String locale,
boolean canIgnoreLocaleAsLastResort) {
if (subtypes == null || subtypes.isEmpty()) {
return null;
}
- if (TextUtils.isEmpty(locale)) {
- locale = res.getConfiguration().locale.toString();
- }
final String language = LocaleUtils.getLanguageFromLocaleString(locale);
boolean partialMatchFound = false;
InputMethodSubtype applicableSubtype = null;
diff --git a/services/core/java/com/android/server/inputmethod/SystemLocaleWrapper.java b/services/core/java/com/android/server/inputmethod/SystemLocaleWrapper.java
new file mode 100644
index 000000000000..0f1b7119f8ce
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/SystemLocaleWrapper.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 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.server.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.LocaleList;
+
+import java.util.Locale;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A set of thread-safe utility methods for the system locals.
+ */
+final class SystemLocaleWrapper {
+ /**
+ * Not intended to be instantiated.
+ */
+ private SystemLocaleWrapper() {
+ }
+
+ private static final AtomicReference<LocaleList> sSystemLocale =
+ new AtomicReference<>(new LocaleList(Locale.getDefault()));
+
+ /**
+ * Returns {@link LocaleList} for the specified user.
+ *
+ * <p>Note: If you call this method twice, it is possible that the second value is different
+ * from the first value. The caller is responsible for taking care of such cases.</p>
+ *
+ * @param userId the ID of the user to query about.
+ * @return {@link LocaleList} associated with the user.
+ */
+ @AnyThread
+ @NonNull
+ static LocaleList get(@UserIdInt int userId) {
+ // Currently system locale is not per-user.
+ // TODO(b/30119489): Make this per-user.
+ return sSystemLocale.get();
+ }
+
+ /**
+ * Callback for the locale change event. When this gets filed, {@link #get(int)} is already
+ * updated to return the new value.
+ */
+ interface Callback {
+ void onLocaleChanged(@NonNull LocaleList prevLocales, @NonNull LocaleList newLocales);
+ }
+
+ /**
+ * Called when {@link InputMethodManagerService} is about to start.
+ *
+ * @param context {@link Context} to be used.
+ * @param callback {@link Callback} for the locale change events.
+ */
+ @AnyThread
+ static void onStart(@NonNull Context context, @NonNull Callback callback,
+ @NonNull Handler handler) {
+ sSystemLocale.set(context.getResources().getConfiguration().getLocales());
+
+ context.registerReceiver(new LocaleChangeListener(context, callback),
+ new IntentFilter(Intent.ACTION_LOCALE_CHANGED), null, handler);
+ }
+
+ private static final class LocaleChangeListener extends BroadcastReceiver {
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final Callback mCallback;
+ LocaleChangeListener(@NonNull Context context, @NonNull Callback callback) {
+ mContext = context;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
+ return;
+ }
+ final LocaleList newLocales = mContext.getResources().getConfiguration().getLocales();
+ final LocaleList prevLocales = sSystemLocale.getAndSet(newLocales);
+ if (!Objects.equals(newLocales, prevLocales)) {
+ mCallback.onLocaleChanged(prevLocales, newLocales);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index e5c4ccc73bc3..b2d4a2ca1102 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1500,15 +1500,14 @@ public class ComputerEngine implements Computer {
state.getFirstInstallTimeMillis(), ps.getLastUpdateTime(), installedPermissions,
grantedPermissions, state, userId, ps);
- if (packageInfo == null) {
- return null;
+ if (packageInfo != null) {
+ packageInfo.packageName = packageInfo.applicationInfo.packageName =
+ resolveExternalPackageName(p);
+ return packageInfo;
}
-
- packageInfo.packageName = packageInfo.applicationInfo.packageName =
- resolveExternalPackageName(p);
-
- return packageInfo;
- } else if ((flags & (MATCH_UNINSTALLED_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0
+ }
+ // TODO(b/314808978): Set ps.setPkg to null during install-archived.
+ if ((flags & (MATCH_UNINSTALLED_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0
&& PackageUserStateUtils.isAvailable(state, flags)) {
PackageInfo pi = new PackageInfo();
pi.packageName = ps.getPackageName();
@@ -1540,9 +1539,8 @@ public class ComputerEngine implements Computer {
+ ps.getPackageName() + "]. Provides a minimum info.");
}
return pi;
- } else {
- return null;
}
+ return null;
}
public final PackageInfo getPackageInfo(String packageName,
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 1a6529732a02..3e7c8c405816 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2158,7 +2158,11 @@ final class InstallPackageHelper {
}
}
if (installRequest.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
- mPm.createArchiveStateIfNeeded(ps,
+ // If this is an archival installation then we'll initialize the archive status,
+ // while also marking package as not installed.
+ // Doing this at the very end of the install as we are using ps.getInstalled
+ // to figure out which users were changed.
+ mPm.markPackageAsArchivedIfNeeded(ps,
installRequest.getArchivedPackage(),
installRequest.getNewUsers());
mPm.updateSequenceNumberLP(ps, installRequest.getNewUsers());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5daada94d815..c0c98dedfae3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1518,8 +1518,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
return archPkg;
}
- void createArchiveStateIfNeeded(PackageSetting pkgSetting, ArchivedPackageParcel archivePackage,
- int[] userIds) {
+ void markPackageAsArchivedIfNeeded(PackageSetting pkgSetting,
+ ArchivedPackageParcel archivePackage, int[] userIds) {
if (pkgSetting == null || archivePackage == null
|| archivePackage.archivedActivities == null || userIds == null
|| userIds.length == 0) {
@@ -1541,6 +1541,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
pkgSetting
.modifyUserState(userId)
+ .setInstalled(false)
.setArchiveState(archiveState);
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 139312170c04..b53a21c9aa1c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -5137,12 +5137,6 @@ public class UserManagerService extends IUserManager.Stub {
mPm.createNewUser(userId, userTypeInstallablePackages, disallowedPackages);
t.traceEnd();
- userInfo.partial = false;
- synchronized (mPackagesLock) {
- writeUserLP(userData);
- }
- updateUserIds();
-
Bundle restrictions = new Bundle();
if (isGuest) {
// Guest default restrictions can be modified via setDefaultGuestRestrictions.
@@ -5160,6 +5154,12 @@ public class UserManagerService extends IUserManager.Stub {
mBaseUserRestrictions.updateRestrictions(userId, restrictions);
}
+ userInfo.partial = false;
+ synchronized (mPackagesLock) {
+ writeUserLP(userData);
+ }
+ updateUserIds();
+
t.traceBegin("PM.onNewUserCreated-" + userId);
mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false);
t.traceEnd();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 9610d051db95..d3931a303d0d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -278,8 +278,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private boolean setAutoRevokeExemptedInternal(@NonNull AndroidPackage pkg, boolean exempted,
@UserIdInt int userId) {
final int packageUid = UserHandle.getUid(userId, pkg.getUid());
+ final AttributionSource attributionSource =
+ new AttributionSource(packageUid, pkg.getPackageName(), null);
+
if (mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_AUTO_REVOKE_MANAGED_BY_INSTALLER,
- packageUid, pkg.getPackageName()) != MODE_ALLOWED) {
+ attributionSource) != MODE_ALLOWED) {
// Allowlist user set - don't override
return false;
}
@@ -330,8 +333,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final long identity = Binder.clearCallingIdentity();
try {
+ final AttributionSource attributionSource =
+ new AttributionSource(packageUid, packageName, null);
return mAppOpsManager.checkOpNoThrow(
- AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid, packageName)
+ AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, attributionSource)
== MODE_IGNORED;
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1157,9 +1162,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (resolvedPackageName == null) {
return;
}
+ final AttributionSource resolvedAccessorSource =
+ accessorSource.withPackageName(resolvedPackageName);
+
appOpsManager.finishOp(attributionSourceState.token, op,
- accessorSource.getUid(), resolvedPackageName,
- accessorSource.getAttributionTag());
+ resolvedAccessorSource);
} else {
final AttributionSource resolvedAttributionSource =
resolveAttributionSource(context, accessorSource);
@@ -1583,16 +1590,19 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (resolvedAccessorPackageName == null) {
return AppOpsManager.MODE_ERRORED;
}
+ final AttributionSource resolvedAttributionSource =
+ accessorSource.withPackageName(resolvedAccessorPackageName);
final int opMode = appOpsManager.unsafeCheckOpRawNoThrow(op,
- accessorSource.getUid(), resolvedAccessorPackageName);
+ resolvedAttributionSource);
final AttributionSource next = accessorSource.getNext();
if (!selfAccess && opMode == AppOpsManager.MODE_ALLOWED && next != null) {
final String resolvedNextPackageName = resolvePackageName(context, next);
if (resolvedNextPackageName == null) {
return AppOpsManager.MODE_ERRORED;
}
- return appOpsManager.unsafeCheckOpRawNoThrow(op, next.getUid(),
- resolvedNextPackageName);
+ final AttributionSource resolvedNextAttributionSource =
+ next.withPackageName(resolvedNextPackageName);
+ return appOpsManager.unsafeCheckOpRawNoThrow(op, resolvedNextAttributionSource);
}
return opMode;
} else if (startDataDelivery) {
@@ -1615,9 +1625,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// the operation. We return the less permissive of the two and check
// the permission op while start the attributed op.
if (attributedOp != AppOpsManager.OP_NONE && attributedOp != op) {
- checkedOpResult = appOpsManager.checkOpNoThrow(op,
- resolvedAttributionSource.getUid(), resolvedAttributionSource
- .getPackageName());
+ checkedOpResult = appOpsManager.checkOpNoThrow(op, resolvedAttributionSource);
if (checkedOpResult == MODE_ERRORED) {
return checkedOpResult;
}
@@ -1626,12 +1634,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (selfAccess) {
try {
startedOpResult = appOpsManager.startOpNoThrow(
- chainStartToken, startedOp,
- resolvedAttributionSource.getUid(),
- resolvedAttributionSource.getPackageName(),
- /*startIfModeDefault*/ false,
- resolvedAttributionSource.getAttributionTag(),
- message, proxyAttributionFlags, attributionChainId);
+ chainStartToken, startedOp, resolvedAttributionSource,
+ /*startIfModeDefault*/ false, message, proxyAttributionFlags,
+ attributionChainId);
} catch (SecurityException e) {
Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
+ " platform defined runtime permission "
@@ -1676,9 +1681,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// the operation. We return the less permissive of the two and check
// the permission op while start the attributed op.
if (attributedOp != AppOpsManager.OP_NONE && attributedOp != op) {
- checkedOpResult = appOpsManager.checkOpNoThrow(op,
- resolvedAttributionSource.getUid(), resolvedAttributionSource
- .getPackageName());
+ checkedOpResult = appOpsManager.checkOpNoThrow(op, resolvedAttributionSource);
if (checkedOpResult == MODE_ERRORED) {
return checkedOpResult;
}
@@ -1692,10 +1695,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// As a fallback we note a proxy op that blames the app and the datasource.
try {
notedOpResult = appOpsManager.noteOpNoThrow(notedOp,
- resolvedAttributionSource.getUid(),
- resolvedAttributionSource.getPackageName(),
- resolvedAttributionSource.getAttributionTag(),
- message);
+ resolvedAttributionSource, message);
} catch (SecurityException e) {
Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
+ " platform defined runtime permission "
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
index fe80f743ffc3..4b3992e63202 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
@@ -93,8 +93,8 @@ public class PackageUserStateUtils {
* this object exists means that the package must be installed or has data on at least one user;
* <li> If it is not installed but still has data (i.e., it was previously uninstalled with
* {@link PackageManager#DELETE_KEEP_DATA}), return true if the caller requested
- * {@link PackageManager#MATCH_UNINSTALLED_PACKAGES} or
- * {@link PackageManager#MATCH_ARCHIVED_PACKAGES};
+ * {@link PackageManager#MATCH_UNINSTALLED_PACKAGES}.
+ * Always available for {@link PackageManager#MATCH_ARCHIVED_PACKAGES}.
* </ul><p>
*/
public static boolean isAvailable(@NonNull PackageUserState state, long flags) {
@@ -109,11 +109,19 @@ public class PackageUserStateUtils {
if (state.isInstalled()) {
if (!state.isHidden()) {
return true;
- } else return matchDataExists;
- } else {
- // not installed
- return matchDataExists && state.dataExists();
+ } else {
+ return matchDataExists;
+ }
}
+
+ // not installed
+ if (matchUninstalled) {
+ return state.dataExists();
+ }
+
+ // archived or installed as archived
+ // TODO(b/314808978): Create data folders during install-archived.
+ return matchArchived;
}
public static boolean reportIfDebug(boolean result, long flags) {
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index b83421fe78d7..ecffd382f542 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -50,11 +50,11 @@ import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.DodecFunction;
+import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
-import com.android.internal.util.function.QuintConsumer;
-import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.LocalServices;
@@ -230,9 +230,10 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
@Override
public int checkOperation(int code, int uid, String packageName,
- @Nullable String attributionTag, boolean raw,
- QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl) {
- return superImpl.apply(code, resolveUid(code, uid), packageName, attributionTag, raw);
+ @Nullable String attributionTag, int virtualDeviceId, boolean raw,
+ HexFunction<Integer, Integer, String, String, Integer, Boolean, Integer> superImpl) {
+ return superImpl.apply(code, resolveUid(code, uid), packageName, attributionTag,
+ virtualDeviceId, raw);
}
@Override
@@ -243,12 +244,13 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
@Override
public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
- @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable
- String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer,
- String, String, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
+ @Nullable String attributionTag, int virtualDeviceId,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage, @NonNull OctFunction<Integer, Integer, String, String,
+ Integer, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag),
- resolveUid(code, uid), packageName, attributionTag, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage);
+ resolveUid(code, uid), packageName, attributionTag, virtualDeviceId,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
@Override
@@ -265,16 +267,16 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
@Override
public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
- @Nullable String packageName, @Nullable String attributionTag,
+ @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
- int attributionChainId, @NonNull UndecFunction<IBinder, Integer, Integer, String,
- String, Boolean, Boolean, String, Boolean, Integer, Integer,
- SyncNotedAppOp> superImpl) {
+ int attributionChainId, @NonNull DodecFunction<IBinder, Integer, Integer, String,
+ String, Integer, Boolean, Boolean, String, Boolean, Integer, Integer,
+ SyncNotedAppOp> superImpl) {
return superImpl.apply(token, resolveDatasourceOp(code, uid, packageName, attributionTag),
- resolveUid(code, uid), packageName, attributionTag, startIfModeDefault,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, attributionFlags,
- attributionChainId);
+ resolveUid(code, uid), packageName, attributionTag, virtualDeviceId,
+ startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ attributionFlags, attributionChainId);
}
@Override
@@ -294,10 +296,10 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
@Override
public void finishOperation(IBinder clientId, int code, int uid, String packageName,
- String attributionTag,
- @NonNull QuintConsumer<IBinder, Integer, Integer, String, String> superImpl) {
+ String attributionTag, int virtualDeviceId,
+ @NonNull HexConsumer<IBinder, Integer, Integer, String, String, Integer> superImpl) {
superImpl.accept(clientId, resolveDatasourceOp(code, uid, packageName, attributionTag),
- resolveUid(code, uid), packageName, attributionTag);
+ resolveUid(code, uid), packageName, attributionTag, virtualDeviceId);
}
@Override
diff --git a/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java b/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java
new file mode 100644
index 000000000000..b531b0eff854
--- /dev/null
+++ b/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2023 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.server.policy;
+
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.KeyEvent;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class that is responsible for queueing deferred key actions which can be triggered at a later
+ * time.
+ */
+class DeferredKeyActionExecutor {
+ private static final boolean DEBUG = PhoneWindowManager.DEBUG_INPUT;
+ private static final String TAG = "DeferredKeyAction";
+
+ private final SparseArray<TimedActionsBuffer> mBuffers = new SparseArray<>();
+
+ /**
+ * Queue a key action which can be triggered at a later time. Note that this method will also
+ * delete any outdated actions belong to the same key code.
+ *
+ * <p>Warning: the queued actions will only be cleaned up lazily when a new gesture downTime is
+ * recorded. If no new gesture downTime is recorded and the existing gesture is not executable,
+ * the actions will be kept in the buffer indefinitely. This may cause memory leak if the action
+ * itself holds references to temporary objects, or if too many actions are queued for the same
+ * gesture. The risk scales as you track more key codes. Please use this method with caution and
+ * ensure you only queue small amount of actions with limited size.
+ *
+ * <p>If you need to queue a large amount of actions with large size, there are several
+ * potential solutions to relief the memory leak risks:
+ *
+ * <p>1. Add a timeout (e.g. ANR timeout) based clean-up mechanism.
+ *
+ * <p>2. Clean-up queued actions when we know they won't be needed. E.g., add a callback when
+ * the gesture is handled by apps, and clean up queued actions associated with the handled
+ * gesture.
+ *
+ * @param keyCode the key code which triggers the action.
+ * @param downTime the down time of the key gesture. For multi-press actions, this is the down
+ * time of the last press. For long-press or very long-press actions, this is the initial
+ * down time.
+ * @param action the action that will be triggered at a later time.
+ */
+ public void queueKeyAction(int keyCode, long downTime, Runnable action) {
+ getActionsBufferWithLazyCleanUp(keyCode, downTime).addAction(action);
+ }
+
+ /**
+ * Make actions associated with the given key gesture executable. Actions already queued for the
+ * given gesture will be executed immediately. Any new actions belonging to this gesture will be
+ * executed as soon as they get queued. Note that this method will also delete any outdated
+ * actions belong to the same key code.
+ *
+ * @param keyCode the key code of the gesture.
+ * @param downTime the down time of the gesture.
+ */
+ public void setActionsExecutable(int keyCode, long downTime) {
+ getActionsBufferWithLazyCleanUp(keyCode, downTime).setExecutable();
+ }
+
+ private TimedActionsBuffer getActionsBufferWithLazyCleanUp(int keyCode, long downTime) {
+ TimedActionsBuffer buffer = mBuffers.get(keyCode);
+ if (buffer == null || buffer.getDownTime() != downTime) {
+ if (DEBUG && buffer != null) {
+ Log.d(
+ TAG,
+ "getActionsBufferWithLazyCleanUp: cleaning up gesture actions for key "
+ + KeyEvent.keyCodeToString(keyCode));
+ }
+ buffer = new TimedActionsBuffer(keyCode, downTime);
+ mBuffers.put(keyCode, buffer);
+ }
+ return buffer;
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "Deferred key action executor:");
+ if (mBuffers.size() == 0) {
+ pw.println(prefix + " empty");
+ return;
+ }
+ for (int i = 0; i < mBuffers.size(); i++) {
+ mBuffers.valueAt(i).dump(prefix, pw);
+ }
+ }
+
+ /** A buffer holding a gesture down time and its corresponding actions. */
+ private static class TimedActionsBuffer {
+ private final List<Runnable> mActions = new ArrayList<>();
+ private final int mKeyCode;
+ private final long mDownTime;
+ private boolean mExecutable;
+
+ TimedActionsBuffer(int keyCode, long downTime) {
+ mKeyCode = keyCode;
+ mDownTime = downTime;
+ }
+
+ long getDownTime() {
+ return mDownTime;
+ }
+
+ void addAction(Runnable action) {
+ if (mExecutable) {
+ if (DEBUG) {
+ Log.i(
+ TAG,
+ "addAction: execute action for key "
+ + KeyEvent.keyCodeToString(mKeyCode));
+ }
+ action.run();
+ return;
+ }
+ mActions.add(action);
+ }
+
+ void setExecutable() {
+ mExecutable = true;
+ if (DEBUG && !mActions.isEmpty()) {
+ Log.i(
+ TAG,
+ "setExecutable: execute actions for key "
+ + KeyEvent.keyCodeToString(mKeyCode));
+ }
+ for (Runnable action : mActions) {
+ action.run();
+ }
+ mActions.clear();
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ if (mExecutable) {
+ pw.println(prefix + " " + KeyEvent.keyCodeToString(mKeyCode) + ": executable");
+ } else {
+ pw.println(
+ prefix
+ + " "
+ + KeyEvent.keyCodeToString(mKeyCode)
+ + ": "
+ + mActions.size()
+ + " actions queued");
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
index c2666f63d7a6..bb5a697114d3 100644
--- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -206,22 +206,16 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
synchronized (mLock) {
ClientState clientState = mClients.get(listener.asBinder());
- if (clientState == null) {
- if (DEBUG) {
- Slog.w(TAG, "#cancel called with no preceding #startListening - ignoring.");
- }
- return;
+ if (clientState != null) {
+ clientState.mRecordingInProgress = false;
+ // Temporary reference to allow for resetting mDelegatingListener to null.
+ final IRecognitionListener delegatingListener = clientState.mDelegatingListener;
+ run(service -> service.cancel(delegatingListener, isShutdown));
}
- clientState.mRecordingInProgress = false;
-
- // Temporary reference to allow for resetting the hard link mDelegatingListener to null.
- final IRecognitionListener delegatingListener = clientState.mDelegatingListener;
- run(service -> service.cancel(delegatingListener, isShutdown));
// If shutdown, remove the client info from the map. Unbind if that was the last client.
if (isShutdown) {
removeClient(listener);
-
if (mClients.isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "Unbinding from the recognition service.");
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index a5123311d499..b271a03c109d 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -196,10 +196,10 @@ public interface StatusBarManagerInternal {
void hideToast(String packageName, IBinder token);
/**
- * @see com.android.internal.statusbar.IStatusBar#requestWindowMagnificationConnection(boolean
+ * @see com.android.internal.statusbar.IStatusBar#requestMagnificationConnection(boolean
* request)
*/
- boolean requestWindowMagnificationConnection(boolean request);
+ boolean requestMagnificationConnection(boolean request);
/**
* @see com.android.internal.statusbar.IStatusBar#setNavigationBarLumaSamplingEnabled(int,
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 7c51e7b84132..b21721ad7b45 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -760,11 +760,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public boolean requestWindowMagnificationConnection(boolean request) {
+ public boolean requestMagnificationConnection(boolean request) {
IStatusBar bar = mBar;
if (bar != null) {
try {
- bar.requestWindowMagnificationConnection(request);
+ bar.requestMagnificationConnection(request);
return true;
} catch (RemoteException ex) { }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
new file mode 100644
index 000000000000..2eeb903bb551
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023 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.server.vibrator;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.frameworks.vibrator.IVibratorControlService;
+import android.frameworks.vibrator.IVibratorController;
+import android.frameworks.vibrator.VibrationParam;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Objects;
+
+/**
+ * Implementation of {@link IVibratorControlService} which allows the registration of
+ * {@link IVibratorController} to set and receive vibration params.
+ *
+ * @hide
+ */
+public final class VibratorControlService extends IVibratorControlService.Stub {
+ private static final String TAG = "VibratorControlService";
+
+ private final VibratorControllerHolder mVibratorControllerHolder;
+ private final Object mLock;
+
+ public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, Object lock) {
+ mVibratorControllerHolder = vibratorControllerHolder;
+ mLock = lock;
+ }
+
+ @Override
+ public void registerVibratorController(IVibratorController controller)
+ throws RemoteException {
+ synchronized (mLock) {
+ mVibratorControllerHolder.setVibratorController(controller);
+ }
+ }
+
+ @Override
+ public void unregisterVibratorController(@NonNull IVibratorController controller)
+ throws RemoteException {
+ Objects.requireNonNull(controller);
+
+ synchronized (mLock) {
+ if (mVibratorControllerHolder.getVibratorController() == null) {
+ Slog.w(TAG, "Received request to unregister IVibratorController = "
+ + controller + ", but no controller was previously registered. Request "
+ + "Ignored.");
+ return;
+ }
+ if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(),
+ controller.asBinder())) {
+ Slog.wtf(TAG, "Failed to unregister IVibratorController. The provided "
+ + "controller doesn't match the registered one. " + this);
+ return;
+ }
+ mVibratorControllerHolder.setVibratorController(null);
+ }
+ }
+
+ @Override
+ public void setVibrationParams(
+ @SuppressLint("ArrayReturn") VibrationParam[] params, IVibratorController token)
+ throws RemoteException {
+ // TODO(b/305939964): Add set vibration implementation.
+ }
+
+ @Override
+ public void clearVibrationParams(int types, IVibratorController token) throws RemoteException {
+ // TODO(b/305939964): Add clear vibration implementation.
+ }
+
+ @Override
+ public void onRequestVibrationParamsComplete(
+ IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result)
+ throws RemoteException {
+ // TODO(305942827): Cache the vibration params in VibrationScaler
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return this.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return this.HASH;
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
new file mode 100644
index 000000000000..63e69db9480f
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 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.server.vibrator;
+
+import android.annotation.NonNull;
+import android.frameworks.vibrator.IVibratorController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Holder class for {@link IVibratorController}.
+ *
+ * @hide
+ */
+public final class VibratorControllerHolder implements IBinder.DeathRecipient {
+ private static final String TAG = "VibratorControllerHolder";
+
+ private IVibratorController mVibratorController;
+
+ public IVibratorController getVibratorController() {
+ return mVibratorController;
+ }
+
+ /**
+ * Sets the {@link IVibratorController} in {@link VibratorControllerHolder} to the new
+ * controller. This will also take care of registering and unregistering death notifications
+ * for the cached {@link IVibratorController}.
+ */
+ public void setVibratorController(IVibratorController controller) {
+ try {
+ if (mVibratorController != null) {
+ mVibratorController.asBinder().unlinkToDeath(this, 0);
+ }
+ mVibratorController = controller;
+ if (mVibratorController != null) {
+ mVibratorController.asBinder().linkToDeath(this, 0);
+ }
+ } catch (RemoteException e) {
+ Slog.wtf(TAG, "Failed to set IVibratorController: " + this, e);
+ }
+ }
+
+ @Override
+ public void binderDied(@NonNull IBinder deadBinder) {
+ if (deadBinder == mVibratorController.asBinder()) {
+ setVibratorController(null);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ // Should not be used as binderDied(IBinder who) is overridden.
+ Slog.wtf(TAG, "binderDied() called unexpectedly.");
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index cf33cc5f43bd..d5044d9bc660 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -53,6 +53,7 @@ import android.os.Trace;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
+import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.os.vibrator.VibratorInfoFactory;
@@ -87,10 +88,13 @@ import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
+
/** System implementation of {@link IVibratorManagerService}. */
public class VibratorManagerService extends IVibratorManagerService.Stub {
private static final String TAG = "VibratorManagerService";
private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
+ private static final String VIBRATOR_CONTROL_SERVICE =
+ "android.frameworks.vibrator.IVibratorControlService/default";
private static final boolean DEBUG = false;
private static final VibrationAttributes DEFAULT_ATTRIBUTES =
new VibrationAttributes.Builder().build();
@@ -269,6 +273,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
+ if (Flags.adaptiveHapticsEnabled()) {
+ injector.addService(VIBRATOR_CONTROL_SERVICE,
+ new VibratorControlService(new VibratorControllerHolder(), mLock));
+ }
}
/** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */
diff --git a/services/manifest_services.xml b/services/manifest_services.xml
index 76389154a885..e2fdfe9e8e47 100644
--- a/services/manifest_services.xml
+++ b/services/manifest_services.xml
@@ -4,4 +4,14 @@
<version>1</version>
<fqname>IAltitudeService/default</fqname>
</hal>
+ <hal format="aidl">
+ <name>android.frameworks.vibrator</name>
+ <version>1</version>
+ <fqname>IVibratorController/default</fqname>
+ </hal>
+ <hal format="aidl">
+ <name>android.frameworks.vibrator</name>
+ <version>1</version>
+ <fqname>IVibratorControlService/default</fqname>
+ </hal>
</manifest>
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 305569edd2fa..fd6aa0c1ffab 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -27,6 +27,7 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -47,10 +48,12 @@ import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
+import android.util.Xml;
import androidx.test.annotation.UiThreadTest;
import com.android.internal.widget.LockSettingsInternal;
+import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.server.LocalServices;
import com.android.server.am.UserState;
@@ -62,8 +65,12 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
/**
* Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceTest}
@@ -96,6 +103,12 @@ public final class UserManagerServiceTest {
*/
private static final int PROFILE_USER_ID = 643;
+ private static final String USER_INFO_DIR = "system" + File.separator + "users";
+
+ private static final String XML_SUFFIX = ".xml";
+
+ private static final String TAG_RESTRICTIONS = "restrictions";
+
@Rule
public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
.spyStatic(UserManager.class)
@@ -530,6 +543,48 @@ public final class UserManagerServiceTest {
assertThat(user1.name.length()).isEqualTo(4);
}
+ @Test
+ public void testDefaultRestrictionsArePersistedAfterCreateUser()
+ throws IOException, XmlPullParserException {
+ UserInfo user = mUms.createUserWithThrow("Test", USER_TYPE_FULL_SECONDARY, 0);
+ assertTrue(hasRestrictionsInUserXMLFile(user.id));
+ }
+
+ /**
+ * Returns true if the user's XML file has Default restrictions
+ * @param userId Id of the user.
+ */
+ private boolean hasRestrictionsInUserXMLFile(int userId)
+ throws IOException, XmlPullParserException {
+ FileInputStream is = new FileInputStream(getUserXmlFile(userId));
+ final TypedXmlPullParser parser = Xml.resolvePullParser(is);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ // Skip
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ return false;
+ }
+
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (TAG_RESTRICTIONS.equals(parser.getName())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private File getUserXmlFile(int userId) {
+ File file = new File(mTestDir, USER_INFO_DIR);
+ return new File(file, userId + XML_SUFFIX);
+ }
+
private String generateLongString() {
String partialString = "Test Name Test Name Test Name Test Name Test Name Test Name Test "
+ "Name Test Name Test Name Test Name "; //String of length 100
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 1b024982c41f..52726caba2bf 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
@@ -19,7 +19,7 @@ package com.android.server.accessibility.magnification;
import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
-import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY;
+import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
index 3843e2507df6..a7cf361c7bc1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
@@ -16,8 +16,8 @@
package com.android.server.accessibility.magnification;
-import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY;
-import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY_2;
+import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY;
+import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY_2;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -79,7 +79,7 @@ public class MagnificationConnectionManagerTest {
private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
private static final int SERVICE_ID = 1;
- private MockWindowMagnificationConnection mMockConnection;
+ private MockMagnificationConnection mMockConnection;
@Mock
private Context mContext;
@Mock
@@ -99,7 +99,7 @@ public class MagnificationConnectionManagerTest {
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
mResolver = new MockContentResolver();
- mMockConnection = new MockWindowMagnificationConnection();
+ mMockConnection = new MockMagnificationConnection();
mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(),
mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext));
@@ -128,7 +128,7 @@ public class MagnificationConnectionManagerTest {
connect ? mMockConnection.getConnection() : null);
}
return true;
- }).when(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(anyBoolean());
+ }).when(mMockStatusBarManagerInternal).requestMagnificationConnection(anyBoolean());
}
@Test
@@ -169,8 +169,7 @@ public class MagnificationConnectionManagerTest {
public void setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath()
throws RemoteException {
mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
- MockWindowMagnificationConnection secondConnection =
- new MockWindowMagnificationConnection();
+ MockMagnificationConnection secondConnection = new MockMagnificationConnection();
mMagnificationConnectionManager.setConnection(secondConnection.getConnection());
mMockConnection.getDeathRecipient().binderDied();
@@ -620,13 +619,13 @@ public class MagnificationConnectionManagerTest {
assertTrue(mMagnificationConnectionManager.requestConnection(false));
verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
- verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(false);
+ verify(mMockStatusBarManagerInternal).requestMagnificationConnection(false);
}
@Test
public void requestConnection_requestWindowMagnificationConnection() throws RemoteException {
assertTrue(mMagnificationConnectionManager.requestConnection(true));
- verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(true);
+ verify(mMockStatusBarManagerInternal).requestMagnificationConnection(true);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
index 8f85f11b7c49..8fdd884380d5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
@@ -24,8 +24,8 @@ import static org.mockito.Mockito.verify;
import android.os.RemoteException;
import android.provider.Settings;
import android.view.Display;
+import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
@@ -45,7 +45,7 @@ public class MagnificationConnectionWrapperTest {
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
- private IWindowMagnificationConnection mConnection;
+ private IMagnificationConnection mConnection;
@Mock
private AccessibilityTraceManager mTrace;
@Mock
@@ -53,14 +53,14 @@ public class MagnificationConnectionWrapperTest {
@Mock
private MagnificationAnimationCallback mAnimationCallback;
- private MockWindowMagnificationConnection mMockWindowMagnificationConnection;
+ private MockMagnificationConnection mMockMagnificationConnection;
private MagnificationConnectionWrapper mConnectionWrapper;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mMockWindowMagnificationConnection = new MockWindowMagnificationConnection();
- mConnection = mMockWindowMagnificationConnection.getConnection();
+ mMockMagnificationConnection = new MockMagnificationConnection();
+ mConnection = mMockMagnificationConnection.getConnection();
mConnectionWrapper = new MagnificationConnectionWrapper(mConnection, mTrace);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index e8cdf35dee13..28d07f995ad3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -130,7 +130,7 @@ public class MagnificationControllerTest {
@Captor
private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor;
- private MockWindowMagnificationConnection mMockConnection;
+ private MockMagnificationConnection mMockConnection;
private MagnificationConnectionManager mMagnificationConnectionManager;
private MockContentResolver mMockResolver;
private MagnificationController mMagnificationController;
@@ -208,7 +208,7 @@ public class MagnificationControllerTest {
mMagnificationConnectionManager = spy(
new MagnificationConnectionManager(mContext, globalLock,
mWindowMagnificationCallbackDelegate, mTraceManager, mScaleProvider));
- mMockConnection = new MockWindowMagnificationConnection(true);
+ mMockConnection = new MockMagnificationConnection(true);
mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMagnificationController = spy(new MagnificationController(mService, globalLock, mContext,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockMagnificationConnection.java
index 4c03ec34f074..3d3d0b7aa07a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockMagnificationConnection.java
@@ -31,8 +31,8 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.Display;
+import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import java.util.ArrayList;
@@ -42,12 +42,12 @@ import java.util.List;
* Mocks the basic logic of window magnification in System UI. We assume the screen size is
* unlimited, so source bounds is always on the center of the mirror window bounds.
*/
-class MockWindowMagnificationConnection {
+class MockMagnificationConnection {
public static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
public static final int TEST_DISPLAY_2 = Display.DEFAULT_DISPLAY + 1;
private final List mValidDisplayIds;
- private final IWindowMagnificationConnection mConnection;
+ private final IMagnificationConnection mConnection;
private final Binder mBinder;
private final boolean mSuspendCallback;
private boolean mHasPendingCallback = false;
@@ -60,17 +60,17 @@ class MockWindowMagnificationConnection {
private Rect mSourceBounds = new Rect();
private IRemoteMagnificationAnimationCallback mAnimationCallback;
- MockWindowMagnificationConnection() throws RemoteException {
+ MockMagnificationConnection() throws RemoteException {
this(false);
}
- MockWindowMagnificationConnection(boolean suspendCallback) throws RemoteException {
+ MockMagnificationConnection(boolean suspendCallback) throws RemoteException {
mValidDisplayIds = new ArrayList();
mValidDisplayIds.add(TEST_DISPLAY);
mValidDisplayIds.add(TEST_DISPLAY_2);
mSuspendCallback = suspendCallback;
- mConnection = mock(IWindowMagnificationConnection.class);
+ mConnection = mock(IMagnificationConnection.class);
mBinder = mock(Binder.class);
when(mConnection.asBinder()).thenReturn(mBinder);
doAnswer((invocation) -> {
@@ -154,7 +154,7 @@ class MockWindowMagnificationConnection {
}
}
- IWindowMagnificationConnection getConnection() {
+ IMagnificationConnection getConnection() {
return mConnection;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index c4be51f9ecbd..a3b67aef551a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -86,14 +86,14 @@ public class WindowMagnificationGestureHandlerTest {
public static final float DEFAULT_TAP_X = 301;
public static final float DEFAULT_TAP_Y = 299;
public static final PointF DEFAULT_POINT = new PointF(DEFAULT_TAP_X, DEFAULT_TAP_Y);
- private static final int DISPLAY_0 = MockWindowMagnificationConnection.TEST_DISPLAY;
+ private static final int DISPLAY_0 = MockMagnificationConnection.TEST_DISPLAY;
@Rule
public final TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
private MagnificationConnectionManager mMagnificationConnectionManager;
- private MockWindowMagnificationConnection mMockConnection;
+ private MockMagnificationConnection mMockConnection;
private SpyWindowMagnificationGestureHandler mWindowMagnificationGestureHandler;
private WindowMagnificationGestureHandler mMockWindowMagnificationGestureHandler;
@Mock
@@ -107,7 +107,7 @@ public class WindowMagnificationGestureHandlerTest {
mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(),
mock(MagnificationConnectionManager.Callback.class), mMockTrace,
new MagnificationScaleProvider(mContext));
- mMockConnection = new MockWindowMagnificationConnection();
+ mMockConnection = new MockMagnificationConnection();
mWindowMagnificationGestureHandler = new SpyWindowMagnificationGestureHandler(
mContext, mMagnificationConnectionManager, mMockTrace, mMockCallback,
/** detectSingleFingerTripleTap= */ true, /** detectTwoFingerTripleTap= */ true,
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
index b732d38aefe7..33559107dfbb 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -26,10 +26,12 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.after;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -70,6 +72,7 @@ import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class GenericWindowPolicyControllerTest {
+ private static final int TIMEOUT_MILLIS = 500;
private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY + 1;
private static final int TEST_UID = 1234567;
private static final String DISPLAY_CATEGORY = "com.display.category";
@@ -134,7 +137,7 @@ public class GenericWindowPolicyControllerTest {
GenericWindowPolicyController gwpc = createGwpc();
assertThat(gwpc.isEnteringPipAllowed(TEST_UID)).isFalse();
- verify(mPipBlockedCallback).onEnteringPipBlocked(TEST_UID);
+ verify(mPipBlockedCallback, timeout(TIMEOUT_MILLIS)).onEnteringPipBlocked(TEST_UID);
}
@Test
@@ -144,7 +147,7 @@ public class GenericWindowPolicyControllerTest {
Arrays.asList(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
WindowConfiguration.WINDOWING_MODE_PINNED)));
assertThat(gwpc.isEnteringPipAllowed(TEST_UID)).isTrue();
- verify(mPipBlockedCallback, never()).onEnteringPipBlocked(TEST_UID);
+ verify(mPipBlockedCallback, after(TIMEOUT_MILLIS).never()).onEnteringPipBlocked(TEST_UID);
}
@Test
@@ -496,7 +499,7 @@ public class GenericWindowPolicyControllerTest {
gwpc.onRunningAppsChanged(uids);
assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(1);
- verify(mRunningAppsChangedListener).onRunningAppsChanged(uids);
+ verify(mRunningAppsChangedListener, timeout(TIMEOUT_MILLIS)).onRunningAppsChanged(uids);
}
@Test
@@ -508,7 +511,7 @@ public class GenericWindowPolicyControllerTest {
gwpc.onRunningAppsChanged(uids);
assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0);
- verify(mActivityListener).onDisplayEmpty(DISPLAY_ID);
+ verify(mActivityListener, timeout(TIMEOUT_MILLIS)).onDisplayEmpty(DISPLAY_ID);
}
@Test
@@ -519,7 +522,8 @@ public class GenericWindowPolicyControllerTest {
gwpc.onRunningAppsChanged(uids);
assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0);
- verify(mRunningAppsChangedListener, never()).onRunningAppsChanged(uids);
+ verify(mRunningAppsChangedListener, after(TIMEOUT_MILLIS).never())
+ .onRunningAppsChanged(uids);
}
@Test
@@ -532,7 +536,8 @@ public class GenericWindowPolicyControllerTest {
gwpc.onRunningAppsChanged(uids);
assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0);
- verify(mRunningAppsChangedListener, never()).onRunningAppsChanged(uids);
+ verify(mRunningAppsChangedListener, after(TIMEOUT_MILLIS).never())
+ .onRunningAppsChanged(uids);
}
@Test
@@ -582,7 +587,8 @@ public class GenericWindowPolicyControllerTest {
assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
.isTrue();
- verify(mIntentListenerCallback).shouldInterceptIntent(any(Intent.class));
+ verify(mIntentListenerCallback, timeout(TIMEOUT_MILLIS))
+ .shouldInterceptIntent(any(Intent.class));
}
@Test
@@ -590,7 +596,7 @@ public class GenericWindowPolicyControllerTest {
GenericWindowPolicyController gwpc = createGwpc();
gwpc.onTopActivityChanged(null, 0, 0);
- verify(mActivityListener, never())
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
.onTopActivityChanged(anyInt(), any(ComponentName.class), anyInt());
}
@@ -601,7 +607,7 @@ public class GenericWindowPolicyControllerTest {
gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
gwpc.onTopActivityChanged(BLOCKED_COMPONENT, 0, userId);
- verify(mActivityListener)
+ verify(mActivityListener, timeout(TIMEOUT_MILLIS))
.onTopActivityChanged(eq(DISPLAY_ID), eq(BLOCKED_COMPONENT), eq(userId));
}
@@ -618,8 +624,8 @@ public class GenericWindowPolicyControllerTest {
assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0, 0)).isTrue();
- verify(mSecureWindowCallback, never()).onSecureWindowShown(DISPLAY_ID,
- activityInfo.applicationInfo.uid);
+ verify(mSecureWindowCallback, after(TIMEOUT_MILLIS).never())
+ .onSecureWindowShown(DISPLAY_ID, activityInfo.applicationInfo.uid);
verify(mActivityBlockedCallback, never()).onActivityBlocked(DISPLAY_ID, activityInfo);
}
@@ -636,9 +642,10 @@ public class GenericWindowPolicyControllerTest {
assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, FLAG_SECURE, 0)).isTrue();
- verify(mSecureWindowCallback).onSecureWindowShown(DISPLAY_ID,
+ verify(mSecureWindowCallback, timeout(TIMEOUT_MILLIS)).onSecureWindowShown(DISPLAY_ID,
activityInfo.applicationInfo.uid);
- verify(mActivityBlockedCallback, never()).onActivityBlocked(DISPLAY_ID, activityInfo);
+ verify(mActivityBlockedCallback, after(TIMEOUT_MILLIS).never())
+ .onActivityBlocked(DISPLAY_ID, activityInfo);
}
@Test
@@ -655,8 +662,8 @@ public class GenericWindowPolicyControllerTest {
assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)).isTrue();
- verify(mSecureWindowCallback, never()).onSecureWindowShown(DISPLAY_ID,
- activityInfo.applicationInfo.uid);
+ verify(mSecureWindowCallback, after(TIMEOUT_MILLIS).never())
+ .onSecureWindowShown(DISPLAY_ID, activityInfo.applicationInfo.uid);
verify(mActivityBlockedCallback, never()).onActivityBlocked(DISPLAY_ID, activityInfo);
}
@@ -882,7 +889,8 @@ public class GenericWindowPolicyControllerTest {
assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay,
isNewTask)).isTrue();
- verify(mActivityBlockedCallback, never()).onActivityBlocked(fromDisplay, activityInfo);
+ verify(mActivityBlockedCallback, after(TIMEOUT_MILLIS).never())
+ .onActivityBlocked(fromDisplay, activityInfo);
verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
}
@@ -897,8 +905,10 @@ public class GenericWindowPolicyControllerTest {
assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay,
isNewTask)).isFalse();
- verify(mActivityBlockedCallback).onActivityBlocked(fromDisplay, activityInfo);
- verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
+ verify(mActivityBlockedCallback, timeout(TIMEOUT_MILLIS))
+ .onActivityBlocked(fromDisplay, activityInfo);
+ verify(mIntentListenerCallback, after(TIMEOUT_MILLIS).never())
+ .shouldInterceptIntent(any(Intent.class));
}
private void assertNoActivityLaunched(GenericWindowPolicyController gwpc, int fromDisplay,
@@ -907,7 +917,8 @@ public class GenericWindowPolicyControllerTest {
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, true))
.isFalse();
- verify(mActivityBlockedCallback, never()).onActivityBlocked(fromDisplay, activityInfo);
+ verify(mActivityBlockedCallback, after(TIMEOUT_MILLIS).never())
+ .onActivityBlocked(fromDisplay, activityInfo);
verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
index 4b318de78827..37a1a411c703 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
@@ -287,7 +287,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_EN_US), imi);
+ new LocaleList(LOCALE_EN_US), imi);
assertEquals(1, result.size());
verifyEquality(autoSubtype, result.get(0));
}
@@ -311,7 +311,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_EN_US), imi);
+ new LocaleList(LOCALE_EN_US), imi);
assertEquals(2, result.size());
verifyEquality(nonAutoEnUS, result.get(0));
verifyEquality(nonAutoHandwritingEn, result.get(1));
@@ -335,7 +335,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_EN_GB), imi);
+ new LocaleList(LOCALE_EN_GB), imi);
assertEquals(2, result.size());
verifyEquality(nonAutoEnGB, result.get(0));
verifyEquality(nonAutoHandwritingEn, result.get(1));
@@ -360,7 +360,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_FR), imi);
+ new LocaleList(LOCALE_FR), imi);
assertEquals(2, result.size());
verifyEquality(nonAutoFrCA, result.get(0));
verifyEquality(nonAutoHandwritingFr, result.get(1));
@@ -381,7 +381,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_FR_CA), imi);
+ new LocaleList(LOCALE_FR_CA), imi);
assertEquals(2, result.size());
verifyEquality(nonAutoFrCA, result.get(0));
verifyEquality(nonAutoHandwritingFr, result.get(1));
@@ -403,7 +403,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_JA_JP), imi);
+ new LocaleList(LOCALE_JA_JP), imi);
assertEquals(3, result.size());
verifyEquality(nonAutoJa, result.get(0));
verifyEquality(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, result.get(1));
@@ -425,7 +425,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_JA_JP), imi);
+ new LocaleList(LOCALE_JA_JP), imi);
assertEquals(1, result.size());
verifyEquality(nonAutoHi, result.get(0));
}
@@ -442,7 +442,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_JA_JP), imi);
+ new LocaleList(LOCALE_JA_JP), imi);
assertEquals(1, result.size());
verifyEquality(nonAutoEnUS, result.get(0));
}
@@ -459,7 +459,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_JA_JP), imi);
+ new LocaleList(LOCALE_JA_JP), imi);
assertEquals(1, result.size());
verifyEquality(nonAutoEnUS, result.get(0));
}
@@ -481,7 +481,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(Locale.forLanguageTag("sr-Latn-RS")), imi);
+ new LocaleList(Locale.forLanguageTag("sr-Latn-RS")), imi);
assertEquals(2, result.size());
assertThat(nonAutoSrLatn, is(in(result)));
assertThat(nonAutoHandwritingSrLatn, is(in(result)));
@@ -501,7 +501,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(Locale.forLanguageTag("sr-Cyrl-RS")), imi);
+ new LocaleList(Locale.forLanguageTag("sr-Cyrl-RS")), imi);
assertEquals(2, result.size());
assertThat(nonAutoSrCyrl, is(in(result)));
assertThat(nonAutoHandwritingSrCyrl, is(in(result)));
@@ -527,7 +527,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(
+ new LocaleList(
Locale.forLanguageTag("sr-Latn-RS-x-android"),
Locale.forLanguageTag("ja-JP"),
Locale.forLanguageTag("fr-FR"),
@@ -554,7 +554,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_FIL_PH), imi);
+ new LocaleList(LOCALE_FIL_PH), imi);
assertEquals(1, result.size());
verifyEquality(nonAutoFil, result.get(0));
}
@@ -572,7 +572,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_FI), imi);
+ new LocaleList(LOCALE_FI), imi);
assertEquals(1, result.size());
verifyEquality(nonAutoJa, result.get(0));
}
@@ -588,7 +588,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_IN), imi);
+ new LocaleList(LOCALE_IN), imi);
assertEquals(1, result.size());
verifyEquality(nonAutoIn, result.get(0));
}
@@ -602,7 +602,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_ID), imi);
+ new LocaleList(LOCALE_ID), imi);
assertEquals(1, result.size());
verifyEquality(nonAutoIn, result.get(0));
}
@@ -616,7 +616,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_IN), imi);
+ new LocaleList(LOCALE_IN), imi);
assertEquals(1, result.size());
verifyEquality(nonAutoId, result.get(0));
}
@@ -630,7 +630,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_ID), imi);
+ new LocaleList(LOCALE_ID), imi);
assertEquals(1, result.size());
verifyEquality(nonAutoId, result.get(0));
}
@@ -652,7 +652,7 @@ public class InputMethodUtilsTest {
subtypes);
final ArrayList<InputMethodSubtype> result =
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- getResourcesForLocales(LOCALE_FR, LOCALE_EN_US, LOCALE_JA_JP), imi);
+ new LocaleList(LOCALE_FR, LOCALE_EN_US, LOCALE_JA_JP), imi);
assertThat(nonAutoFrCA, is(in(result)));
assertThat(nonAutoEnUS, is(in(result)));
assertThat(nonAutoJa, is(in(result)));
@@ -940,10 +940,6 @@ public class InputMethodUtilsTest {
.createConfigurationContext(resourceConfiguration);
}
- private Resources getResourcesForLocales(Locale... locales) {
- return createTargetContextWithLocales(new LocaleList(locales)).getResources();
- }
-
private String[] getPackageNames(final ArrayList<InputMethodInfo> imis) {
final String[] packageNames = new String[imis.size()];
for (int i = 0; i < imis.size(); ++i) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
new file mode 100644
index 000000000000..49efd1bdd92a
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class VibratorControlServiceTest {
+
+ private VibratorControlService mVibratorControlService;
+ private final Object mLock = new Object();
+
+ @Before
+ public void setUp() throws Exception {
+ mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(), mLock);
+ }
+
+ @Test
+ public void testRegisterVibratorController() throws RemoteException {
+ FakeVibratorController fakeController = new FakeVibratorController();
+ mVibratorControlService.registerVibratorController(fakeController);
+
+ assertThat(fakeController.isLinkedToDeath).isTrue();
+ }
+
+ @Test
+ public void testUnregisterVibratorController_providingTheRegisteredController_performsRequest()
+ throws RemoteException {
+ FakeVibratorController fakeController = new FakeVibratorController();
+ mVibratorControlService.registerVibratorController(fakeController);
+ mVibratorControlService.unregisterVibratorController(fakeController);
+ assertThat(fakeController.isLinkedToDeath).isFalse();
+ }
+
+ @Test
+ public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest()
+ throws RemoteException {
+ FakeVibratorController fakeController1 = new FakeVibratorController();
+ FakeVibratorController fakeController2 = new FakeVibratorController();
+ mVibratorControlService.registerVibratorController(fakeController1);
+
+ mVibratorControlService.unregisterVibratorController(fakeController2);
+ assertThat(fakeController1.isLinkedToDeath).isTrue();
+ }
+}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java
new file mode 100644
index 000000000000..79abe21a301d
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class VibratorControllerHolderTest {
+
+ private final FakeVibratorController mFakeVibratorController = new FakeVibratorController();
+ private VibratorControllerHolder mVibratorControllerHolder;
+
+ @Before
+ public void setUp() throws Exception {
+ mVibratorControllerHolder = new VibratorControllerHolder();
+ }
+
+ @Test
+ public void testSetVibratorController_linksVibratorControllerToDeath() throws RemoteException {
+ mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
+ assertThat(mVibratorControllerHolder.getVibratorController())
+ .isEqualTo(mFakeVibratorController);
+ assertThat(mFakeVibratorController.isLinkedToDeath).isTrue();
+ }
+
+ @Test
+ public void testSetVibratorController_setControllerToNull_unlinksVibratorControllerToDeath()
+ throws RemoteException {
+ mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
+ mVibratorControllerHolder.setVibratorController(null);
+ assertThat(mFakeVibratorController.isLinkedToDeath).isFalse();
+ assertThat(mVibratorControllerHolder.getVibratorController()).isNull();
+ }
+
+ @Test
+ public void testBinderDied_withValidController_unlinksVibratorControllerToDeath()
+ throws RemoteException {
+ mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
+ mVibratorControllerHolder.binderDied(mFakeVibratorController);
+ assertThat(mFakeVibratorController.isLinkedToDeath).isFalse();
+ assertThat(mVibratorControllerHolder.getVibratorController()).isNull();
+ }
+
+ @Test
+ public void testBinderDied_withInvalidController_ignoresRequest()
+ throws RemoteException {
+ mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
+ FakeVibratorController imposterVibratorController = new FakeVibratorController();
+ mVibratorControllerHolder.binderDied(imposterVibratorController);
+ assertThat(mFakeVibratorController.isLinkedToDeath).isTrue();
+ assertThat(mVibratorControllerHolder.getVibratorController())
+ .isEqualTo(mFakeVibratorController);
+ }
+}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 3fce9e7a83ef..a105649c9b5b 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -307,9 +307,10 @@ public class VibratorManagerServiceTest {
@Override
void addService(String name, IBinder service) {
- Object serviceInstance = service;
- mExternalVibratorService =
- (VibratorManagerService.ExternalVibratorService) serviceInstance;
+ if (service instanceof VibratorManagerService.ExternalVibratorService) {
+ mExternalVibratorService =
+ (VibratorManagerService.ExternalVibratorService) service;
+ }
}
HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
new file mode 100644
index 000000000000..7e235870cedc
--- /dev/null
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
@@ -0,0 +1,58 @@
+/*
+ * 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.server.vibrator;
+
+import android.annotation.NonNull;
+import android.frameworks.vibrator.IVibratorController;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * Provides a fake implementation of {@link android.frameworks.vibrator.IVibratorController} for
+ * testing.
+ */
+public final class FakeVibratorController extends IVibratorController.Stub {
+
+ public boolean isLinkedToDeath = false;
+
+ @Override
+ public void requestVibrationParams(int i, long l, IBinder iBinder) throws RemoteException {
+
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
+ super.linkToDeath(recipient, flags);
+ isLinkedToDeath = true;
+ }
+
+ @Override
+ public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
+ isLinkedToDeath = false;
+ return super.unlinkToDeath(recipient, flags);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
new file mode 100644
index 000000000000..d2ef1808652f
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 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.server.policy;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.view.KeyEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test class for {@link DeferredKeyActionExecutor}.
+ *
+ * <p>Build/Install/Run: atest WmTests:DeferredKeyActionExecutorTests
+ */
+public final class DeferredKeyActionExecutorTests {
+
+ private DeferredKeyActionExecutor mKeyActionExecutor;
+
+ @Before
+ public void setUp() {
+ mKeyActionExecutor = new DeferredKeyActionExecutor();
+ }
+
+ @Test
+ public void queueKeyAction_actionNotExecuted() {
+ TestAction action = new TestAction();
+
+ mKeyActionExecutor.queueKeyAction(KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action);
+
+ assertFalse(action.executed);
+ }
+
+ @Test
+ public void setActionsExecutable_afterActionQueued_actionExecuted() {
+ TestAction action = new TestAction();
+ mKeyActionExecutor.queueKeyAction(KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action);
+
+ mKeyActionExecutor.setActionsExecutable(KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1);
+
+ assertTrue(action.executed);
+ }
+
+ @Test
+ public void queueKeyAction_alreadyExecutable_actionExecuted() {
+ TestAction action = new TestAction();
+ mKeyActionExecutor.setActionsExecutable(KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1);
+
+ mKeyActionExecutor.queueKeyAction(KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action);
+
+ assertTrue(action.executed);
+ }
+
+ @Test
+ public void setActionsExecutable_afterActionQueued_downTimeMismatch_actionNotExecuted() {
+ TestAction action1 = new TestAction();
+ mKeyActionExecutor.queueKeyAction(
+ KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action1);
+
+ mKeyActionExecutor.setActionsExecutable(KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 2);
+
+ assertFalse(action1.executed);
+
+ TestAction action2 = new TestAction();
+ mKeyActionExecutor.queueKeyAction(
+ KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 2, action2);
+
+ assertFalse(action1.executed);
+ assertTrue(action2.executed);
+ }
+
+ @Test
+ public void queueKeyAction_afterSetExecutable_downTimeMismatch_actionNotExecuted() {
+ TestAction action = new TestAction();
+ mKeyActionExecutor.setActionsExecutable(KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1);
+
+ mKeyActionExecutor.queueKeyAction(KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 2, action);
+
+ assertFalse(action.executed);
+ }
+
+ static class TestAction implements Runnable {
+ public boolean executed;
+
+ @Override
+ public void run() {
+ executed = true;
+ }
+ }
+}