summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/test-current.txt5
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl25
-rw-r--r--core/java/android/hardware/input/InputManager.java125
-rw-r--r--core/java/android/hardware/input/InputManagerGlobal.java25
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java16
-rw-r--r--core/java/android/view/ImeInsetsSourceConsumer.java4
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig7
-rw-r--r--core/java/android/window/flags/wallpaper_manager.aconfig10
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig19
-rw-r--r--data/etc/core.protolog.pbbin54086 -> 54168 bytes
-rw-r--r--data/etc/services.core.protolog.json18
-rw-r--r--data/keyboards/Generic.kl2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java13
-rw-r--r--media/java/android/media/MediaRouter2.java115
-rw-r--r--media/java/android/media/session/ISessionManager.aidl3
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt3
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt4
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedTwoTargetSwitchPreference.kt6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java311
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java42
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt39
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/BottomComponentButtonSurface.kt41
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt33
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt55
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt20
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegate.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogLogger.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepository.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepository.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/BadgedAppIconLoader.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/BasicAppIconLoader.kt (renamed from packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt)24
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java81
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractorTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegateTest.kt)38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepositoryTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/BasicPackageManagerAppIconLoaderTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt)19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt61
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java81
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java67
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java40
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java13
-rw-r--r--services/core/java/com/android/server/connectivity/Android.bp10
-rw-r--r--services/core/java/com/android/server/connectivity/flags.aconfig8
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java48
-rw-r--r--services/core/java/com/android/server/input/KeyboardLayoutManager.java430
-rw-r--r--services/core/java/com/android/server/input/PersistentDataStore.java185
-rw-r--r--services/core/java/com/android/server/media/MediaSession2Record.java5
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java126
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecordImpl.java6
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java145
-rw-r--r--services/core/java/com/android/server/media/MediaShellCommand.java6
-rw-r--r--services/core/java/com/android/server/os/BugreportManagerServiceImpl.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java8
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java13
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java6
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java56
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java28
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java12
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java6
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java16
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java12
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt46
-rw-r--r--tests/FlickerTests/README.md75
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt1067
98 files changed, 1885 insertions, 2118 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 80578e4e40f6..ac679095faa0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1765,15 +1765,12 @@ package android.hardware.input {
public final class InputManager {
method public void addUniqueIdAssociation(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.REMAP_MODIFIER_KEYS) public void clearAllModifierKeyRemappings();
- method @Nullable public String getCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier);
- method @NonNull public java.util.List<java.lang.String> getKeyboardLayoutDescriptorsForInputDevice(@NonNull android.view.InputDevice);
+ method @NonNull public java.util.List<java.lang.String> getKeyboardLayoutDescriptors();
method @NonNull public String getKeyboardLayoutTypeForLayoutDescriptor(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.REMAP_MODIFIER_KEYS) public java.util.Map<java.lang.Integer,java.lang.Integer> getModifierKeyRemapping();
method public int getMousePointerSpeed();
method @RequiresPermission(android.Manifest.permission.REMAP_MODIFIER_KEYS) public void remapModifierKey(int, int);
- method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void removeKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
method public void removeUniqueIdAssociation(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void setCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 2816f777e8ab..1c37aa25af5f 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -94,33 +94,8 @@ interface IInputManager {
// Keyboard layouts configuration.
KeyboardLayout[] getKeyboardLayouts();
- KeyboardLayout[] getKeyboardLayoutsForInputDevice(in InputDeviceIdentifier identifier);
-
KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor);
- String getCurrentKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier);
-
- @EnforcePermission("SET_KEYBOARD_LAYOUT")
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
- + "android.Manifest.permission.SET_KEYBOARD_LAYOUT)")
- void setCurrentKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier,
- String keyboardLayoutDescriptor);
-
- String[] getEnabledKeyboardLayoutsForInputDevice(in InputDeviceIdentifier identifier);
-
- @EnforcePermission("SET_KEYBOARD_LAYOUT")
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
- + "android.Manifest.permission.SET_KEYBOARD_LAYOUT)")
- void addKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier,
- String keyboardLayoutDescriptor);
-
- @EnforcePermission("SET_KEYBOARD_LAYOUT")
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
- + "android.Manifest.permission.SET_KEYBOARD_LAYOUT)")
- void removeKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier,
- String keyboardLayoutDescriptor);
-
- // New Keyboard layout config APIs
KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice(
in InputDeviceIdentifier identifier, int userId, in InputMethodInfo imeInfo,
in InputMethodSubtype imeSubtype);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index a1242fb43bbd..f94915876a2f 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -473,22 +473,21 @@ public final class InputManager {
}
/**
- * Returns the descriptors of all supported keyboard layouts appropriate for the specified
- * input device.
+ * Returns the descriptors of all supported keyboard layouts.
* <p>
* The input manager consults the built-in keyboard layouts as well as all keyboard layouts
* advertised by applications using a {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
* </p>
*
- * @param device The input device to query.
* @return The ids of all keyboard layouts which are supported by the specified input device.
*
* @hide
*/
@TestApi
@NonNull
- public List<String> getKeyboardLayoutDescriptorsForInputDevice(@NonNull InputDevice device) {
- KeyboardLayout[] layouts = getKeyboardLayoutsForInputDevice(device.getIdentifier());
+ @SuppressLint("UnflaggedApi")
+ public List<String> getKeyboardLayoutDescriptors() {
+ KeyboardLayout[] layouts = getKeyboardLayouts();
List<String> res = new ArrayList<>();
for (KeyboardLayout kl : layouts) {
res.add(kl.getDescriptor());
@@ -511,33 +510,18 @@ public final class InputManager {
@TestApi
@NonNull
public String getKeyboardLayoutTypeForLayoutDescriptor(@NonNull String layoutDescriptor) {
- KeyboardLayout[] layouts = getKeyboardLayouts();
- for (KeyboardLayout kl : layouts) {
- if (layoutDescriptor.equals(kl.getDescriptor())) {
- return kl.getLayoutType();
- }
- }
- return "";
+ KeyboardLayout layout = getKeyboardLayout(layoutDescriptor);
+ return layout == null ? "" : layout.getLayoutType();
}
/**
- * Gets information about all supported keyboard layouts appropriate
- * for a specific input device.
- * <p>
- * The input manager consults the built-in keyboard layouts as well
- * as all keyboard layouts advertised by applications using a
- * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
- * </p>
- *
- * @return A list of all supported keyboard layouts for a specific
- * input device.
- *
+ * TODO(b/330517633): Cleanup the unsupported API
* @hide
*/
@NonNull
public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
@NonNull InputDeviceIdentifier identifier) {
- return mGlobal.getKeyboardLayoutsForInputDevice(identifier);
+ return new KeyboardLayout[0];
}
/**
@@ -549,6 +533,7 @@ public final class InputManager {
*
* @hide
*/
+ @Nullable
public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
if (keyboardLayoutDescriptor == null) {
throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
@@ -562,121 +547,45 @@ public final class InputManager {
}
/**
- * Gets the current keyboard layout descriptor for the specified input device.
- *
- * @param identifier Identifier for the input device
- * @return The keyboard layout descriptor, or null if no keyboard layout has been set.
- *
+ * TODO(b/330517633): Cleanup the unsupported API
* @hide
*/
- @TestApi
@Nullable
public String getCurrentKeyboardLayoutForInputDevice(
@NonNull InputDeviceIdentifier identifier) {
- try {
- return mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ return null;
}
/**
- * Sets the current keyboard layout descriptor for the specified input device.
- * <p>
- * This method may have the side-effect of causing the input device in question to be
- * reconfigured.
- * </p>
- *
- * @param identifier The identifier for the input device.
- * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, must not be null.
- *
+ * TODO(b/330517633): Cleanup the unsupported API
* @hide
*/
- @TestApi
- @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
public void setCurrentKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier,
- @NonNull String keyboardLayoutDescriptor) {
- mGlobal.setCurrentKeyboardLayoutForInputDevice(identifier,
- keyboardLayoutDescriptor);
- }
+ @NonNull String keyboardLayoutDescriptor) {}
/**
- * Gets all keyboard layout descriptors that are enabled for the specified input device.
- *
- * @param identifier The identifier for the input device.
- * @return The keyboard layout descriptors.
- *
+ * TODO(b/330517633): Cleanup the unsupported API
* @hide
*/
public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
- if (identifier == null) {
- throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
- }
-
- try {
- return mIm.getEnabledKeyboardLayoutsForInputDevice(identifier);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ return new String[0];
}
/**
- * Adds the keyboard layout descriptor for the specified input device.
- * <p>
- * This method may have the side-effect of causing the input device in question to be
- * reconfigured.
- * </p>
- *
- * @param identifier The identifier for the input device.
- * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to add.
- *
+ * TODO(b/330517633): Cleanup the unsupported API
* @hide
*/
- @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
String keyboardLayoutDescriptor) {
- if (identifier == null) {
- throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
- }
- if (keyboardLayoutDescriptor == null) {
- throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
- }
-
- try {
- mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
}
/**
- * Removes the keyboard layout descriptor for the specified input device.
- * <p>
- * This method may have the side-effect of causing the input device in question to be
- * reconfigured.
- * </p>
- *
- * @param identifier The identifier for the input device.
- * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to remove.
- *
+ * TODO(b/330517633): Cleanup the unsupported API
* @hide
*/
- @TestApi
@RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
public void removeKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier,
@NonNull String keyboardLayoutDescriptor) {
- if (identifier == null) {
- throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
- }
- if (keyboardLayoutDescriptor == null) {
- throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
- }
-
- try {
- mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
}
/**
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index 7c104a0ca946..7b29666d9a96 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -1068,36 +1068,21 @@ public final class InputManagerGlobal {
}
/**
- * @see InputManager#getKeyboardLayoutsForInputDevice(InputDeviceIdentifier)
+ * TODO(b/330517633): Cleanup the unsupported API
*/
@NonNull
public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
@NonNull InputDeviceIdentifier identifier) {
- try {
- return mIm.getKeyboardLayoutsForInputDevice(identifier);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ return new KeyboardLayout[0];
}
/**
- * @see InputManager#setCurrentKeyboardLayoutForInputDevice
- * (InputDeviceIdentifier, String)
+ * TODO(b/330517633): Cleanup the unsupported API
*/
- @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
public void setCurrentKeyboardLayoutForInputDevice(
@NonNull InputDeviceIdentifier identifier,
- @NonNull String keyboardLayoutDescriptor) {
- Objects.requireNonNull(identifier, "identifier must not be null");
- Objects.requireNonNull(keyboardLayoutDescriptor,
- "keyboardLayoutDescriptor must not be null");
- try {
- mIm.setCurrentKeyboardLayoutForInputDevice(identifier,
- keyboardLayoutDescriptor);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
- }
+ @NonNull String keyboardLayoutDescriptor) {}
+
/**
* @see InputDevice#getSensorManager()
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 0fc51e74d570..582c90feb547 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -27,12 +27,15 @@ import static android.graphics.Matrix.MSKEW_Y;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.window.flags.Flags.FLAG_OFFLOAD_COLOR_EXTRACTION;
import static com.android.window.flags.Flags.noConsecutiveVisibilityEvents;
+import static com.android.window.flags.Flags.offloadColorExtraction;
import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -832,6 +835,15 @@ public abstract class WallpaperService extends Service {
}
/**
+ * Called when the dim amount of the wallpaper changed. This can be used to recompute the
+ * wallpaper colors based on the new dim, and call {@link #notifyColorsChanged()}.
+ * @hide
+ */
+ @FlaggedApi(FLAG_OFFLOAD_COLOR_EXTRACTION)
+ public void onDimAmountChanged(float dimAmount) {
+ }
+
+ /**
* Called when an application has changed the desired virtual size of
* the wallpaper.
*/
@@ -1043,6 +1055,10 @@ public abstract class WallpaperService extends Service {
}
mPreviousWallpaperDimAmount = mWallpaperDimAmount;
+
+ // after the dim changes, allow colors to be immediately recomputed
+ mLastColorInvalidation = 0;
+ if (offloadColorExtraction()) onDimAmountChanged(mWallpaperDimAmount);
}
/**
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index b30002228d54..6caf4d6ff992 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -196,11 +196,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
if (!super.setControl(control, showTypes, hideTypes)) {
return false;
}
- final boolean hasLeash = control != null && control.getLeash() != null;
- if (!hasLeash && !mIsRequestedVisibleAwaitingLeash) {
+ if (control == null && !mIsRequestedVisibleAwaitingLeash) {
mController.setRequestedVisibleTypes(0 /* visibleTypes */, getType());
removeSurface();
}
+ final boolean hasLeash = control != null && control.getLeash() != null;
if (hasLeash) {
mIsRequestedVisibleAwaitingLeash = false;
}
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index efe31fff411e..616004b78dba 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -36,3 +36,10 @@ flag {
description: "Enables a limit on the number of Tasks shown in Desktop Mode"
bug: "332502912"
}
+
+flag {
+ name: "enable_windowing_edge_drag_resize"
+ namespace: "lse_desktop_experience"
+ description: "Enables edge drag resizing for all input sources"
+ bug: "323383067"
+}
diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig
index aa92af228862..150b04e87d97 100644
--- a/core/java/android/window/flags/wallpaper_manager.aconfig
+++ b/core/java/android/window/flags/wallpaper_manager.aconfig
@@ -21,4 +21,14 @@ flag {
namespace: "systemui"
description: "Prevent the system from sending consecutive onVisibilityChanged(false) events."
bug: "285631818"
+}
+
+flag {
+ name: "offload_color_extraction"
+ namespace: "systemui"
+ description: "Let ImageWallpaper take care of its wallpaper color extraction, instead of system_server"
+ bug: "328791519"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
} \ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index dd6b772ab871..87c47da16b9a 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -128,9 +128,28 @@ flag {
}
flag {
+ name: "fifo_priority_for_major_ui_processes"
+ namespace: "windowing_frontend"
+ description: "Use realtime priority for SystemUI and launcher"
+ bug: "288140556"
+ is_fixed_read_only: true
+}
+
+flag {
name: "insets_decoupled_configuration"
namespace: "windowing_frontend"
description: "Configuration decoupled from insets"
bug: "151861875"
is_fixed_read_only: true
+}
+
+flag {
+ name: "keyguard_appear_transition"
+ namespace: "windowing_frontend"
+ description: "Add transition when keyguard appears"
+ bug: "327970608"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
} \ No newline at end of file
diff --git a/data/etc/core.protolog.pb b/data/etc/core.protolog.pb
index 826adc39c19a..97147a01b259 100644
--- a/data/etc/core.protolog.pb
+++ b/data/etc/core.protolog.pb
Binary files differ
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index d410d5f5400e..6cf12deea928 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -709,18 +709,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
},
- "2959074735946674755": {
- "message": "Trying to update display configuration for system\/invalid process.",
- "level": "WARN",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
- "5668810920995272206": {
- "message": "Trying to update display configuration for invalid process, pid=%d",
- "level": "WARN",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
"-1123414663662718691": {
"message": "setVr2dDisplayId called for: %d",
"level": "DEBUG",
@@ -3469,6 +3457,12 @@
"group": "WM_DEBUG_WALLPAPER",
"at": "com\/android\/server\/wm\/WallpaperController.java"
},
+ "257349083882992098": {
+ "message": "updateWallpaperTokens requestedVisibility=%b on keyguardLocked=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WALLPAPER",
+ "at": "com\/android\/server\/wm\/WallpaperController.java"
+ },
"7408402065665963407": {
"message": "Wallpaper at display %d - visibility: %b, keyguardLocked: %b",
"level": "VERBOSE",
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index f9347ee6ea09..e8b4104a33bb 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -424,12 +424,14 @@ key 580 APP_SWITCH
key 582 VOICE_ASSIST
# Linux KEY_ASSISTANT
key 583 ASSIST
+key 585 EMOJI_PICKER
key 656 MACRO_1
key 657 MACRO_2
key 658 MACRO_3
key 659 MACRO_4
# Keys defined by HID usages
+key usage 0x0c0065 SCREENSHOT FALLBACK_USAGE_MAPPING
key usage 0x0c0067 WINDOW FALLBACK_USAGE_MAPPING
key usage 0x0c006F BRIGHTNESS_UP FALLBACK_USAGE_MAPPING
key usage 0x0c0070 BRIGHTNESS_DOWN FALLBACK_USAGE_MAPPING
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 313d0d24b459..d2958779c0d4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -455,8 +455,7 @@ public class BubbleController implements ConfigurationChangeListener,
ProtoLog.d(WM_SHELL_BUBBLES,
"onActivityRestartAttempt - taskId=%d selecting matching bubble=%s",
task.taskId, b.getKey());
- mBubbleData.setSelectedBubble(b);
- mBubbleData.setExpanded(true);
+ mBubbleData.setSelectedBubbleAndExpandStack(b);
return;
}
}
@@ -593,13 +592,6 @@ public class BubbleController implements ConfigurationChangeListener,
}
}
- private void openBubbleOverflow() {
- ensureBubbleViewsAndWindowCreated();
- mBubbleData.setShowingOverflow(true);
- mBubbleData.setSelectedBubble(mBubbleData.getOverflow());
- mBubbleData.setExpanded(true);
- }
-
/**
* Called when the status bar has become visible or invisible (either permanently or
* temporarily).
@@ -1247,8 +1239,7 @@ public class BubbleController implements ConfigurationChangeListener,
}
if (mBubbleData.hasBubbleInStackWithKey(b.getKey())) {
// already in the stack
- mBubbleData.setSelectedBubble(b);
- mBubbleData.setExpanded(true);
+ mBubbleData.setSelectedBubbleAndExpandStack(b);
} else if (mBubbleData.hasOverflowBubbleWithKey(b.getKey())) {
// promote it out of the overflow
promoteBubbleFromOverflow(b);
@@ -1273,8 +1264,7 @@ public class BubbleController implements ConfigurationChangeListener,
String key = entry.getKey();
Bubble bubble = mBubbleData.getBubbleInStackWithKey(key);
if (bubble != null) {
- mBubbleData.setSelectedBubble(bubble);
- mBubbleData.setExpanded(true);
+ mBubbleData.setSelectedBubbleAndExpandStack(bubble);
} else {
bubble = mBubbleData.getOverflowBubbleWithKey(key);
if (bubble != null) {
@@ -1367,8 +1357,7 @@ public class BubbleController implements ConfigurationChangeListener,
} else {
// App bubble is not selected, select it & expand
Log.i(TAG, " showOrHideAppBubble, expand and select existing app bubble");
- mBubbleData.setSelectedBubble(existingAppBubble);
- mBubbleData.setExpanded(true);
+ mBubbleData.setSelectedBubbleAndExpandStack(existingAppBubble);
}
} else {
// Check if it exists in the overflow
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 61f0ed22b537..ae3d0c559014 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -365,6 +365,19 @@ public class BubbleData {
mSelectedBubble = bubble;
}
+ /**
+ * Sets the selected bubble and expands it.
+ *
+ * <p>This dispatches a single state update for both changes and should be used instead of
+ * calling {@link #setSelectedBubble(BubbleViewProvider)} followed by
+ * {@link #setExpanded(boolean)} immediately after, which will generate 2 separate updates.
+ */
+ public void setSelectedBubbleAndExpandStack(BubbleViewProvider bubble) {
+ setSelectedBubbleInternal(bubble);
+ setExpandedInternal(true);
+ dispatchPendingChanges();
+ }
+
public void setSelectedBubble(BubbleViewProvider bubble) {
setSelectedBubbleInternal(bubble);
dispatchPendingChanges();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 48e396a4817f..6be411dd81d0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -1222,6 +1222,19 @@ public class BubbleDataTest extends ShellTestCase {
assertThat(update.bubbleBarLocation).isEqualTo(BubbleBarLocation.LEFT);
}
+ @Test
+ public void setSelectedBubbleAndExpandStack() {
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.setSelectedBubbleAndExpandStack(mBubbleA1);
+
+ verifyUpdateReceived();
+ assertSelectionChangedTo(mBubbleA1);
+ assertExpandedChangedTo(true);
+ }
+
private void verifyUpdateReceived() {
verify(mListener).applyUpdate(mUpdateCaptor.capture());
reset(mListener);
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 76bbca620072..5aa006bce000 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -1309,18 +1309,24 @@ public final class MediaRouter2 {
return;
}
- RoutingController newController;
- if (sessionInfo.isSystemSession()) {
- newController = getSystemController();
- newController.setRoutingSessionInfo(sessionInfo);
+ RoutingController newController = addRoutingController(sessionInfo);
+ notifyTransfer(oldController, newController);
+ }
+
+ @NonNull
+ private RoutingController addRoutingController(@NonNull RoutingSessionInfo session) {
+ RoutingController controller;
+ if (session.isSystemSession()) {
+ // mSystemController is never released, so we only need to update its status.
+ mSystemController.setRoutingSessionInfo(session);
+ controller = mSystemController;
} else {
- newController = new RoutingController(sessionInfo);
+ controller = new RoutingController(session);
synchronized (mLock) {
- mNonSystemRoutingControllers.put(newController.getId(), newController);
+ mNonSystemRoutingControllers.put(controller.getId(), controller);
}
}
-
- notifyTransfer(oldController, newController);
+ return controller;
}
void updateControllerOnHandler(RoutingSessionInfo sessionInfo) {
@@ -1329,40 +1335,12 @@ public final class MediaRouter2 {
return;
}
- if (sessionInfo.isSystemSession()) {
- // The session info is sent from SystemMediaRoute2Provider.
- RoutingController systemController = getSystemController();
- systemController.setRoutingSessionInfo(sessionInfo);
- notifyControllerUpdated(systemController);
- return;
- }
-
- RoutingController matchingController;
- synchronized (mLock) {
- matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId());
- }
-
- if (matchingController == null) {
- Log.w(
- TAG,
- "updateControllerOnHandler: Matching controller not found. uniqueSessionId="
- + sessionInfo.getId());
- return;
- }
-
- RoutingSessionInfo oldInfo = matchingController.getRoutingSessionInfo();
- if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) {
- Log.w(
- TAG,
- "updateControllerOnHandler: Provider IDs are not matched. old="
- + oldInfo.getProviderId()
- + ", new="
- + sessionInfo.getProviderId());
- return;
+ RoutingController controller =
+ getMatchingController(sessionInfo, /* logPrefix */ "updateControllerOnHandler");
+ if (controller != null) {
+ controller.setRoutingSessionInfo(sessionInfo);
+ notifyControllerUpdated(controller);
}
-
- matchingController.setRoutingSessionInfo(sessionInfo);
- notifyControllerUpdated(matchingController);
}
void releaseControllerOnHandler(RoutingSessionInfo sessionInfo) {
@@ -1371,34 +1349,47 @@ public final class MediaRouter2 {
return;
}
- RoutingController matchingController;
- synchronized (mLock) {
- matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId());
+ RoutingController matchingController =
+ getMatchingController(sessionInfo, /* logPrefix */ "releaseControllerOnHandler");
+
+ if (matchingController != null) {
+ matchingController.releaseInternal(/* shouldReleaseSession= */ false);
}
+ }
+
+ @Nullable
+ private RoutingController getMatchingController(
+ RoutingSessionInfo sessionInfo, String logPrefix) {
+ if (sessionInfo.isSystemSession()) {
+ return getSystemController();
+ } else {
+ RoutingController controller;
+ synchronized (mLock) {
+ controller = mNonSystemRoutingControllers.get(sessionInfo.getId());
+ }
- if (matchingController == null) {
- if (DEBUG) {
- Log.d(
+ if (controller == null) {
+ Log.w(
TAG,
- "releaseControllerOnHandler: Matching controller not found. "
- + "uniqueSessionId="
+ logPrefix
+ + ": Matching controller not found. uniqueSessionId="
+ sessionInfo.getId());
+ return null;
}
- return;
- }
- RoutingSessionInfo oldInfo = matchingController.getRoutingSessionInfo();
- if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) {
- Log.w(
- TAG,
- "releaseControllerOnHandler: Provider IDs are not matched. old="
- + oldInfo.getProviderId()
- + ", new="
- + sessionInfo.getProviderId());
- return;
+ RoutingSessionInfo oldInfo = controller.getRoutingSessionInfo();
+ if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) {
+ Log.w(
+ TAG,
+ logPrefix
+ + ": Provider IDs are not matched. old="
+ + oldInfo.getProviderId()
+ + ", new="
+ + sessionInfo.getProviderId());
+ return null;
+ }
+ return controller;
}
-
- matchingController.releaseInternal(/* shouldReleaseSession= */ false);
}
void onRequestCreateControllerByManagerOnHandler(
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 207ccbee0b50..871e9ab87299 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -80,4 +80,7 @@ interface ISessionManager {
boolean hasCustomMediaSessionPolicyProvider(String componentName);
int getSessionPolicies(in MediaSession.Token token);
void setSessionPolicies(in MediaSession.Token token, int policies);
+
+ // For testing of temporarily engaged sessions.
+ void expireTempEngagedSessions();
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
index 8b546b4de069..791893b3c056 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
@@ -23,7 +23,6 @@ import com.android.settingslib.spa.widget.ui.SettingsSwitch
@Composable
fun TwoTargetSwitchPreference(
model: SwitchPreferenceModel,
- icon: @Composable (() -> Unit)? = null,
primaryEnabled: () -> Boolean = { true },
primaryOnClick: (() -> Unit)?,
) {
@@ -33,7 +32,7 @@ fun TwoTargetSwitchPreference(
summary = model.summary,
primaryEnabled = primaryEnabled,
primaryOnClick = primaryOnClick,
- icon = icon,
+ icon = model.icon,
) {
SettingsSwitch(
checked = model.checked(),
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt
index 5c2d7701fd6f..1f7122e82c30 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt
@@ -33,11 +33,13 @@ fun <T : AppRecord> AppListItemModel<T>.AppListTwoTargetSwitchItem(
model = object : SwitchPreferenceModel {
override val title = label
override val summary = this@AppListTwoTargetSwitchItem.summary
+ override val icon = @Composable {
+ AppIcon(record.app, SettingsDimension.appIconItemSize)
+ }
override val checked = checked
override val changeable = changeable
override val onCheckedChange = onCheckedChange
},
- icon = { AppIcon(record.app, SettingsDimension.appIconItemSize) },
primaryOnClick = onClick,
)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
index 87cd2b844a2b..c9934adfad22 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
@@ -52,6 +52,8 @@ internal class RestrictedSwitchPreferenceModel(
checked = model.checked,
)
+ override val icon = model.icon
+
override val checked = when (restrictedMode) {
null -> ({ null })
is NoRestricted -> model.checked
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedTwoTargetSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedTwoTargetSwitchPreference.kt
index e100773b2358..1bed73365e80 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedTwoTargetSwitchPreference.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedTwoTargetSwitchPreference.kt
@@ -29,14 +29,12 @@ import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitc
@Composable
fun RestrictedTwoTargetSwitchPreference(
model: SwitchPreferenceModel,
- icon: @Composable (() -> Unit)? = null,
restrictions: Restrictions,
primaryEnabled: () -> Boolean = { true },
primaryOnClick: (() -> Unit)?,
) {
RestrictedTwoTargetSwitchPreference(
model = model,
- icon = icon,
primaryEnabled = primaryEnabled,
primaryOnClick = primaryOnClick,
restrictions = restrictions,
@@ -48,21 +46,19 @@ fun RestrictedTwoTargetSwitchPreference(
@Composable
internal fun RestrictedTwoTargetSwitchPreference(
model: SwitchPreferenceModel,
- icon: @Composable (() -> Unit)? = null,
primaryEnabled: () -> Boolean = { true },
primaryOnClick: (() -> Unit)?,
restrictions: Restrictions,
restrictionsProviderFactory: RestrictionsProviderFactory,
) {
if (restrictions.isEmpty()) {
- TwoTargetSwitchPreference(model, icon, primaryEnabled, primaryOnClick)
+ TwoTargetSwitchPreference(model, primaryEnabled, primaryOnClick)
return
}
val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions).value
RestrictedSwitchWrapper(model, restrictedMode) { restrictedModel ->
TwoTargetSwitchPreference(
model = restrictedModel,
- icon = icon,
primaryEnabled = restrictedMode.restrictEnabled(primaryEnabled),
primaryOnClick = restrictedMode.restrictOnClick(primaryOnClick),
)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 57fcc7462a65..a906875e11ef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -3,10 +3,13 @@ package com.android.settingslib.bluetooth;
import static com.android.settingslib.widget.AdaptiveOutlineDrawable.ICON_TYPE_ADVANCED;
import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -30,6 +33,7 @@ import androidx.annotation.WorkerThread;
import androidx.core.graphics.drawable.IconCompat;
import com.android.settingslib.R;
+import com.android.settingslib.flags.Flags;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.settingslib.widget.AdaptiveOutlineDrawable;
@@ -46,14 +50,14 @@ public class BluetoothUtils {
private static final String TAG = "BluetoothUtils";
public static final boolean V = false; // verbose logging
- public static final boolean D = true; // regular logging
+ public static final boolean D = true; // regular logging
public static final int META_INT_ERROR = -1;
public static final String BT_ADVANCED_HEADER_ENABLED = "bt_advanced_header_enabled";
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
private static final String KEY_HEARABLE_CONTROL_SLICE = "HEARABLE_CONTROL_SLICE_WITH_WIDTH";
- private static final Set<String> EXCLUSIVE_MANAGERS = ImmutableSet.of(
- "com.google.android.gms.dck");
+ private static final Set<String> EXCLUSIVE_MANAGERS =
+ ImmutableSet.of("com.google.android.gms.dck");
private static ErrorListener sErrorListener;
@@ -89,23 +93,23 @@ public class BluetoothUtils {
/**
* @param context to access resources from
* @param cachedDevice to get class from
- * @return pair containing the drawable and the description of the Bluetooth class
- * of the device.
+ * @return pair containing the drawable and the description of the Bluetooth class of the
+ * device.
*/
- public static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
- CachedBluetoothDevice cachedDevice) {
+ public static Pair<Drawable, String> getBtClassDrawableWithDescription(
+ Context context, CachedBluetoothDevice cachedDevice) {
BluetoothClass btClass = cachedDevice.getBtClass();
if (btClass != null) {
switch (btClass.getMajorDeviceClass()) {
case BluetoothClass.Device.Major.COMPUTER:
- return new Pair<>(getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_bt_laptop),
+ return new Pair<>(
+ getBluetoothDrawable(
+ context, com.android.internal.R.drawable.ic_bt_laptop),
context.getString(R.string.bluetooth_talkback_computer));
case BluetoothClass.Device.Major.PHONE:
return new Pair<>(
- getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_phone),
+ getBluetoothDrawable(context, com.android.internal.R.drawable.ic_phone),
context.getString(R.string.bluetooth_talkback_phone));
case BluetoothClass.Device.Major.PERIPHERAL:
@@ -115,8 +119,8 @@ public class BluetoothUtils {
case BluetoothClass.Device.Major.IMAGING:
return new Pair<>(
- getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_settings_print),
+ getBluetoothDrawable(
+ context, com.android.internal.R.drawable.ic_settings_print),
context.getString(R.string.bluetooth_talkback_imaging));
default:
@@ -125,8 +129,9 @@ public class BluetoothUtils {
}
if (cachedDevice.isHearingAidDevice()) {
- return new Pair<>(getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_bt_hearing_aid),
+ return new Pair<>(
+ getBluetoothDrawable(
+ context, com.android.internal.R.drawable.ic_bt_hearing_aid),
context.getString(R.string.bluetooth_talkback_hearing_aids));
}
@@ -138,7 +143,8 @@ public class BluetoothUtils {
// The device should show hearing aid icon if it contains any hearing aid related
// profiles
if (profile instanceof HearingAidProfile || profile instanceof HapClientProfile) {
- return new Pair<>(getBluetoothDrawable(context, profileResId),
+ return new Pair<>(
+ getBluetoothDrawable(context, profileResId),
context.getString(R.string.bluetooth_talkback_hearing_aids));
}
if (resId == 0) {
@@ -153,42 +159,40 @@ public class BluetoothUtils {
if (btClass != null) {
if (doesClassMatch(btClass, BluetoothClass.PROFILE_HEADSET)) {
return new Pair<>(
- getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_bt_headset_hfp),
+ getBluetoothDrawable(
+ context, com.android.internal.R.drawable.ic_bt_headset_hfp),
context.getString(R.string.bluetooth_talkback_headset));
}
if (doesClassMatch(btClass, BluetoothClass.PROFILE_A2DP)) {
return new Pair<>(
- getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_bt_headphones_a2dp),
+ getBluetoothDrawable(
+ context, com.android.internal.R.drawable.ic_bt_headphones_a2dp),
context.getString(R.string.bluetooth_talkback_headphone));
}
}
return new Pair<>(
- getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_settings_bluetooth).mutate(),
+ getBluetoothDrawable(context, com.android.internal.R.drawable.ic_settings_bluetooth)
+ .mutate(),
context.getString(R.string.bluetooth_talkback_bluetooth));
}
- /**
- * Get bluetooth drawable by {@code resId}
- */
+ /** Get bluetooth drawable by {@code resId} */
public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId) {
return context.getDrawable(resId);
}
- /**
- * Get colorful bluetooth icon with description
- */
- public static Pair<Drawable, String> getBtRainbowDrawableWithDescription(Context context,
- CachedBluetoothDevice cachedDevice) {
+ /** Get colorful bluetooth icon with description */
+ public static Pair<Drawable, String> getBtRainbowDrawableWithDescription(
+ Context context, CachedBluetoothDevice cachedDevice) {
final Resources resources = context.getResources();
- final Pair<Drawable, String> pair = BluetoothUtils.getBtDrawableWithDescription(context,
- cachedDevice);
+ final Pair<Drawable, String> pair =
+ BluetoothUtils.getBtDrawableWithDescription(context, cachedDevice);
if (pair.first instanceof BitmapDrawable) {
- return new Pair<>(new AdaptiveOutlineDrawable(
- resources, ((BitmapDrawable) pair.first).getBitmap()), pair.second);
+ return new Pair<>(
+ new AdaptiveOutlineDrawable(
+ resources, ((BitmapDrawable) pair.first).getBitmap()),
+ pair.second);
}
int hashCode;
@@ -198,15 +202,12 @@ public class BluetoothUtils {
hashCode = cachedDevice.getAddress().hashCode();
}
- return new Pair<>(buildBtRainbowDrawable(context,
- pair.first, hashCode), pair.second);
+ return new Pair<>(buildBtRainbowDrawable(context, pair.first, hashCode), pair.second);
}
- /**
- * Build Bluetooth device icon with rainbow
- */
- private static Drawable buildBtRainbowDrawable(Context context, Drawable drawable,
- int hashCode) {
+ /** Build Bluetooth device icon with rainbow */
+ private static Drawable buildBtRainbowDrawable(
+ Context context, Drawable drawable, int hashCode) {
final Resources resources = context.getResources();
// Deal with normal headset
@@ -222,38 +223,37 @@ public class BluetoothUtils {
return adaptiveIcon;
}
- /**
- * Get bluetooth icon with description
- */
- public static Pair<Drawable, String> getBtDrawableWithDescription(Context context,
- CachedBluetoothDevice cachedDevice) {
- final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
- context, cachedDevice);
+ /** Get bluetooth icon with description */
+ public static Pair<Drawable, String> getBtDrawableWithDescription(
+ Context context, CachedBluetoothDevice cachedDevice) {
+ final Pair<Drawable, String> pair =
+ BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice);
final BluetoothDevice bluetoothDevice = cachedDevice.getDevice();
- final int iconSize = context.getResources().getDimensionPixelSize(
- R.dimen.bt_nearby_icon_size);
+ final int iconSize =
+ context.getResources().getDimensionPixelSize(R.dimen.bt_nearby_icon_size);
final Resources resources = context.getResources();
// Deal with advanced device icon
if (isAdvancedDetailsHeader(bluetoothDevice)) {
- final Uri iconUri = getUriMetaData(bluetoothDevice,
- BluetoothDevice.METADATA_MAIN_ICON);
+ final Uri iconUri = getUriMetaData(bluetoothDevice, BluetoothDevice.METADATA_MAIN_ICON);
if (iconUri != null) {
try {
- context.getContentResolver().takePersistableUriPermission(iconUri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ context.getContentResolver()
+ .takePersistableUriPermission(
+ iconUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
} catch (SecurityException e) {
Log.e(TAG, "Failed to take persistable permission for: " + iconUri, e);
}
try {
- final Bitmap bitmap = MediaStore.Images.Media.getBitmap(
- context.getContentResolver(), iconUri);
+ final Bitmap bitmap =
+ MediaStore.Images.Media.getBitmap(
+ context.getContentResolver(), iconUri);
if (bitmap != null) {
- final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
- iconSize, false);
+ final Bitmap resizedBitmap =
+ Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, false);
bitmap.recycle();
- return new Pair<>(new BitmapDrawable(resources,
- resizedBitmap), pair.second);
+ return new Pair<>(
+ new BitmapDrawable(resources, resizedBitmap), pair.second);
}
} catch (IOException e) {
Log.e(TAG, "Failed to get drawable for: " + iconUri, e);
@@ -280,8 +280,8 @@ public class BluetoothUtils {
return true;
}
// The metadata is for Android S
- String deviceType = getStringMetaData(bluetoothDevice,
- BluetoothDevice.METADATA_DEVICE_TYPE);
+ String deviceType =
+ getStringMetaData(bluetoothDevice, BluetoothDevice.METADATA_DEVICE_TYPE);
if (TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET)
|| TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_WATCH)
|| TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_DEFAULT)
@@ -306,8 +306,8 @@ public class BluetoothUtils {
return true;
}
// The metadata is for Android S
- String deviceType = getStringMetaData(bluetoothDevice,
- BluetoothDevice.METADATA_DEVICE_TYPE);
+ String deviceType =
+ getStringMetaData(bluetoothDevice, BluetoothDevice.METADATA_DEVICE_TYPE);
if (TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET)) {
Log.d(TAG, "isAdvancedUntetheredDevice: is untethered device ");
return true;
@@ -321,15 +321,15 @@ public class BluetoothUtils {
* @param device Must be one of the public constants in {@link BluetoothClass.Device}
* @return true if device class matches, false otherwise.
*/
- public static boolean isDeviceClassMatched(@NonNull BluetoothDevice bluetoothDevice,
- int device) {
+ public static boolean isDeviceClassMatched(
+ @NonNull BluetoothDevice bluetoothDevice, int device) {
final BluetoothClass bluetoothClass = bluetoothDevice.getBluetoothClass();
return bluetoothClass != null && bluetoothClass.getDeviceClass() == device;
}
private static boolean isAdvancedHeaderEnabled() {
- if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI, BT_ADVANCED_HEADER_ENABLED,
- true)) {
+ if (!DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SETTINGS_UI, BT_ADVANCED_HEADER_ENABLED, true)) {
Log.d(TAG, "isAdvancedDetailsHeader: advancedEnabled is false");
return false;
}
@@ -345,9 +345,7 @@ public class BluetoothUtils {
return false;
}
- /**
- * Create an Icon pointing to a drawable.
- */
+ /** Create an Icon pointing to a drawable. */
public static IconCompat createIconWithDrawable(Drawable drawable) {
Bitmap bitmap;
if (drawable instanceof BitmapDrawable) {
@@ -355,19 +353,15 @@ public class BluetoothUtils {
} else {
final int width = drawable.getIntrinsicWidth();
final int height = drawable.getIntrinsicHeight();
- bitmap = createBitmap(drawable,
- width > 0 ? width : 1,
- height > 0 ? height : 1);
+ bitmap = createBitmap(drawable, width > 0 ? width : 1, height > 0 ? height : 1);
}
return IconCompat.createWithBitmap(bitmap);
}
- /**
- * Build device icon with advanced outline
- */
+ /** Build device icon with advanced outline */
public static Drawable buildAdvancedDrawable(Context context, Drawable drawable) {
- final int iconSize = context.getResources().getDimensionPixelSize(
- R.dimen.advanced_icon_size);
+ final int iconSize =
+ context.getResources().getDimensionPixelSize(R.dimen.advanced_icon_size);
final Resources resources = context.getResources();
Bitmap bitmap = null;
@@ -376,14 +370,12 @@ public class BluetoothUtils {
} else {
final int width = drawable.getIntrinsicWidth();
final int height = drawable.getIntrinsicHeight();
- bitmap = createBitmap(drawable,
- width > 0 ? width : 1,
- height > 0 ? height : 1);
+ bitmap = createBitmap(drawable, width > 0 ? width : 1, height > 0 ? height : 1);
}
if (bitmap != null) {
- final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
- iconSize, false);
+ final Bitmap resizedBitmap =
+ Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, false);
bitmap.recycle();
return new AdaptiveOutlineDrawable(resources, resizedBitmap, ICON_TYPE_ADVANCED);
}
@@ -391,9 +383,7 @@ public class BluetoothUtils {
return drawable;
}
- /**
- * Creates a drawable with specified width and height.
- */
+ /** Creates a drawable with specified width and height. */
public static Bitmap createBitmap(Drawable drawable, int width, int height) {
final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
@@ -487,11 +477,8 @@ public class BluetoothUtils {
}
/**
- * Check if the Bluetooth device is an AvailableMediaBluetoothDevice, which means:
- * 1) currently connected
- * 2) is Hearing Aid or LE Audio
- * OR
- * 3) connected profile matches currentAudioProfile
+ * Check if the Bluetooth device is an AvailableMediaBluetoothDevice, which means: 1) currently
+ * connected 2) is Hearing Aid or LE Audio OR 3) connected profile matches currentAudioProfile
*
* @param cachedDevice the CachedBluetoothDevice
* @param audioManager audio manager to get the current audio profile
@@ -519,8 +506,11 @@ public class BluetoothUtils {
// It would show in Available Devices group.
if (cachedDevice.isConnectedAshaHearingAidDevice()
|| cachedDevice.isConnectedLeAudioDevice()) {
- Log.d(TAG, "isFilterMatched() device : "
- + cachedDevice.getName() + ", the profile is connected.");
+ Log.d(
+ TAG,
+ "isFilterMatched() device : "
+ + cachedDevice.getName()
+ + ", the profile is connected.");
return true;
}
// According to the current audio profile type,
@@ -541,11 +531,79 @@ public class BluetoothUtils {
return isFilterMatched;
}
+ /** Returns if the le audio sharing is enabled. */
+ public static boolean isAudioSharingEnabled() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ return Flags.enableLeAudioSharing()
+ && adapter.isLeAudioBroadcastSourceSupported()
+ == BluetoothStatusCodes.FEATURE_SUPPORTED
+ && adapter.isLeAudioBroadcastAssistantSupported()
+ == BluetoothStatusCodes.FEATURE_SUPPORTED;
+ }
+
+ /** Returns if the broadcast is on-going. */
+ @WorkerThread
+ public static boolean isBroadcasting(@Nullable LocalBluetoothManager manager) {
+ if (manager == null) return false;
+ LocalBluetoothLeBroadcast broadcast =
+ manager.getProfileManager().getLeAudioBroadcastProfile();
+ return broadcast != null && broadcast.isEnabled(null);
+ }
+
+ /**
+ * Check if {@link CachedBluetoothDevice} has connected to a broadcast source.
+ *
+ * @param cachedDevice The cached bluetooth device to check.
+ * @param localBtManager The BT manager to provide BT functions.
+ * @return Whether the device has connected to a broadcast source.
+ */
+ @WorkerThread
+ public static boolean hasConnectedBroadcastSource(
+ CachedBluetoothDevice cachedDevice, LocalBluetoothManager localBtManager) {
+ if (localBtManager == null) {
+ Log.d(TAG, "Skip check hasConnectedBroadcastSource due to bt manager is null");
+ return false;
+ }
+ LocalBluetoothLeBroadcastAssistant assistant =
+ localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+ if (assistant == null) {
+ Log.d(TAG, "Skip check hasConnectedBroadcastSource due to assistant profile is null");
+ return false;
+ }
+ List<BluetoothLeBroadcastReceiveState> sourceList =
+ assistant.getAllSources(cachedDevice.getDevice());
+ if (!sourceList.isEmpty() && sourceList.stream().anyMatch(BluetoothUtils::isConnected)) {
+ Log.d(
+ TAG,
+ "Lead device has connected broadcast source, device = "
+ + cachedDevice.getDevice().getAnonymizedAddress());
+ return true;
+ }
+ // Return true if member device is in broadcast.
+ for (CachedBluetoothDevice device : cachedDevice.getMemberDevice()) {
+ List<BluetoothLeBroadcastReceiveState> list =
+ assistant.getAllSources(device.getDevice());
+ if (!list.isEmpty() && list.stream().anyMatch(BluetoothUtils::isConnected)) {
+ Log.d(
+ TAG,
+ "Member device has connected broadcast source, device = "
+ + device.getDevice().getAnonymizedAddress());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Checks the connectivity status based on the provided broadcast receive state. */
+ @WorkerThread
+ public static boolean isConnected(BluetoothLeBroadcastReceiveState state) {
+ return state.getBisSyncState().stream().anyMatch(bitmap -> bitmap != 0);
+ }
+
/**
- * Checks if the Bluetooth device is an available hearing device, which means:
- * 1) currently connected
- * 2) is Hearing Aid
- * 3) connected profile match hearing aid related profiles (e.g. ASHA, HAP)
+ * Checks if the Bluetooth device is an available hearing device, which means: 1) currently
+ * connected 2) is Hearing Aid 3) connected profile match hearing aid related profiles (e.g.
+ * ASHA, HAP)
*
* @param cachedDevice the CachedBluetoothDevice
* @return if the device is Available hearing device
@@ -553,19 +611,20 @@ public class BluetoothUtils {
@WorkerThread
public static boolean isAvailableHearingDevice(CachedBluetoothDevice cachedDevice) {
if (isDeviceConnected(cachedDevice) && cachedDevice.isConnectedHearingAidDevice()) {
- Log.d(TAG, "isFilterMatched() device : "
- + cachedDevice.getName() + ", the profile is connected.");
+ Log.d(
+ TAG,
+ "isFilterMatched() device : "
+ + cachedDevice.getName()
+ + ", the profile is connected.");
return true;
}
return false;
}
/**
- * Check if the Bluetooth device is a ConnectedBluetoothDevice, which means:
- * 1) currently connected
- * 2) is not Hearing Aid or LE Audio
- * AND
- * 3) connected profile does not match currentAudioProfile
+ * Check if the Bluetooth device is a ConnectedBluetoothDevice, which means: 1) currently
+ * connected 2) is not Hearing Aid or LE Audio AND 3) connected profile does not match
+ * currentAudioProfile
*
* @param cachedDevice the CachedBluetoothDevice
* @param audioManager audio manager to get the current audio profile
@@ -675,29 +734,28 @@ public class BluetoothUtils {
}
/**
- * Returns the BluetoothDevice's exclusive manager
- * ({@link BluetoothDevice.METADATA_EXCLUSIVE_MANAGER} in metadata) if it exists and is in the
- * given set, otherwise null.
+ * Returns the BluetoothDevice's exclusive manager ({@link
+ * BluetoothDevice.METADATA_EXCLUSIVE_MANAGER} in metadata) if it exists and is in the given
+ * set, otherwise null.
*/
@Nullable
private static String getAllowedExclusiveManager(BluetoothDevice bluetoothDevice) {
- byte[] exclusiveManagerNameBytes = bluetoothDevice.getMetadata(
- BluetoothDevice.METADATA_EXCLUSIVE_MANAGER);
+ byte[] exclusiveManagerNameBytes =
+ bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER);
if (exclusiveManagerNameBytes == null) {
- Log.d(TAG, "Bluetooth device " + bluetoothDevice.getName()
- + " doesn't have exclusive manager");
+ Log.d(
+ TAG,
+ "Bluetooth device "
+ + bluetoothDevice.getName()
+ + " doesn't have exclusive manager");
return null;
}
String exclusiveManagerName = new String(exclusiveManagerNameBytes);
- return getExclusiveManagers().contains(exclusiveManagerName) ? exclusiveManagerName
- : null;
+ return getExclusiveManagers().contains(exclusiveManagerName) ? exclusiveManagerName : null;
}
- /**
- * Checks if given package is installed
- */
- private static boolean isPackageInstalled(Context context,
- String packageName) {
+ /** Checks if given package is installed */
+ private static boolean isPackageInstalled(Context context, String packageName) {
PackageManager packageManager = context.getPackageManager();
try {
packageManager.getPackageInfo(packageName, 0);
@@ -709,13 +767,12 @@ public class BluetoothUtils {
}
/**
- * A BluetoothDevice is exclusively managed if
- * 1) it has field {@link BluetoothDevice.METADATA_EXCLUSIVE_MANAGER} in metadata.
- * 2) the exclusive manager app name is in the allowlist.
- * 3) the exclusive manager app is installed.
+ * A BluetoothDevice is exclusively managed if 1) it has field {@link
+ * BluetoothDevice.METADATA_EXCLUSIVE_MANAGER} in metadata. 2) the exclusive manager app name is
+ * in the allowlist. 3) the exclusive manager app is installed.
*/
- public static boolean isExclusivelyManagedBluetoothDevice(@NonNull Context context,
- @NonNull BluetoothDevice bluetoothDevice) {
+ public static boolean isExclusivelyManagedBluetoothDevice(
+ @NonNull Context context, @NonNull BluetoothDevice bluetoothDevice) {
String exclusiveManagerName = getAllowedExclusiveManager(bluetoothDevice);
if (exclusiveManagerName == null) {
return false;
@@ -728,9 +785,7 @@ public class BluetoothUtils {
}
}
- /**
- * Return the allowlist for exclusive manager names.
- */
+ /** Return the allowlist for exclusive manager names. */
@NonNull
public static Set<String> getExclusiveManagers() {
return EXCLUSIVE_MANAGERS;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 1246fd85ee16..f197f9ee0baf 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -17,6 +17,7 @@ package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy;
@@ -25,6 +26,7 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -44,6 +46,9 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(RobolectricTestRunner.class)
public class BluetoothUtilsTest {
@@ -55,6 +60,16 @@ public class BluetoothUtilsTest {
private AudioManager mAudioManager;
@Mock
private PackageManager mPackageManager;
+ @Mock
+ private LocalBluetoothLeBroadcast mBroadcast;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
+ @Mock
+ private LocalBluetoothLeBroadcastAssistant mAssistant;
+ @Mock
+ private BluetoothLeBroadcastReceiveState mLeBroadcastReceiveState;
private Context mContext;
private static final String STRING_METADATA = "string_metadata";
@@ -72,6 +87,9 @@ public class BluetoothUtilsTest {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
+ when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager);
+ when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
+ when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
}
@Test
@@ -432,6 +450,30 @@ public class BluetoothUtilsTest {
}
@Test
+ public void testIsBroadcasting_broadcastEnabled_returnTrue() {
+ when(mBroadcast.isEnabled(any())).thenReturn(true);
+ assertThat(BluetoothUtils.isBroadcasting(mLocalBluetoothManager)).isEqualTo(true);
+ }
+
+ @Test
+ public void testHasConnectedBroadcastSource_deviceConnectedToBroadcastSource() {
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+
+ List<Long> bisSyncState = new ArrayList<>();
+ bisSyncState.add(1L);
+ when(mLeBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState);
+
+ List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+ sourceList.add(mLeBroadcastReceiveState);
+ when(mAssistant.getAllSources(any())).thenReturn(sourceList);
+
+ assertThat(
+ BluetoothUtils.hasConnectedBroadcastSource(
+ mCachedBluetoothDevice, mLocalBluetoothManager))
+ .isEqualTo(true);
+ }
+
+ @Test
public void isAvailableHearingDevice_isConnectedHearingAid_returnTure() {
when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index fcd77609768e..02a12e4e0814 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -54,9 +54,9 @@ import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.TransitionState
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateElementFloatAsState
-import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.settingslib.Utils
import com.android.systemui.battery.BatteryMeterView
@@ -87,10 +87,6 @@ object ShadeHeader {
val ShadeCarrierGroup = ElementKey("ShadeCarrierGroup")
}
- object Keys {
- val transitionProgress = ValueKey("ShadeHeaderTransitionProgress")
- }
-
object Values {
val ClockScale = ValueKey("ShadeHeaderClockScale")
}
@@ -119,19 +115,17 @@ fun SceneScope.CollapsedShadeHeader(
return
}
- val formatProgress =
- animateSceneFloatAsState(0f, ShadeHeader.Keys.transitionProgress)
- .unsafeCompositionState(initialValue = 0f)
-
val cutoutWidth = LocalDisplayCutout.current.width()
val cutoutLocation = LocalDisplayCutout.current.location
val useExpandedFormat by
- remember(formatProgress) {
+ remember(cutoutLocation) {
derivedStateOf {
- cutoutLocation != CutoutLocation.CENTER || formatProgress.value > 0.5f
+ cutoutLocation != CutoutLocation.CENTER ||
+ shouldUseExpandedFormat(layoutState.transitionState)
}
}
+
val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
// This layout assumes it is globally positioned at (0, 0) and is the
@@ -207,7 +201,7 @@ fun SceneScope.CollapsedShadeHeader(
val screenWidth = constraints.maxWidth
val cutoutWidthPx = cutoutWidth.roundToPx()
- val height = ShadeHeader.Dimensions.CollapsedHeight.roundToPx()
+ val height = CollapsedHeight.roundToPx()
val childConstraints = Constraints.fixed((screenWidth - cutoutWidthPx) / 2, height)
val startMeasurable = measurables[0][0]
@@ -261,11 +255,10 @@ fun SceneScope.ExpandedShadeHeader(
return
}
- val formatProgress =
- animateSceneFloatAsState(1f, ShadeHeader.Keys.transitionProgress)
- .unsafeCompositionState(initialValue = 1f)
- val useExpandedFormat by
- remember(formatProgress) { derivedStateOf { formatProgress.value > 0.5f } }
+ val useExpandedFormat by remember {
+ derivedStateOf { shouldUseExpandedFormat(layoutState.transitionState) }
+ }
+
val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
Box(modifier = modifier) {
@@ -530,3 +523,15 @@ private fun SceneScope.PrivacyChip(
modifier = modifier.element(ShadeHeader.Elements.PrivacyChip),
)
}
+
+private fun shouldUseExpandedFormat(state: TransitionState): Boolean {
+ return when (state) {
+ is TransitionState.Idle -> {
+ state.currentScene == Scenes.QuickSettings
+ }
+ is TransitionState.Transition -> {
+ (state.isTransitioning(Scenes.Shade, Scenes.QuickSettings) && state.progress >= 0.5) ||
+ (state.isTransitioning(Scenes.QuickSettings, Scenes.Shade) && state.progress < 0.5)
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/BottomComponentButtonSurface.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/BottomComponentButtonSurface.kt
new file mode 100644
index 000000000000..167eb65da7ee
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/BottomComponentButtonSurface.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.button.ui.composable
+
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+/**
+ * Container to create a rim around the button. Both `Expandable` and `OutlinedIconToggleButton`
+ * have antialiasing problem when used with [androidx.compose.foundation.BorderStroke] and some
+ * contrast container color.
+ */
+// TODO(b/331584069): Remove this once antialiasing bug is fixed
+@Composable
+fun BottomComponentButtonSurface(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
+ Surface(
+ modifier = modifier.height(64.dp),
+ shape = RoundedCornerShape(28.dp),
+ color = MaterialTheme.colorScheme.surface,
+ content = content,
+ )
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
index 8f187cce9e77..fc511e12ec54 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
@@ -16,14 +16,12 @@
package com.android.systemui.volume.panel.component.button.ui.composable
-import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
@@ -64,20 +62,21 @@ class ButtonComponent(
verticalArrangement = Arrangement.spacedBy(12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
- Expandable(
- modifier =
- Modifier.height(64.dp).fillMaxWidth().semantics {
- role = Role.Button
- contentDescription = label
- },
- color = MaterialTheme.colorScheme.primaryContainer,
- shape = RoundedCornerShape(28.dp),
- contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
- borderStroke = BorderStroke(8.dp, MaterialTheme.colorScheme.surface),
- onClick = onClick,
- ) {
- Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
- Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
+ BottomComponentButtonSurface {
+ Expandable(
+ modifier =
+ Modifier.fillMaxSize().padding(8.dp).semantics {
+ role = Role.Button
+ contentDescription = label
+ },
+ color = MaterialTheme.colorScheme.primaryContainer,
+ shape = RoundedCornerShape(28.dp),
+ contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ onClick = onClick,
+ ) {
+ Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+ Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
+ }
}
}
Text(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index 51ec63b16fbb..780e3f2de4c8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -16,23 +16,23 @@
package com.android.systemui.volume.panel.component.button.ui.composable
-import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedIconToggleButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
@@ -62,26 +62,33 @@ class ToggleButtonComponent(
verticalArrangement = Arrangement.spacedBy(12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
- OutlinedIconToggleButton(
- modifier =
- Modifier.height(64.dp).fillMaxWidth().semantics {
- role = Role.Switch
- contentDescription = label
- },
- checked = viewModel.isChecked,
- onCheckedChange = onCheckedChange,
- shape = RoundedCornerShape(28.dp),
- colors =
- IconButtonDefaults.outlinedIconToggleButtonColors(
- containerColor = MaterialTheme.colorScheme.surface,
- contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
- checkedContainerColor = MaterialTheme.colorScheme.primaryContainer,
- checkedContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
- ),
- border = BorderStroke(8.dp, MaterialTheme.colorScheme.surface),
- ) {
- Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
+ BottomComponentButtonSurface {
+ val colors =
+ if (viewModel.isChecked) {
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.primaryContainer,
+ contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ )
+ } else {
+ ButtonDefaults.buttonColors(
+ containerColor = Color.Transparent,
+ contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
+ Button(
+ modifier =
+ Modifier.fillMaxSize().padding(8.dp).semantics {
+ role = Role.Switch
+ contentDescription = label
+ },
+ onClick = { onCheckedChange(!viewModel.isChecked) },
+ shape = RoundedCornerShape(28.dp),
+ colors = colors
+ ) {
+ Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
+ }
}
+
Text(
modifier = Modifier.clearAndSetSemantics {}.basicMarquee(),
text = label,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt
index e2d7d1165bb7..c74331477229 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt
@@ -46,6 +46,12 @@ import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.selected
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
@@ -112,10 +118,16 @@ fun VolumePanelRadioButtonBar(
horizontalArrangement = Arrangement.spacedBy(spacing)
) {
for (itemIndex in items.indices) {
+ val item = items[itemIndex]
Row(
modifier =
Modifier.height(48.dp)
.weight(1f)
+ .semantics {
+ item.contentDescription?.let { contentDescription = it }
+ role = Role.Switch
+ selected = itemIndex == scope.selectedIndex
+ }
.clickable(
interactionSource = null,
indication = null,
@@ -124,7 +136,6 @@ fun VolumePanelRadioButtonBar(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
- val item = items[itemIndex]
if (item.icon !== Empty) {
with(items[itemIndex]) { icon() }
}
@@ -138,7 +149,8 @@ fun VolumePanelRadioButtonBar(
start = indicatorBackgroundPadding,
top = labelIndicatorBackgroundSpacing,
end = indicatorBackgroundPadding
- ),
+ )
+ .clearAndSetSemantics {},
horizontalArrangement = Arrangement.spacedBy(spacing),
) {
for (itemIndex in items.indices) {
@@ -296,6 +308,7 @@ interface VolumePanelRadioButtonBarScope {
onItemSelected: () -> Unit,
icon: @Composable RowScope.() -> Unit = Empty,
label: @Composable RowScope.() -> Unit = Empty,
+ contentDescription: String? = null,
)
}
@@ -317,6 +330,7 @@ private class VolumePanelRadioButtonBarScopeImpl : VolumePanelRadioButtonBarScop
onItemSelected: () -> Unit,
icon: @Composable RowScope.() -> Unit,
label: @Composable RowScope.() -> Unit,
+ contentDescription: String?,
) {
require(!isSelected || !hasSelectedItem) { "Only one item should be selected at a time" }
if (isSelected) {
@@ -327,6 +341,7 @@ private class VolumePanelRadioButtonBarScopeImpl : VolumePanelRadioButtonBarScop
onItemSelected = onItemSelected,
icon = icon,
label = label,
+ contentDescription = contentDescription,
)
)
}
@@ -340,6 +355,7 @@ private class Item(
val onItemSelected: () -> Unit,
val icon: @Composable RowScope.() -> Unit,
val label: @Composable RowScope.() -> Unit,
+ val contentDescription: String?,
)
private const val UNSET_OFFSET = -1
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index 6673afdfafb4..eed54dab6faf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -74,9 +74,11 @@ constructor(
}
VolumePanelRadioButtonBar {
for (buttonViewModel in enabledModelStates) {
+ val label = buttonViewModel.button.label.toString()
item(
isSelected = buttonViewModel.button.isChecked,
onItemSelected = { viewModel.setEnabled(buttonViewModel.model) },
+ contentDescription = label,
icon = {
Icon(
icon = buttonViewModel.button.icon,
@@ -86,7 +88,7 @@ constructor(
label = {
Text(
modifier = Modifier.basicMarquee(),
- text = buttonViewModel.button.label.toString(),
+ text = label,
style = MaterialTheme.typography.labelMedium,
color = buttonViewModel.labelColor.toColor(),
textAlign = TextAlign.Center,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 96eb4b3a4c3f..475bb2c83b0e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -43,14 +43,14 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.accessibility.hearingaid.HearingDevicesListAdapter.HearingDeviceItemCallback;
import com.android.systemui.animation.DialogTransitionAnimator;
+import com.android.systemui.bluetooth.qsdialog.ActiveHearingDeviceItemFactory;
+import com.android.systemui.bluetooth.qsdialog.AvailableHearingDeviceItemFactory;
+import com.android.systemui.bluetooth.qsdialog.ConnectedDeviceItemFactory;
+import com.android.systemui.bluetooth.qsdialog.DeviceItem;
+import com.android.systemui.bluetooth.qsdialog.DeviceItemFactory;
+import com.android.systemui.bluetooth.qsdialog.SavedHearingDeviceItemFactory;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.qs.tiles.dialog.bluetooth.ActiveHearingDeviceItemFactory;
-import com.android.systemui.qs.tiles.dialog.bluetooth.AvailableHearingDeviceItemFactory;
-import com.android.systemui.qs.tiles.dialog.bluetooth.ConnectedDeviceItemFactory;
-import com.android.systemui.qs.tiles.dialog.bluetooth.DeviceItem;
-import com.android.systemui.qs.tiles.dialog.bluetooth.DeviceItemFactory;
-import com.android.systemui.qs.tiles.dialog.bluetooth.SavedHearingDeviceItemFactory;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java
index 695d04f5df9d..737805b4d62f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java
@@ -27,7 +27,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.systemui.qs.tiles.dialog.bluetooth.DeviceItem;
+import com.android.systemui.bluetooth.qsdialog.DeviceItem;
import com.android.systemui.res.R;
import kotlin.Pair;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractor.kt
index 59fc81c82df0..f86cad5b9c59 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractor.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt
index 9ee582a77862..81fe2a5a5f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
import android.util.Log
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt
index 9c63a30dfc1c..94d7af74f1dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothAdapter.STATE_OFF
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
index a8d9e781228b..c7d171d5b804 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.os.Bundle
import android.view.LayoutInflater
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
index 5d18dc1d453a..c30aea07e959 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel.DEBUG
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepository.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepository.kt
index ea51beecc2c1..6e51915797cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepository.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
import com.android.settingslib.bluetooth.CachedBluetoothDevice
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
index cd52e0dcca4a..add1647143d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
index fd624d2f1ba1..e65b65710f94 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.content.Intent
import android.content.SharedPreferences
@@ -31,15 +31,15 @@ import com.android.internal.logging.UiEventLogger
import com.android.systemui.Prefs
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_BLUETOOTH_DEVICE_DETAILS
+import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_PAIR_NEW_DEVICE
+import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_PREVIOUSLY_CONNECTED_DEVICE
+import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.MAX_DEVICE_ITEM_ENTRY
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialogDelegate.Companion.ACTION_BLUETOOTH_DEVICE_DETAILS
-import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialogDelegate.Companion.ACTION_PAIR_NEW_DEVICE
-import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialogDelegate.Companion.ACTION_PREVIOUSLY_CONNECTED_DEVICE
-import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialogDelegate.Companion.MAX_DEVICE_ITEM_ENTRY
import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt
index 1c621b87533d..dc5efefdfb16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt
@@ -30,7 +30,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.graphics.drawable.Drawable
import com.android.settingslib.bluetooth.CachedBluetoothDevice
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
index 56ba07941e4d..f04ba75ca3ef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothDevice
import android.content.Context
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
index fce25ec68190..4e28cafb5004 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 42bd4aff1dc4..ce1aed08ab49 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -621,7 +621,9 @@ constructor(
}
private suspend fun updateClockAppearance(clock: ClockController) {
- clockController.clock = clock
+ if (!MigrateClocksToBlueprint.isEnabled) {
+ clockController.clock = clock
+ }
val colors = wallpaperColors
if (clockRegistry.seedColor == null && colors != null) {
// Seed color null means users do not override any color on the clock. The default
@@ -639,6 +641,11 @@ constructor(
if (isWallpaperDark) lightClockColor else darkClockColor
)
}
+ // In clock preview, we should have a seed color for clock
+ // before setting clock to clockEventController to avoid updateColor with seedColor == null
+ if (MigrateClocksToBlueprint.isEnabled) {
+ clockController.clock = clock
+ }
}
private fun onClockChanged() {
if (MigrateClocksToBlueprint.isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index d2471225a093..f08bc17c4f23 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -25,8 +25,8 @@ import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
-import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
-import com.android.systemui.mediaprojection.appselector.data.IconLoaderLibAppIconLoader
+import com.android.systemui.mediaprojection.appselector.data.BasicAppIconLoader
+import com.android.systemui.mediaprojection.appselector.data.BasicPackageManagerAppIconLoader
import com.android.systemui.mediaprojection.appselector.data.RecentTaskLabelLoader
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
@@ -102,7 +102,7 @@ interface MediaProjectionAppSelectorModule {
@Binds
@MediaProjectionAppSelectorScope
- fun bindAppIconLoader(impl: IconLoaderLibAppIconLoader): AppIconLoader
+ fun bindAppIconLoader(impl: BasicPackageManagerAppIconLoader): BasicAppIconLoader
@Binds
@IntoSet
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/BadgedAppIconLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/BadgedAppIconLoader.kt
new file mode 100644
index 000000000000..ca5b5f842b25
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/BadgedAppIconLoader.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 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.mediaprojection.appselector.data
+
+import android.content.ComponentName
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.launcher3.icons.IconFactory
+import com.android.launcher3.util.UserIconInfo
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import javax.inject.Provider
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+class BadgedAppIconLoader
+@Inject
+constructor(
+ private val basicAppIconLoader: BasicAppIconLoader,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val context: Context,
+ private val iconFactoryProvider: Provider<IconFactory>,
+) {
+
+ suspend fun loadIcon(
+ userId: Int,
+ userType: RecentTask.UserType,
+ componentName: ComponentName
+ ): Drawable? =
+ withContext(backgroundDispatcher) {
+ iconFactoryProvider.get().use<IconFactory, Drawable?> { iconFactory ->
+ val icon =
+ basicAppIconLoader.loadIcon(userId, componentName) ?: return@withContext null
+ val userHandler = UserHandle.of(userId)
+ val iconType = getIconType(userType)
+ val options =
+ BaseIconFactory.IconOptions().apply {
+ setUser(UserIconInfo(userHandler, iconType))
+ }
+ val badgedIcon = iconFactory.createBadgedIconBitmap(icon, options)
+ badgedIcon.newIcon(context)
+ }
+ }
+
+ private fun getIconType(userType: RecentTask.UserType): Int =
+ when (userType) {
+ RecentTask.UserType.CLONED -> UserIconInfo.TYPE_CLONED
+ RecentTask.UserType.WORK -> UserIconInfo.TYPE_WORK
+ RecentTask.UserType.PRIVATE -> UserIconInfo.TYPE_PRIVATE
+ RecentTask.UserType.STANDARD -> UserIconInfo.TYPE_MAIN
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/BasicAppIconLoader.kt
index b85d6285c35b..03f6f015300f 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/BasicAppIconLoader.kt
@@ -17,45 +17,29 @@
package com.android.systemui.mediaprojection.appselector.data
import android.content.ComponentName
-import android.content.Context
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
-import android.os.UserHandle
-import com.android.launcher3.icons.BaseIconFactory.IconOptions
-import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.shared.system.PackageManagerWrapper
import javax.inject.Inject
-import javax.inject.Provider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
-interface AppIconLoader {
+interface BasicAppIconLoader {
suspend fun loadIcon(userId: Int, component: ComponentName): Drawable?
}
-class IconLoaderLibAppIconLoader
+class BasicPackageManagerAppIconLoader
@Inject
constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
- private val context: Context,
// Use wrapper to access hidden API that allows to get ActivityInfo for any user id
private val packageManagerWrapper: PackageManagerWrapper,
private val packageManager: PackageManager,
- private val iconFactoryProvider: Provider<IconFactory>
-) : AppIconLoader {
+) : BasicAppIconLoader {
override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? =
withContext(backgroundDispatcher) {
- iconFactoryProvider.get().use<IconFactory, Drawable?> { iconFactory ->
- val activityInfo =
- packageManagerWrapper.getActivityInfo(component, userId)
- ?: return@withContext null
- val icon = activityInfo.loadIcon(packageManager) ?: return@withContext null
- val userHandler = UserHandle.of(userId)
- val options = IconOptions().apply { setUser(userHandler) }
- val badgedIcon = iconFactory.createBadgedIconBitmap(icon, options)
- badgedIcon.newIcon(context)
- }
+ packageManagerWrapper.getActivityInfo(component, userId)?.loadIcon(packageManager)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
index e9b458271ef7..3e9b546d58c9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -28,4 +28,12 @@ data class RecentTask(
val baseIntentComponent: ComponentName?,
@ColorInt val colorBackground: Int?,
val isForegroundTask: Boolean,
-)
+ val userType: UserType,
+) {
+ enum class UserType {
+ STANDARD,
+ WORK,
+ PRIVATE,
+ CLONED
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 5dde14bf0867..a6049c8b556d 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -17,6 +17,8 @@
package com.android.systemui.mediaprojection.appselector.data
import android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE
+import android.content.pm.UserInfo
+import android.os.UserManager
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.kotlin.getOrNull
@@ -41,7 +43,8 @@ constructor(
@Background private val coroutineDispatcher: CoroutineDispatcher,
@Background private val backgroundExecutor: Executor,
private val recentTasks: Optional<RecentTasks>,
- private val userTracker: UserTracker
+ private val userTracker: UserTracker,
+ private val userManager: UserManager,
) : RecentTaskListProvider {
private val recents by lazy { recentTasks.getOrNull() }
@@ -65,7 +68,8 @@ constructor(
it.topActivity,
it.baseIntent?.component,
it.taskDescription?.backgroundColor,
- isForegroundTask = it.taskId in foregroundTaskIds && it.isVisible
+ isForegroundTask = it.taskId in foregroundTaskIds && it.isVisible,
+ userType = userManager.getUserInfo(it.userId).toUserType(),
)
}
}
@@ -81,4 +85,15 @@ constructor(
continuation.resume(tasks)
}
}
+
+ private fun UserInfo.toUserType(): RecentTask.UserType =
+ if (isCloneProfile) {
+ RecentTask.UserType.CLONED
+ } else if (isManagedProfile) {
+ RecentTask.UserType.WORK
+ } else if (isPrivateProfile) {
+ RecentTask.UserType.PRIVATE
+ } else {
+ RecentTask.UserType.STANDARD
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
index 3fe040a0d715..3b84d2c53a2b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
@@ -21,12 +21,12 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
-import com.android.systemui.res.R
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelector
-import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
+import com.android.systemui.mediaprojection.appselector.data.BadgedAppIconLoader
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.data.RecentTaskLabelLoader
import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -39,7 +39,7 @@ class RecentTaskViewHolder
@AssistedInject
constructor(
@Assisted private val root: ViewGroup,
- private val iconLoader: AppIconLoader,
+ private val iconLoader: BadgedAppIconLoader,
private val thumbnailLoader: RecentTaskThumbnailLoader,
private val labelLoader: RecentTaskLabelLoader,
private val taskViewSizeProvider: TaskPreviewSizeProvider,
@@ -63,7 +63,7 @@ constructor(
scope.launch {
task.baseIntentComponent?.let { component ->
launch {
- val icon = iconLoader.loadIcon(task.userId, component)
+ val icon = iconLoader.loadIcon(task.userId, task.userType, component)
iconView.setImageDrawable(icon)
}
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index b0707db0d02d..6eae32a0ffd6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -39,6 +39,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
@@ -51,7 +52,6 @@ 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.qs.tiles.dialog.bluetooth.BluetoothTileDialogViewModel;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BluetoothController;
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index d00081e0f595..1568e8c011a1 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -20,6 +20,9 @@ import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.app.WallpaperManager.SetWallpaperFlags;
+import static com.android.window.flags.Flags.offloadColorExtraction;
+
+import android.annotation.Nullable;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
@@ -135,6 +138,12 @@ public class ImageWallpaper extends WallpaperService {
mLongExecutor,
mLock,
new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() {
+
+ @Override
+ public void onColorsProcessed() {
+ CanvasEngine.this.notifyColorsChanged();
+ }
+
@Override
public void onColorsProcessed(List<RectF> regions,
List<WallpaperColors> colors) {
@@ -433,6 +442,12 @@ public class ImageWallpaper extends WallpaperService {
}
@Override
+ public @Nullable WallpaperColors onComputeColors() {
+ if (!offloadColorExtraction()) return null;
+ return mWallpaperLocalColorExtractor.onComputeColors();
+ }
+
+ @Override
public boolean supportsLocalColorExtraction() {
return true;
}
@@ -469,6 +484,12 @@ public class ImageWallpaper extends WallpaperService {
}
@Override
+ public void onDimAmountChanged(float dimAmount) {
+ if (!offloadColorExtraction()) return;
+ mWallpaperLocalColorExtractor.onDimAmountChanged(dimAmount);
+ }
+
+ @Override
public void onDisplayAdded(int displayId) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
index e2ec8dc056ca..d37dfb49c5e5 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
@@ -17,6 +17,8 @@
package com.android.systemui.wallpapers;
+import static com.android.window.flags.Flags.offloadColorExtraction;
+
import android.app.WallpaperColors;
import android.graphics.Bitmap;
import android.graphics.Rect;
@@ -66,6 +68,12 @@ public class WallpaperLocalColorExtractor {
private final List<RectF> mPendingRegions = new ArrayList<>();
private final Set<RectF> mProcessedRegions = new ArraySet<>();
+ private float mWallpaperDimAmount = 0f;
+ private WallpaperColors mWallpaperColors;
+
+ // By default we assume that colors were loaded from disk and don't need to be recomputed
+ private boolean mRecomputeColors = false;
+
@LongRunning
private final Executor mLongExecutor;
@@ -75,6 +83,12 @@ public class WallpaperLocalColorExtractor {
* Interface to handle the callbacks after the different steps of the color extraction
*/
public interface WallpaperLocalColorExtractorCallback {
+
+ /**
+ * Callback after the wallpaper colors have been computed
+ */
+ void onColorsProcessed();
+
/**
* Callback after the colors of new regions have been extracted
* @param regions the list of new regions that have been processed
@@ -129,7 +143,7 @@ public class WallpaperLocalColorExtractor {
if (displayWidth == mDisplayWidth && displayHeight == mDisplayHeight) return;
mDisplayWidth = displayWidth;
mDisplayHeight = displayHeight;
- processColorsInternal();
+ processLocalColorsInternal();
}
}
@@ -166,7 +180,8 @@ public class WallpaperLocalColorExtractor {
mBitmapHeight = bitmap.getHeight();
mMiniBitmap = createMiniBitmap(bitmap);
mWallpaperLocalColorExtractorCallback.onMiniBitmapUpdated();
- recomputeColors();
+ if (offloadColorExtraction() && mRecomputeColors) recomputeColorsInternal();
+ recomputeLocalColors();
}
}
@@ -184,16 +199,66 @@ public class WallpaperLocalColorExtractor {
if (mPages == pages) return;
mPages = pages;
if (mMiniBitmap != null && !mMiniBitmap.isRecycled()) {
- recomputeColors();
+ recomputeLocalColors();
}
}
}
- // helper to recompute colors, to be called in synchronized methods
- private void recomputeColors() {
+ /**
+ * Should be called when the dim amount of the wallpaper changes, to recompute the colors
+ */
+ public void onDimAmountChanged(float dimAmount) {
+ mLongExecutor.execute(() -> onDimAmountChangedSynchronized(dimAmount));
+ }
+
+ private void onDimAmountChangedSynchronized(float dimAmount) {
+ synchronized (mLock) {
+ if (mWallpaperDimAmount == dimAmount) return;
+ mWallpaperDimAmount = dimAmount;
+ mRecomputeColors = true;
+ recomputeColorsInternal();
+ }
+ }
+
+ /**
+ * To be called by {@link ImageWallpaper.CanvasEngine#onComputeColors}. This will either
+ * return the current wallpaper colors, or if the bitmap is not yet loaded, return null and call
+ * {@link WallpaperLocalColorExtractorCallback#onColorsProcessed()} when the colors are ready.
+ */
+ public WallpaperColors onComputeColors() {
+ mLongExecutor.execute(this::onComputeColorsSynchronized);
+ return mWallpaperColors;
+ }
+
+ private void onComputeColorsSynchronized() {
+ synchronized (mLock) {
+ if (mRecomputeColors) return;
+ mRecomputeColors = true;
+ recomputeColorsInternal();
+ }
+ }
+
+ /**
+ * helper to recompute main colors, to be called in synchronized methods
+ */
+ private void recomputeColorsInternal() {
+ if (mMiniBitmap == null) return;
+ mWallpaperColors = getWallpaperColors(mMiniBitmap, mWallpaperDimAmount);
+ mWallpaperLocalColorExtractorCallback.onColorsProcessed();
+ }
+
+ @VisibleForTesting
+ WallpaperColors getWallpaperColors(@NonNull Bitmap bitmap, float dimAmount) {
+ return WallpaperColors.fromBitmap(bitmap, dimAmount);
+ }
+
+ /**
+ * helper to recompute local colors, to be called in synchronized methods
+ */
+ private void recomputeLocalColors() {
mPendingRegions.addAll(mProcessedRegions);
mProcessedRegions.clear();
- processColorsInternal();
+ processLocalColorsInternal();
}
/**
@@ -216,7 +281,7 @@ public class WallpaperLocalColorExtractor {
if (!wasActive && isActive()) {
mWallpaperLocalColorExtractorCallback.onActivated();
}
- processColorsInternal();
+ processLocalColorsInternal();
}
}
@@ -353,7 +418,7 @@ public class WallpaperLocalColorExtractor {
* then notify the callback with the resulting colors for these regions
* This method should only be called synchronously
*/
- private void processColorsInternal() {
+ private void processLocalColorsInternal() {
/*
* if the miniBitmap is not yet loaded, that means the onBitmapChanged has not yet been
* called, and thus the wallpaper is not yet loaded. In that case, exit, the function
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index b0f0363a3e97..15764571ce02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -43,10 +43,10 @@ import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
+import com.android.systemui.bluetooth.qsdialog.DeviceItem;
+import com.android.systemui.bluetooth.qsdialog.DeviceItemType;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.qs.tiles.dialog.bluetooth.DeviceItem;
-import com.android.systemui.qs.tiles.dialog.bluetooth.DeviceItemType;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java
index 95d5597ef645..d16db65334d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java
@@ -27,7 +27,7 @@ import androidx.test.filters.SmallTest;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.qs.tiles.dialog.bluetooth.DeviceItem;
+import com.android.systemui.bluetooth.qsdialog.DeviceItem;
import org.junit.Before;
import org.junit.Rule;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt
index 036d3c862ae0..4949716ad129 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
import android.testing.AndroidTestingRunner
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt
index 31192841ec77..85e2a8d4b48e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
import android.testing.AndroidTestingRunner
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
index 479e62d4d724..a8f82eda51c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
index 17b612714fe2..12dfe97649d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
-import android.content.Context
import android.graphics.drawable.Drawable
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -121,23 +120,18 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
sysuiDialogFactory
)
- whenever(
- sysuiDialogFactory.create(
- any(SystemUIDialog.Delegate::class.java)
- )
+ whenever(sysuiDialogFactory.create(any(SystemUIDialog.Delegate::class.java))).thenAnswer {
+ SystemUIDialog(
+ mContext,
+ 0,
+ SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK,
+ dialogManager,
+ sysuiState,
+ fakeBroadcastDispatcher,
+ dialogTransitionAnimator,
+ it.getArgument(0)
)
- .thenAnswer {
- SystemUIDialog(
- mContext,
- 0,
- SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK,
- dialogManager,
- sysuiState,
- fakeBroadcastDispatcher,
- dialogTransitionAnimator,
- it.getArgument(0)
- )
- }
+ }
icon = Pair(drawable, DEVICE_NAME)
deviceItem =
@@ -163,6 +157,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
assertThat(recyclerView.visibility).isEqualTo(VISIBLE)
assertThat(recyclerView.adapter).isNotNull()
assertThat(recyclerView.layoutManager is LinearLayoutManager).isTrue()
+ dialog.dismiss()
}
@Test
@@ -184,6 +179,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
assertThat(adapter.getItem(0).deviceName).isEqualTo(DEVICE_NAME)
assertThat(adapter.getItem(0).connectionSummary).isEqualTo(DEVICE_CONNECTION_SUMMARY)
assertThat(adapter.getItem(0).iconWithDescription).isEqualTo(icon)
+ dialog.dismiss()
}
}
@@ -259,6 +255,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
assertThat(pairNewButton.visibility).isEqualTo(VISIBLE)
assertThat(adapter.itemCount).isEqualTo(1)
assertThat(scrollViewContent.layoutParams.height).isEqualTo(WRAP_CONTENT)
+ dialog.dismiss()
}
}
@@ -283,6 +280,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
dialog.show()
assertThat(dialog.requireViewById<View>(R.id.scroll_view).layoutParams.height)
.isEqualTo(cachedHeight)
+ dialog.dismiss()
}
}
@@ -306,6 +304,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
dialog.show()
assertThat(dialog.requireViewById<View>(R.id.scroll_view).layoutParams.height)
.isGreaterThan(MATCH_PARENT)
+ dialog.dismiss()
}
}
@@ -331,6 +330,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
dialog.requireViewById<View>(R.id.bluetooth_auto_on_toggle_layout).visibility
)
.isEqualTo(GONE)
+ dialog.dismiss()
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt
index da8f60a9b926..4aa6209fab3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
import android.testing.AndroidTestingRunner
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
index c8a2aa64ffa2..6d99c5b62e9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
import android.testing.AndroidTestingRunner
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
index a8cd8c801a95..28cbcb435223 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothDevice
import android.content.pm.PackageInfo
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
index ddf0b9a78165..eb735cbfec47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.dialog.bluetooth
+package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 44798ea99bee..8b79fa45b8ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -257,6 +257,7 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
userId = userId,
colorBackground = 0,
isForegroundTask = isForegroundTask,
+ userType = RecentTask.UserType.STANDARD,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/BasicPackageManagerAppIconLoaderTest.kt
index 9b346d0120ef..fa1c8f8ea2c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/BasicPackageManagerAppIconLoaderTest.kt
@@ -20,15 +20,10 @@ import android.content.ComponentName
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.graphics.Bitmap
-import android.graphics.drawable.Drawable
import androidx.test.filters.SmallTest
-import com.android.launcher3.icons.BitmapInfo
import com.android.launcher3.icons.FastBitmapDrawable
-import com.android.launcher3.icons.IconFactory
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.system.PackageManagerWrapper
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -40,20 +35,17 @@ import org.junit.runners.JUnit4
@SmallTest
@RunWith(JUnit4::class)
-class IconLoaderLibAppIconLoaderTest : SysuiTestCase() {
+class BasicPackageManagerAppIconLoaderTest : SysuiTestCase() {
- private val iconFactory: IconFactory = mock()
private val packageManagerWrapper: PackageManagerWrapper = mock()
private val packageManager: PackageManager = mock()
private val dispatcher = Dispatchers.Unconfined
private val appIconLoader =
- IconLoaderLibAppIconLoader(
+ BasicPackageManagerAppIconLoader(
backgroundDispatcher = dispatcher,
- context = context,
packageManagerWrapper = packageManagerWrapper,
packageManager = packageManager,
- iconFactoryProvider = { iconFactory }
)
@Test
@@ -70,12 +62,7 @@ class IconLoaderLibAppIconLoaderTest : SysuiTestCase() {
private fun givenIcon(component: ComponentName, userId: Int, icon: FastBitmapDrawable) {
val activityInfo = mock<ActivityInfo>()
whenever(packageManagerWrapper.getActivityInfo(component, userId)).thenReturn(activityInfo)
- val rawIcon = mock<Drawable>()
- whenever(activityInfo.loadIcon(packageManager)).thenReturn(rawIcon)
-
- val bitmapInfo = mock<BitmapInfo>()
- whenever(iconFactory.createBadgedIconBitmap(eq(rawIcon), any())).thenReturn(bitmapInfo)
- whenever(bitmapInfo.newIcon(context)).thenReturn(icon)
+ whenever(activityInfo.loadIcon(packageManager)).thenReturn(icon)
}
private fun createIcon(): FastBitmapDrawable =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
index b593def283ae..dd621129ad9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -1,9 +1,15 @@
package com.android.systemui.mediaprojection.appselector.data
import android.app.ActivityManager.RecentTaskInfo
+import android.content.pm.UserInfo
+import android.os.UserManager
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.mediaprojection.appselector.data.RecentTask.UserType.CLONED
+import com.android.systemui.mediaprojection.appselector.data.RecentTask.UserType.PRIVATE
+import com.android.systemui.mediaprojection.appselector.data.RecentTask.UserType.STANDARD
+import com.android.systemui.mediaprojection.appselector.data.RecentTask.UserType.WORK
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -17,6 +23,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -25,12 +32,16 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
private val dispatcher = Dispatchers.Unconfined
private val recentTasks: RecentTasks = mock()
private val userTracker: UserTracker = mock()
+ private val userManager: UserManager = mock {
+ whenever(getUserInfo(anyInt())).thenReturn(mock())
+ }
private val recentTaskListProvider =
ShellRecentTaskListProvider(
dispatcher,
Runnable::run,
Optional.of(recentTasks),
- userTracker
+ userTracker,
+ userManager,
)
@Test
@@ -147,6 +158,22 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
.inOrder()
}
+ @Test
+ fun loadRecentTasks_assignsCorrectUserType() {
+ givenRecentTasks(
+ createSingleTask(taskId = 1, userId = 10, userType = STANDARD),
+ createSingleTask(taskId = 2, userId = 20, userType = WORK),
+ createSingleTask(taskId = 3, userId = 30, userType = CLONED),
+ createSingleTask(taskId = 4, userId = 40, userType = PRIVATE),
+ )
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result.map { it.userType })
+ .containsExactly(STANDARD, WORK, CLONED, PRIVATE)
+ .inOrder()
+ }
+
@Suppress("UNCHECKED_CAST")
private fun givenRecentTasks(vararg tasks: GroupedRecentTaskInfo) {
whenever(recentTasks.getRecentTasks(any(), any(), any(), any(), any())).thenAnswer {
@@ -155,7 +182,10 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
}
}
- private fun createRecentTask(taskId: Int): RecentTask =
+ private fun createRecentTask(
+ taskId: Int,
+ userType: RecentTask.UserType = STANDARD
+ ): RecentTask =
RecentTask(
taskId = taskId,
displayId = 0,
@@ -164,25 +194,42 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
baseIntentComponent = null,
colorBackground = null,
isForegroundTask = false,
+ userType = userType,
)
- private fun createSingleTask(taskId: Int, isVisible: Boolean = false): GroupedRecentTaskInfo =
- GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId, isVisible))
+ private fun createSingleTask(
+ taskId: Int,
+ userId: Int = 0,
+ isVisible: Boolean = false,
+ userType: RecentTask.UserType = STANDARD,
+ ): GroupedRecentTaskInfo {
+ val userInfo =
+ mock<UserInfo> {
+ whenever(isCloneProfile).thenReturn(userType == CLONED)
+ whenever(isManagedProfile).thenReturn(userType == WORK)
+ whenever(isPrivateProfile).thenReturn(userType == PRIVATE)
+ }
+ whenever(userManager.getUserInfo(userId)).thenReturn(userInfo)
+ return GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId, userId, isVisible))
+ }
private fun createTaskPair(
taskId1: Int,
+ userId1: Int = 0,
taskId2: Int,
+ userId2: Int = 0,
isVisible: Boolean = false
): GroupedRecentTaskInfo =
GroupedRecentTaskInfo.forSplitTasks(
- createTaskInfo(taskId1, isVisible),
- createTaskInfo(taskId2, isVisible),
+ createTaskInfo(taskId1, userId1, isVisible),
+ createTaskInfo(taskId2, userId2, isVisible),
null
)
- private fun createTaskInfo(taskId: Int, isVisible: Boolean = false) =
+ private fun createTaskInfo(taskId: Int, userId: Int, isVisible: Boolean = false) =
RecentTaskInfo().apply {
this.taskId = taskId
this.isVisible = isVisible
+ this.userId = userId
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
index ac4107359dbc..a84008b0353c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
@@ -56,7 +56,8 @@ class MediaProjectionRecentsViewControllerTest : SysuiTestCase() {
topActivityComponent = null,
baseIntentComponent = null,
colorBackground = null,
- isForegroundTask = false
+ isForegroundTask = false,
+ userType = RecentTask.UserType.STANDARD,
)
private val taskView =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
index 82ee99a29427..830f08a0c445 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
@@ -23,7 +23,7 @@ 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.qs.tiles.dialog.bluetooth.BluetoothTileDialogViewModel
+import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel
import com.android.systemui.statusbar.policy.BluetoothController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
index 1125d41856c6..0eba21ada789 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
@@ -16,9 +16,12 @@
package com.android.systemui.wallpapers;
+import static com.android.window.flags.Flags.FLAG_OFFLOAD_COLOR_EXTRACTION;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
@@ -32,6 +35,7 @@ import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -77,6 +81,7 @@ public class WallpaperLocalColorExtractorTest extends SysuiTestCase {
private Executor mBackgroundExecutor;
private int mColorsProcessed;
+ private int mLocalColorsProcessed;
private int mMiniBitmapUpdatedCount;
private int mActivatedCount;
private int mDeactivatedCount;
@@ -93,6 +98,7 @@ public class WallpaperLocalColorExtractorTest extends SysuiTestCase {
private void resetCounters() {
mColorsProcessed = 0;
+ mLocalColorsProcessed = 0;
mMiniBitmapUpdatedCount = 0;
mActivatedCount = 0;
mDeactivatedCount = 0;
@@ -112,10 +118,14 @@ public class WallpaperLocalColorExtractorTest extends SysuiTestCase {
new Object(),
new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() {
@Override
+ public void onColorsProcessed() {
+ mColorsProcessed++;
+ }
+ @Override
public void onColorsProcessed(List<RectF> regions,
List<WallpaperColors> colors) {
assertThat(regions.size()).isEqualTo(colors.size());
- mColorsProcessed += regions.size();
+ mLocalColorsProcessed += regions.size();
}
@Override
@@ -148,8 +158,10 @@ public class WallpaperLocalColorExtractorTest extends SysuiTestCase {
.when(spyColorExtractor)
.createMiniBitmap(any(Bitmap.class), anyInt(), anyInt());
- doReturn(new WallpaperColors(Color.valueOf(0), Color.valueOf(0), Color.valueOf(0)))
- .when(spyColorExtractor).getLocalWallpaperColors(any(Rect.class));
+ WallpaperColors colors = new WallpaperColors(
+ Color.valueOf(0), Color.valueOf(0), Color.valueOf(0));
+ doReturn(colors).when(spyColorExtractor).getLocalWallpaperColors(any(Rect.class));
+ doReturn(colors).when(spyColorExtractor).getWallpaperColors(any(Bitmap.class), anyFloat());
return spyColorExtractor;
}
@@ -244,7 +256,7 @@ public class WallpaperLocalColorExtractorTest extends SysuiTestCase {
assertThat(mActivatedCount).isEqualTo(1);
assertThat(mMiniBitmapUpdatedCount).isEqualTo(1);
- assertThat(mColorsProcessed).isEqualTo(regions.size());
+ assertThat(mLocalColorsProcessed).isEqualTo(regions.size());
spyColorExtractor.removeLocalColorAreas(regions);
assertThat(mDeactivatedCount).isEqualTo(1);
@@ -329,12 +341,69 @@ public class WallpaperLocalColorExtractorTest extends SysuiTestCase {
spyColorExtractor.onBitmapChanged(newBitmap);
assertThat(mMiniBitmapUpdatedCount).isEqualTo(1);
}
- assertThat(mColorsProcessed).isEqualTo(regions.size());
+ assertThat(mLocalColorsProcessed).isEqualTo(regions.size());
}
spyColorExtractor.removeLocalColorAreas(regions);
assertThat(mDeactivatedCount).isEqualTo(1);
}
+ /**
+ * Test that after the bitmap changes, the colors are computed only if asked via onComputeColors
+ */
+ @Test
+ @EnableFlags(FLAG_OFFLOAD_COLOR_EXTRACTION)
+ public void testRecomputeColors() {
+ resetCounters();
+ Bitmap bitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+ WallpaperLocalColorExtractor spyColorExtractor = getSpyWallpaperLocalColorExtractor();
+ spyColorExtractor.onBitmapChanged(bitmap);
+ assertThat(mColorsProcessed).isEqualTo(0);
+ spyColorExtractor.onComputeColors();
+ assertThat(mColorsProcessed).isEqualTo(1);
+ }
+
+ /**
+ * Test that after onComputeColors is called, the colors are computed once the bitmap is loaded
+ */
+ @Test
+ @EnableFlags(FLAG_OFFLOAD_COLOR_EXTRACTION)
+ public void testRecomputeColorsBeforeBitmapLoaded() {
+ resetCounters();
+ Bitmap bitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+ WallpaperLocalColorExtractor spyColorExtractor = getSpyWallpaperLocalColorExtractor();
+ spyColorExtractor.onComputeColors();
+ spyColorExtractor.onBitmapChanged(bitmap);
+ assertThat(mColorsProcessed).isEqualTo(1);
+ }
+
+ /**
+ * Test that after the dim changes, the colors are computed if the bitmap is already loaded
+ */
+ @Test
+ @EnableFlags(FLAG_OFFLOAD_COLOR_EXTRACTION)
+ public void testRecomputeColorsOnDimChanged() {
+ resetCounters();
+ Bitmap bitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+ WallpaperLocalColorExtractor spyColorExtractor = getSpyWallpaperLocalColorExtractor();
+ spyColorExtractor.onBitmapChanged(bitmap);
+ spyColorExtractor.onDimAmountChanged(0.5f);
+ assertThat(mColorsProcessed).isEqualTo(1);
+ }
+
+ /**
+ * Test that after the dim changes, the colors will be recomputed once the bitmap is loaded
+ */
+ @Test
+ @EnableFlags(FLAG_OFFLOAD_COLOR_EXTRACTION)
+ public void testRecomputeColorsOnDimChangedBeforeBitmapLoaded() {
+ resetCounters();
+ Bitmap bitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+ WallpaperLocalColorExtractor spyColorExtractor = getSpyWallpaperLocalColorExtractor();
+ spyColorExtractor.onDimAmountChanged(0.3f);
+ spyColorExtractor.onBitmapChanged(bitmap);
+ assertThat(mColorsProcessed).isEqualTo(1);
+ }
+
@Test
public void testCleanUp() {
resetCounters();
@@ -346,6 +415,6 @@ public class WallpaperLocalColorExtractorTest extends SysuiTestCase {
assertThat(mMiniBitmapUpdatedCount).isEqualTo(1);
spyColorExtractor.cleanUp();
spyColorExtractor.addLocalColorsAreas(listOfRandomAreas(MIN_AREAS, MAX_AREAS));
- assertThat(mColorsProcessed).isEqualTo(0);
+ assertThat(mLocalColorsProcessed).isEqualTo(0);
}
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index c7d99424f72b..fd7e4abc6d16 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -254,6 +254,7 @@ java_library_static {
"net_flags_lib",
"stats_flags_lib",
"core_os_flags_lib",
+ "connectivity_flags_lib",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b6148e83d496..ad15ea90c45c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -725,6 +725,9 @@ public class ActivityManagerService extends IActivityManager.Stub
// Whether we should use SCHED_FIFO for UI and RenderThreads.
final boolean mUseFifoUiScheduling;
+ /** Whether some specified important processes are allowed to use FIFO priority. */
+ boolean mAllowSpecifiedFifoScheduling = true;
+
@GuardedBy("this")
private final SparseArray<IUnsafeIntentStrictModeCallback>
mStrictModeCallbacks = new SparseArray<>();
@@ -1048,6 +1051,10 @@ public class ActivityManagerService extends IActivityManager.Stub
@GuardedBy("this")
final SparseArray<ImportanceToken> mImportantProcesses = new SparseArray<ImportanceToken>();
+ /** The processes that are allowed to use SCHED_FIFO prorioty. */
+ @GuardedBy("mProcLock")
+ final ArrayList<ProcessRecord> mSpecifiedFifoProcesses = new ArrayList<>();
+
/**
* List of records for processes that someone had tried to start before the
* system was ready. We don't start them at that point, but ensure they
@@ -8244,6 +8251,27 @@ public class ActivityManagerService extends IActivityManager.Stub
return false;
}
+ /**
+ * Switches the priority between SCHED_FIFO and SCHED_OTHER for the main thread and render
+ * thread of the given process.
+ */
+ @GuardedBy("mProcLock")
+ static void setFifoPriority(@NonNull ProcessRecord app, boolean enable) {
+ final int pid = app.getPid();
+ final int renderThreadTid = app.getRenderThreadTid();
+ if (enable) {
+ scheduleAsFifoPriority(pid, true /* suppressLogs */);
+ if (renderThreadTid != 0) {
+ scheduleAsFifoPriority(renderThreadTid, true /* suppressLogs */);
+ }
+ } else {
+ scheduleAsRegularPriority(pid, true /* suppressLogs */);
+ if (renderThreadTid != 0) {
+ scheduleAsRegularPriority(renderThreadTid, true /* suppressLogs */);
+ }
+ }
+ }
+
@Override
public void setRenderThread(int tid) {
synchronized (mProcLock) {
@@ -8269,7 +8297,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// promote to FIFO now
if (proc.mState.getCurrentSchedulingGroup() == ProcessList.SCHED_GROUP_TOP_APP) {
if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band");
- if (mUseFifoUiScheduling) {
+ if (proc.useFifoUiScheduling()) {
setThreadScheduler(proc.getRenderThreadTid(),
SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
} else {
@@ -11306,6 +11334,9 @@ public class ActivityManagerService extends IActivityManager.Stub
if (mAlwaysFinishActivities) {
pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities);
}
+ if (mAllowSpecifiedFifoScheduling) {
+ pw.println(" mAllowSpecifiedFifoScheduling=true");
+ }
if (dumpAll) {
pw.println(" Total persistent processes: " + numPers);
pw.println(" mProcessesReady=" + mProcessesReady
@@ -17374,6 +17405,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
}
+
+ if (com.android.window.flags.Flags.fifoPriorityForMajorUiProcesses()) {
+ synchronized (mProcLock) {
+ adjustFifoProcessesIfNeeded(uid, !active /* allowFifo */);
+ }
+ }
}
final boolean isCameraActiveForUid(@UserIdInt int uid) {
@@ -17382,6 +17419,34 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ /**
+ * This is called when the given uid is using camera. If the uid has top process state, then
+ * cancel the FIFO priority of the high priority processes.
+ */
+ @VisibleForTesting
+ @GuardedBy("mProcLock")
+ void adjustFifoProcessesIfNeeded(int preemptiveUid, boolean allowSpecifiedFifo) {
+ if (allowSpecifiedFifo == mAllowSpecifiedFifoScheduling) {
+ return;
+ }
+ if (!allowSpecifiedFifo) {
+ final UidRecord uidRec = mProcessList.mActiveUids.get(preemptiveUid);
+ if (uidRec == null || uidRec.getCurProcState() > PROCESS_STATE_TOP) {
+ // To avoid frequent switching by background camera usages, e.g. face unlock,
+ // face detection (auto rotation), screen attention (keep screen on).
+ return;
+ }
+ }
+ mAllowSpecifiedFifoScheduling = allowSpecifiedFifo;
+ for (int i = mSpecifiedFifoProcesses.size() - 1; i >= 0; i--) {
+ final ProcessRecord proc = mSpecifiedFifoProcesses.get(i);
+ if (proc.mState.getSetSchedGroup() != ProcessList.SCHED_GROUP_TOP_APP) {
+ continue;
+ }
+ setFifoPriority(proc, allowSpecifiedFifo /* enable */);
+ }
+ }
+
@GuardedBy("this")
final void doStopUidLocked(int uid, final UidRecord uidRec) {
mServices.stopInBackgroundLocked(uid);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 5a750c2ba6c8..ea7a21dd19cd 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -72,7 +72,6 @@ import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYB
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL;
import static android.media.audio.Flags.roForegroundAudioControl;
-import static android.os.Process.SCHED_OTHER;
import static android.os.Process.THREAD_GROUP_BACKGROUND;
import static android.os.Process.THREAD_GROUP_DEFAULT;
import static android.os.Process.THREAD_GROUP_RESTRICTED;
@@ -81,7 +80,6 @@ import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
import static android.os.Process.setProcessGroup;
import static android.os.Process.setThreadPriority;
-import static android.os.Process.setThreadScheduler;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
@@ -3315,22 +3313,10 @@ public class OomAdjuster {
// do nothing if we already switched to RT
if (oldSchedGroup != SCHED_GROUP_TOP_APP) {
app.getWindowProcessController().onTopProcChanged();
- if (mService.mUseFifoUiScheduling) {
+ if (app.useFifoUiScheduling()) {
// Switch UI pipeline for app to SCHED_FIFO
state.setSavedPriority(Process.getThreadPriority(app.getPid()));
- mService.scheduleAsFifoPriority(app.getPid(), true);
- if (renderThreadTid != 0) {
- mService.scheduleAsFifoPriority(renderThreadTid,
- /* suppressLogs */true);
- if (DEBUG_OOM_ADJ) {
- Slog.d("UI_FIFO", "Set RenderThread (TID " +
- renderThreadTid + ") to FIFO");
- }
- } else {
- if (DEBUG_OOM_ADJ) {
- Slog.d("UI_FIFO", "Not setting RenderThread TID");
- }
- }
+ ActivityManagerService.setFifoPriority(app, true /* enable */);
} else {
// Boost priority for top app UI and render threads
setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
@@ -3347,22 +3333,10 @@ public class OomAdjuster {
} else if (oldSchedGroup == SCHED_GROUP_TOP_APP
&& curSchedGroup != SCHED_GROUP_TOP_APP) {
app.getWindowProcessController().onTopProcChanged();
- if (mService.mUseFifoUiScheduling) {
- try {
- // Reset UI pipeline to SCHED_OTHER
- setThreadScheduler(app.getPid(), SCHED_OTHER, 0);
- setThreadPriority(app.getPid(), state.getSavedPriority());
- if (renderThreadTid != 0) {
- setThreadScheduler(renderThreadTid,
- SCHED_OTHER, 0);
- }
- } catch (IllegalArgumentException e) {
- Slog.w(TAG,
- "Failed to set scheduling policy, thread does not exist:\n"
- + e);
- } catch (SecurityException e) {
- Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e);
- }
+ if (app.useFifoUiScheduling()) {
+ // Reset UI pipeline to SCHED_OTHER
+ ActivityManagerService.setFifoPriority(app, false /* enable */);
+ setThreadPriority(app.getPid(), state.getSavedPriority());
} else {
// Reset priority for top app UI and render threads
setThreadPriority(app.getPid(), 0);
@@ -3557,7 +3531,7 @@ public class OomAdjuster {
// {@link SCHED_GROUP_TOP_APP}. We don't check render thread because it
// is not ready when attaching.
app.getWindowProcessController().onTopProcChanged();
- if (mService.mUseFifoUiScheduling) {
+ if (app.useFifoUiScheduling()) {
mService.scheduleAsFifoPriority(app.getPid(), true);
} else {
setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b93908974a42..08165275757e 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -745,6 +745,9 @@ class ProcessRecord implements WindowProcessListener {
mOnewayThread = thread;
}
mWindowProcessController.setThread(thread);
+ if (mWindowProcessController.useFifoUiScheduling()) {
+ mService.mSpecifiedFifoProcesses.add(this);
+ }
}
@GuardedBy({"mService", "mProcLock"})
@@ -752,9 +755,19 @@ class ProcessRecord implements WindowProcessListener {
mThread = null;
mOnewayThread = null;
mWindowProcessController.setThread(null);
+ if (mWindowProcessController.useFifoUiScheduling()) {
+ mService.mSpecifiedFifoProcesses.remove(this);
+ }
mProfile.onProcessInactive(tracker);
}
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean useFifoUiScheduling() {
+ return mService.mUseFifoUiScheduling
+ || (mService.mAllowSpecifiedFifoScheduling
+ && mWindowProcessController.useFifoUiScheduling());
+ }
+
@GuardedBy("mService")
int getDyingPid() {
return mDyingPid;
diff --git a/services/core/java/com/android/server/connectivity/Android.bp b/services/core/java/com/android/server/connectivity/Android.bp
new file mode 100644
index 000000000000..a374ec2cea9a
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/Android.bp
@@ -0,0 +1,10 @@
+aconfig_declarations {
+ name: "connectivity_flags",
+ package: "com.android.server.connectivity",
+ srcs: ["flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "connectivity_flags_lib",
+ aconfig_declarations: "connectivity_flags",
+}
diff --git a/services/core/java/com/android/server/connectivity/flags.aconfig b/services/core/java/com/android/server/connectivity/flags.aconfig
new file mode 100644
index 000000000000..32593d4bcdaa
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.connectivity"
+
+flag {
+ name: "replace_vpn_profile_store"
+ namespace: "android_core_networking"
+ description: "This flag controls the usage of VpnBlobStore to replace LegacyVpnProfileStore."
+ bug: "307903113"
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 77119d5ac384..f32c11d90c0d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1197,54 +1197,11 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
- final InputDeviceIdentifier identifier) {
- return mKeyboardLayoutManager.getKeyboardLayoutsForInputDevice(identifier);
- }
-
- @Override // Binder call
public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
return mKeyboardLayoutManager.getKeyboardLayout(keyboardLayoutDescriptor);
}
@Override // Binder call
- public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
- return mKeyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(identifier);
- }
-
- @EnforcePermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
- @Override // Binder call
- public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- String keyboardLayoutDescriptor) {
- super.setCurrentKeyboardLayoutForInputDevice_enforcePermission();
- mKeyboardLayoutManager.setCurrentKeyboardLayoutForInputDevice(identifier,
- keyboardLayoutDescriptor);
- }
-
- @Override // Binder call
- public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
- return mKeyboardLayoutManager.getEnabledKeyboardLayoutsForInputDevice(identifier);
- }
-
- @EnforcePermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
- @Override // Binder call
- public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- String keyboardLayoutDescriptor) {
- super.addKeyboardLayoutForInputDevice_enforcePermission();
- mKeyboardLayoutManager.addKeyboardLayoutForInputDevice(identifier,
- keyboardLayoutDescriptor);
- }
-
- @EnforcePermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
- @Override // Binder call
- public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- String keyboardLayoutDescriptor) {
- super.removeKeyboardLayoutForInputDevice_enforcePermission();
- mKeyboardLayoutManager.removeKeyboardLayoutForInputDevice(identifier,
- keyboardLayoutDescriptor);
- }
-
- @Override // Binder call
public KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice(
InputDeviceIdentifier identifier, @UserIdInt int userId,
@NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) {
@@ -1270,11 +1227,6 @@ public class InputManagerService extends IInputManager.Stub
imeInfo, imeSubtype);
}
-
- public void switchKeyboardLayout(int deviceId, int direction) {
- mKeyboardLayoutManager.switchKeyboardLayout(deviceId, direction);
- }
-
public void setFocusedApplication(int displayId, InputApplicationHandle application) {
mNative.setFocusedApplication(displayId, application);
}
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 661008103a25..9ba647fb1d2d 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -60,7 +60,6 @@ import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -69,7 +68,6 @@ import android.view.KeyCharacterMap;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
-import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -96,7 +94,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.stream.Stream;
/**
* A component of {@link InputManagerService} responsible for managing Physical Keyboard layouts.
@@ -112,9 +109,8 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int MSG_UPDATE_EXISTING_DEVICES = 1;
- private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
- private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3;
- private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
+ private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 2;
+ private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 3;
private final Context mContext;
private final NativeInputManagerService mNative;
@@ -126,13 +122,14 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
// Connected keyboards with associated keyboard layouts (either auto-detected or manually
// selected layout).
private final SparseArray<KeyboardConfiguration> mConfiguredKeyboards = new SparseArray<>();
- private Toast mSwitchedKeyboardLayoutToast;
// This cache stores "best-matched" layouts so that we don't need to run the matching
// algorithm repeatedly.
@GuardedBy("mKeyboardLayoutCache")
private final Map<String, KeyboardLayoutSelectionResult> mKeyboardLayoutCache =
new ArrayMap<>();
+
+ private HashSet<String> mAvailableLayouts = new HashSet<>();
private final Object mImeInfoLock = new Object();
@Nullable
@GuardedBy("mImeInfoLock")
@@ -206,68 +203,51 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
boolean needToShowNotification = false;
- if (!useNewSettingsUi()) {
- synchronized (mDataStore) {
- String layout = getCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier());
- if (layout == null) {
- layout = getDefaultKeyboardLayout(inputDevice);
- if (layout != null) {
- setCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier(), layout);
- }
- }
- if (layout == null) {
- // In old settings show notification always until user manually selects a
- // layout in the settings.
- needToShowNotification = true;
- }
- }
- } else {
- Set<String> selectedLayouts = new HashSet<>();
- List<ImeInfo> imeInfoList = getImeInfoListForLayoutMapping();
- List<KeyboardLayoutSelectionResult> resultList = new ArrayList<>();
- boolean hasMissingLayout = false;
- for (ImeInfo imeInfo : imeInfoList) {
- // Check if the layout has been previously configured
- KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal(
- keyboardIdentifier, imeInfo);
- boolean noLayoutFound = result.getLayoutDescriptor() == null;
- if (!noLayoutFound) {
- selectedLayouts.add(result.getLayoutDescriptor());
- }
- resultList.add(result);
- hasMissingLayout |= noLayoutFound;
+ Set<String> selectedLayouts = new HashSet<>();
+ List<ImeInfo> imeInfoList = getImeInfoListForLayoutMapping();
+ List<KeyboardLayoutSelectionResult> resultList = new ArrayList<>();
+ boolean hasMissingLayout = false;
+ for (ImeInfo imeInfo : imeInfoList) {
+ // Check if the layout has been previously configured
+ KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal(
+ keyboardIdentifier, imeInfo);
+ if (result.getLayoutDescriptor() != null) {
+ selectedLayouts.add(result.getLayoutDescriptor());
+ } else {
+ hasMissingLayout = true;
}
+ resultList.add(result);
+ }
- if (DEBUG) {
- Slog.d(TAG,
- "Layouts selected for input device: " + keyboardIdentifier
- + " -> selectedLayouts: " + selectedLayouts);
- }
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Layouts selected for input device: " + keyboardIdentifier
+ + " -> selectedLayouts: " + selectedLayouts);
+ }
- // If even one layout not configured properly, we need to ask user to configure
- // the keyboard properly from the Settings.
- if (hasMissingLayout) {
- selectedLayouts.clear();
- }
+ // If even one layout not configured properly, we need to ask user to configure
+ // the keyboard properly from the Settings.
+ if (hasMissingLayout) {
+ selectedLayouts.clear();
+ }
- config.setConfiguredLayouts(selectedLayouts);
+ config.setConfiguredLayouts(selectedLayouts);
- synchronized (mDataStore) {
- try {
- final String key = keyboardIdentifier.toString();
- if (mDataStore.setSelectedKeyboardLayouts(key, selectedLayouts)) {
- // Need to show the notification only if layout selection changed
- // from the previous configuration
- needToShowNotification = true;
- }
+ synchronized (mDataStore) {
+ try {
+ final String key = keyboardIdentifier.toString();
+ if (mDataStore.setSelectedKeyboardLayouts(key, selectedLayouts)) {
+ // Need to show the notification only if layout selection changed
+ // from the previous configuration
+ needToShowNotification = true;
+ }
- if (shouldLogConfiguration) {
- logKeyboardConfigurationEvent(inputDevice, imeInfoList, resultList,
- !mDataStore.hasInputDeviceEntry(key));
- }
- } finally {
- mDataStore.saveIfNeeded();
+ if (shouldLogConfiguration) {
+ logKeyboardConfigurationEvent(inputDevice, imeInfoList, resultList,
+ !mDataStore.hasInputDeviceEntry(key));
}
+ } finally {
+ mDataStore.saveIfNeeded();
}
}
if (needToShowNotification) {
@@ -275,63 +255,6 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
}
- private String getDefaultKeyboardLayout(final InputDevice inputDevice) {
- final Locale systemLocale = mContext.getResources().getConfiguration().locale;
- // If our locale doesn't have a language for some reason, then we don't really have a
- // reasonable default.
- if (TextUtils.isEmpty(systemLocale.getLanguage())) {
- return null;
- }
- final List<KeyboardLayout> layouts = new ArrayList<>();
- visitAllKeyboardLayouts((resources, keyboardLayoutResId, layout) -> {
- // Only select a default when we know the layout is appropriate. For now, this
- // means it's a custom layout for a specific keyboard.
- if (layout.getVendorId() != inputDevice.getVendorId()
- || layout.getProductId() != inputDevice.getProductId()) {
- return;
- }
- final LocaleList locales = layout.getLocales();
- for (int localeIndex = 0; localeIndex < locales.size(); ++localeIndex) {
- final Locale locale = locales.get(localeIndex);
- if (locale != null && isCompatibleLocale(systemLocale, locale)) {
- layouts.add(layout);
- break;
- }
- }
- });
-
- if (layouts.isEmpty()) {
- return null;
- }
-
- // First sort so that ones with higher priority are listed at the top
- Collections.sort(layouts);
- // Next we want to try to find an exact match of language, country and variant.
- for (KeyboardLayout layout : layouts) {
- final LocaleList locales = layout.getLocales();
- for (int localeIndex = 0; localeIndex < locales.size(); ++localeIndex) {
- final Locale locale = locales.get(localeIndex);
- if (locale != null && locale.getCountry().equals(systemLocale.getCountry())
- && locale.getVariant().equals(systemLocale.getVariant())) {
- return layout.getDescriptor();
- }
- }
- }
- // Then try an exact match of language and country
- for (KeyboardLayout layout : layouts) {
- final LocaleList locales = layout.getLocales();
- for (int localeIndex = 0; localeIndex < locales.size(); ++localeIndex) {
- final Locale locale = locales.get(localeIndex);
- if (locale != null && locale.getCountry().equals(systemLocale.getCountry())) {
- return layout.getDescriptor();
- }
- }
- }
-
- // Give up and just use the highest priority layout with matching language
- return layouts.get(0).getDescriptor();
- }
-
private static boolean isCompatibleLocale(Locale systemLocale, Locale keyboardLocale) {
// Different languages are never compatible
if (!systemLocale.getLanguage().equals(keyboardLocale.getLanguage())) {
@@ -343,11 +266,19 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|| systemLocale.getCountry().equals(keyboardLocale.getCountry());
}
+ @MainThread
private void updateKeyboardLayouts() {
// Scan all input devices state for keyboard layouts that have been uninstalled.
- final HashSet<String> availableKeyboardLayouts = new HashSet<String>();
+ final HashSet<String> availableKeyboardLayouts = new HashSet<>();
visitAllKeyboardLayouts((resources, keyboardLayoutResId, layout) ->
availableKeyboardLayouts.add(layout.getDescriptor()));
+
+ // If available layouts don't change, there is no need to reload layouts.
+ if (mAvailableLayouts.equals(availableKeyboardLayouts)) {
+ return;
+ }
+ mAvailableLayouts = availableKeyboardLayouts;
+
synchronized (mDataStore) {
try {
mDataStore.removeUninstalledKeyboardLayouts(availableKeyboardLayouts);
@@ -374,53 +305,6 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
@AnyThread
- public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
- final InputDeviceIdentifier identifier) {
- if (useNewSettingsUi()) {
- // Provide all supported keyboard layouts since Ime info is not provided
- return getKeyboardLayouts();
- }
- final String[] enabledLayoutDescriptors =
- getEnabledKeyboardLayoutsForInputDevice(identifier);
- final ArrayList<KeyboardLayout> enabledLayouts =
- new ArrayList<>(enabledLayoutDescriptors.length);
- final ArrayList<KeyboardLayout> potentialLayouts = new ArrayList<>();
- visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
- boolean mHasSeenDeviceSpecificLayout;
-
- @Override
- public void visitKeyboardLayout(Resources resources,
- int keyboardLayoutResId, KeyboardLayout layout) {
- // First check if it's enabled. If the keyboard layout is enabled then we always
- // want to return it as a possible layout for the device.
- for (String s : enabledLayoutDescriptors) {
- if (s != null && s.equals(layout.getDescriptor())) {
- enabledLayouts.add(layout);
- return;
- }
- }
- // Next find any potential layouts that aren't yet enabled for the device. For
- // devices that have special layouts we assume there's a reason that the generic
- // layouts don't work for them so we don't want to return them since it's likely
- // to result in a poor user experience.
- if (layout.getVendorId() == identifier.getVendorId()
- && layout.getProductId() == identifier.getProductId()) {
- if (!mHasSeenDeviceSpecificLayout) {
- mHasSeenDeviceSpecificLayout = true;
- potentialLayouts.clear();
- }
- potentialLayouts.add(layout);
- } else if (layout.getVendorId() == -1 && layout.getProductId() == -1
- && !mHasSeenDeviceSpecificLayout) {
- potentialLayouts.add(layout);
- }
- }
- });
- return Stream.concat(enabledLayouts.stream(), potentialLayouts.stream()).toArray(
- KeyboardLayout[]::new);
- }
-
- @AnyThread
@Nullable
public KeyboardLayout getKeyboardLayout(@NonNull String keyboardLayoutDescriptor) {
Objects.requireNonNull(keyboardLayoutDescriptor,
@@ -580,195 +464,16 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
return LocaleList.forLanguageTags(languageTags.replace('|', ','));
}
- @AnyThread
- @Nullable
- public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
- if (useNewSettingsUi()) {
- Slog.e(TAG, "getCurrentKeyboardLayoutForInputDevice API not supported");
- return null;
- }
- String key = new KeyboardIdentifier(identifier).toString();
- synchronized (mDataStore) {
- String layout;
- // try loading it using the layout descriptor if we have it
- layout = mDataStore.getCurrentKeyboardLayout(key);
- if (layout == null && !key.equals(identifier.getDescriptor())) {
- // if it doesn't exist fall back to the device descriptor
- layout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
- }
- if (DEBUG) {
- Slog.d(TAG, "getCurrentKeyboardLayoutForInputDevice() "
- + identifier.toString() + ": " + layout);
- }
- return layout;
- }
- }
-
- @AnyThread
- public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- String keyboardLayoutDescriptor) {
- if (useNewSettingsUi()) {
- Slog.e(TAG, "setCurrentKeyboardLayoutForInputDevice API not supported");
- return;
- }
-
- Objects.requireNonNull(keyboardLayoutDescriptor,
- "keyboardLayoutDescriptor must not be null");
- String key = new KeyboardIdentifier(identifier).toString();
- synchronized (mDataStore) {
- try {
- if (mDataStore.setCurrentKeyboardLayout(key, keyboardLayoutDescriptor)) {
- if (DEBUG) {
- Slog.d(TAG, "setCurrentKeyboardLayoutForInputDevice() " + identifier
- + " key: " + key
- + " keyboardLayoutDescriptor: " + keyboardLayoutDescriptor);
- }
- mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
- }
- } finally {
- mDataStore.saveIfNeeded();
- }
- }
- }
-
- @AnyThread
- public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
- if (useNewSettingsUi()) {
- Slog.e(TAG, "getEnabledKeyboardLayoutsForInputDevice API not supported");
- return new String[0];
- }
- String key = new KeyboardIdentifier(identifier).toString();
- synchronized (mDataStore) {
- String[] layouts = mDataStore.getKeyboardLayouts(key);
- if ((layouts == null || layouts.length == 0)
- && !key.equals(identifier.getDescriptor())) {
- layouts = mDataStore.getKeyboardLayouts(identifier.getDescriptor());
- }
- return layouts;
- }
- }
-
- @AnyThread
- public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- String keyboardLayoutDescriptor) {
- if (useNewSettingsUi()) {
- Slog.e(TAG, "addKeyboardLayoutForInputDevice API not supported");
- return;
- }
- Objects.requireNonNull(keyboardLayoutDescriptor,
- "keyboardLayoutDescriptor must not be null");
-
- String key = new KeyboardIdentifier(identifier).toString();
- synchronized (mDataStore) {
- try {
- String oldLayout = mDataStore.getCurrentKeyboardLayout(key);
- if (oldLayout == null && !key.equals(identifier.getDescriptor())) {
- oldLayout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
- }
- if (mDataStore.addKeyboardLayout(key, keyboardLayoutDescriptor)
- && !Objects.equals(oldLayout,
- mDataStore.getCurrentKeyboardLayout(key))) {
- mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
- }
- } finally {
- mDataStore.saveIfNeeded();
- }
- }
- }
-
- @AnyThread
- public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- String keyboardLayoutDescriptor) {
- if (useNewSettingsUi()) {
- Slog.e(TAG, "removeKeyboardLayoutForInputDevice API not supported");
- return;
- }
- Objects.requireNonNull(keyboardLayoutDescriptor,
- "keyboardLayoutDescriptor must not be null");
-
- String key = new KeyboardIdentifier(identifier).toString();
- synchronized (mDataStore) {
- try {
- String oldLayout = mDataStore.getCurrentKeyboardLayout(key);
- if (oldLayout == null && !key.equals(identifier.getDescriptor())) {
- oldLayout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
- }
- boolean removed = mDataStore.removeKeyboardLayout(key, keyboardLayoutDescriptor);
- if (!key.equals(identifier.getDescriptor())) {
- // We need to remove from both places to ensure it is gone
- removed |= mDataStore.removeKeyboardLayout(identifier.getDescriptor(),
- keyboardLayoutDescriptor);
- }
- if (removed && !Objects.equals(oldLayout,
- mDataStore.getCurrentKeyboardLayout(key))) {
- mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
- }
- } finally {
- mDataStore.saveIfNeeded();
- }
- }
- }
-
- @AnyThread
- public void switchKeyboardLayout(int deviceId, int direction) {
- if (useNewSettingsUi()) {
- Slog.e(TAG, "switchKeyboardLayout API not supported");
- return;
- }
- mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, deviceId, direction).sendToTarget();
- }
-
- @MainThread
- private void handleSwitchKeyboardLayout(int deviceId, int direction) {
- final InputDevice device = getInputDevice(deviceId);
- if (device != null) {
- final boolean changed;
- final String keyboardLayoutDescriptor;
-
- String key = new KeyboardIdentifier(device.getIdentifier()).toString();
- synchronized (mDataStore) {
- try {
- changed = mDataStore.switchKeyboardLayout(key, direction);
- keyboardLayoutDescriptor = mDataStore.getCurrentKeyboardLayout(
- key);
- } finally {
- mDataStore.saveIfNeeded();
- }
- }
-
- if (changed) {
- if (mSwitchedKeyboardLayoutToast != null) {
- mSwitchedKeyboardLayoutToast.cancel();
- mSwitchedKeyboardLayoutToast = null;
- }
- if (keyboardLayoutDescriptor != null) {
- KeyboardLayout keyboardLayout = getKeyboardLayout(keyboardLayoutDescriptor);
- if (keyboardLayout != null) {
- mSwitchedKeyboardLayoutToast = Toast.makeText(
- mContext, keyboardLayout.getLabel(), Toast.LENGTH_SHORT);
- mSwitchedKeyboardLayoutToast.show();
- }
- }
-
- reloadKeyboardLayouts();
- }
- }
- }
-
@Nullable
@AnyThread
public String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier, String languageTag,
String layoutType) {
String keyboardLayoutDescriptor;
- if (useNewSettingsUi()) {
- synchronized (mImeInfoLock) {
- KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal(
- new KeyboardIdentifier(identifier, languageTag, layoutType),
- mCurrentImeInfo);
- keyboardLayoutDescriptor = result.getLayoutDescriptor();
- }
- } else {
- keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(identifier);
+ synchronized (mImeInfoLock) {
+ KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal(
+ new KeyboardIdentifier(identifier, languageTag, layoutType),
+ mCurrentImeInfo);
+ keyboardLayoutDescriptor = result.getLayoutDescriptor();
}
if (keyboardLayoutDescriptor == null) {
return null;
@@ -797,10 +502,6 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
public KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice(
InputDeviceIdentifier identifier, @UserIdInt int userId,
@NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) {
- if (!useNewSettingsUi()) {
- Slog.e(TAG, "getKeyboardLayoutForInputDevice() API not supported");
- return FAILED;
- }
InputDevice inputDevice = getInputDevice(identifier);
if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
return FAILED;
@@ -820,10 +521,6 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
@UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
@Nullable InputMethodSubtype imeSubtype,
String keyboardLayoutDescriptor) {
- if (!useNewSettingsUi()) {
- Slog.e(TAG, "setKeyboardLayoutForInputDevice() API not supported");
- return;
- }
Objects.requireNonNull(keyboardLayoutDescriptor,
"keyboardLayoutDescriptor must not be null");
InputDevice inputDevice = getInputDevice(identifier);
@@ -854,10 +551,6 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
public KeyboardLayout[] getKeyboardLayoutListForInputDevice(InputDeviceIdentifier identifier,
@UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
@Nullable InputMethodSubtype imeSubtype) {
- if (!useNewSettingsUi()) {
- Slog.e(TAG, "getKeyboardLayoutListForInputDevice() API not supported");
- return new KeyboardLayout[0];
- }
InputDevice inputDevice = getInputDevice(identifier);
if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
return new KeyboardLayout[0];
@@ -923,10 +616,6 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
public void onInputMethodSubtypeChanged(@UserIdInt int userId,
@Nullable InputMethodSubtypeHandle subtypeHandle,
@Nullable InputMethodSubtype subtype) {
- if (!useNewSettingsUi()) {
- Slog.e(TAG, "onInputMethodSubtypeChanged() API not supported");
- return;
- }
if (subtypeHandle == null) {
if (DEBUG) {
Slog.d(TAG, "No InputMethod is running, ignoring change");
@@ -1289,9 +978,6 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
onInputDeviceAdded(deviceId);
}
return true;
- case MSG_SWITCH_KEYBOARD_LAYOUT:
- handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
- return true;
case MSG_RELOAD_KEYBOARD_LAYOUTS:
reloadKeyboardLayouts();
return true;
@@ -1303,10 +989,6 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
}
- private boolean useNewSettingsUi() {
- return FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI);
- }
-
@Nullable
private InputDevice getInputDevice(int deviceId) {
InputManager inputManager = mContext.getSystemService(InputManager.class);
diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java
index 31083fd5de03..7859253d66e0 100644
--- a/services/core/java/com/android/server/input/PersistentDataStore.java
+++ b/services/core/java/com/android/server/input/PersistentDataStore.java
@@ -27,7 +27,6 @@ import android.util.Xml;
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -42,7 +41,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -74,7 +72,7 @@ final class PersistentDataStore {
new HashMap<String, InputDeviceState>();
// The interface for methods which should be replaced by the test harness.
- private Injector mInjector;
+ private final Injector mInjector;
// True if the data has been loaded.
private boolean mLoaded;
@@ -83,7 +81,7 @@ final class PersistentDataStore {
private boolean mDirty;
// Storing key remapping
- private Map<Integer, Integer> mKeyRemapping = new HashMap<>();
+ private final Map<Integer, Integer> mKeyRemapping = new HashMap<>();
public PersistentDataStore() {
this(new Injector());
@@ -130,22 +128,6 @@ final class PersistentDataStore {
}
@Nullable
- public String getCurrentKeyboardLayout(String inputDeviceDescriptor) {
- InputDeviceState state = getInputDeviceState(inputDeviceDescriptor);
- return state != null ? state.getCurrentKeyboardLayout() : null;
- }
-
- public boolean setCurrentKeyboardLayout(String inputDeviceDescriptor,
- String keyboardLayoutDescriptor) {
- InputDeviceState state = getOrCreateInputDeviceState(inputDeviceDescriptor);
- if (state.setCurrentKeyboardLayout(keyboardLayoutDescriptor)) {
- setDirty();
- return true;
- }
- return false;
- }
-
- @Nullable
public String getKeyboardLayout(String inputDeviceDescriptor, String key) {
InputDeviceState state = getInputDeviceState(inputDeviceDescriptor);
return state != null ? state.getKeyboardLayout(key) : null;
@@ -171,43 +153,6 @@ final class PersistentDataStore {
return false;
}
- public String[] getKeyboardLayouts(String inputDeviceDescriptor) {
- InputDeviceState state = getInputDeviceState(inputDeviceDescriptor);
- if (state == null) {
- return (String[])ArrayUtils.emptyArray(String.class);
- }
- return state.getKeyboardLayouts();
- }
-
- public boolean addKeyboardLayout(String inputDeviceDescriptor,
- String keyboardLayoutDescriptor) {
- InputDeviceState state = getOrCreateInputDeviceState(inputDeviceDescriptor);
- if (state.addKeyboardLayout(keyboardLayoutDescriptor)) {
- setDirty();
- return true;
- }
- return false;
- }
-
- public boolean removeKeyboardLayout(String inputDeviceDescriptor,
- String keyboardLayoutDescriptor) {
- InputDeviceState state = getOrCreateInputDeviceState(inputDeviceDescriptor);
- if (state.removeKeyboardLayout(keyboardLayoutDescriptor)) {
- setDirty();
- return true;
- }
- return false;
- }
-
- public boolean switchKeyboardLayout(String inputDeviceDescriptor, int direction) {
- InputDeviceState state = getInputDeviceState(inputDeviceDescriptor);
- if (state != null && state.switchKeyboardLayout(direction)) {
- setDirty();
- return true;
- }
- return false;
- }
-
public boolean setKeyboardBacklightBrightness(String inputDeviceDescriptor, int lightId,
int brightness) {
InputDeviceState state = getOrCreateInputDeviceState(inputDeviceDescriptor);
@@ -417,9 +362,6 @@ final class PersistentDataStore {
"x_ymix", "x_offset", "y_xmix", "y_scale", "y_offset" };
private final TouchCalibration[] mTouchCalibration = new TouchCalibration[4];
- @Nullable
- private String mCurrentKeyboardLayout;
- private final ArrayList<String> mKeyboardLayouts = new ArrayList<String>();
private final SparseIntArray mKeyboardBacklightBrightnessMap = new SparseIntArray();
private final Map<String, String> mKeyboardLayoutMap = new ArrayMap<>();
@@ -465,49 +407,6 @@ final class PersistentDataStore {
return true;
}
- @Nullable
- public String getCurrentKeyboardLayout() {
- return mCurrentKeyboardLayout;
- }
-
- public boolean setCurrentKeyboardLayout(String keyboardLayout) {
- if (Objects.equals(mCurrentKeyboardLayout, keyboardLayout)) {
- return false;
- }
- addKeyboardLayout(keyboardLayout);
- mCurrentKeyboardLayout = keyboardLayout;
- return true;
- }
-
- public String[] getKeyboardLayouts() {
- if (mKeyboardLayouts.isEmpty()) {
- return (String[])ArrayUtils.emptyArray(String.class);
- }
- return mKeyboardLayouts.toArray(new String[mKeyboardLayouts.size()]);
- }
-
- public boolean addKeyboardLayout(String keyboardLayout) {
- int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout);
- if (index >= 0) {
- return false;
- }
- mKeyboardLayouts.add(-index - 1, keyboardLayout);
- if (mCurrentKeyboardLayout == null) {
- mCurrentKeyboardLayout = keyboardLayout;
- }
- return true;
- }
-
- public boolean removeKeyboardLayout(String keyboardLayout) {
- int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout);
- if (index < 0) {
- return false;
- }
- mKeyboardLayouts.remove(index);
- updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, index);
- return true;
- }
-
public boolean setKeyboardBacklightBrightness(int lightId, int brightness) {
if (mKeyboardBacklightBrightnessMap.get(lightId, INVALID_VALUE) == brightness) {
return false;
@@ -521,48 +420,8 @@ final class PersistentDataStore {
return brightness == INVALID_VALUE ? OptionalInt.empty() : OptionalInt.of(brightness);
}
- private void updateCurrentKeyboardLayoutIfRemoved(
- String removedKeyboardLayout, int removedIndex) {
- if (Objects.equals(mCurrentKeyboardLayout, removedKeyboardLayout)) {
- if (!mKeyboardLayouts.isEmpty()) {
- int index = removedIndex;
- if (index == mKeyboardLayouts.size()) {
- index = 0;
- }
- mCurrentKeyboardLayout = mKeyboardLayouts.get(index);
- } else {
- mCurrentKeyboardLayout = null;
- }
- }
- }
-
- public boolean switchKeyboardLayout(int direction) {
- final int size = mKeyboardLayouts.size();
- if (size < 2) {
- return false;
- }
- int index = Collections.binarySearch(mKeyboardLayouts, mCurrentKeyboardLayout);
- assert index >= 0;
- if (direction > 0) {
- index = (index + 1) % size;
- } else {
- index = (index + size - 1) % size;
- }
- mCurrentKeyboardLayout = mKeyboardLayouts.get(index);
- return true;
- }
-
public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
boolean changed = false;
- for (int i = mKeyboardLayouts.size(); i-- > 0; ) {
- String keyboardLayout = mKeyboardLayouts.get(i);
- if (!availableKeyboardLayouts.contains(keyboardLayout)) {
- Slog.i(TAG, "Removing uninstalled keyboard layout " + keyboardLayout);
- mKeyboardLayouts.remove(i);
- updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, i);
- changed = true;
- }
- }
List<String> removedEntries = new ArrayList<>();
for (String key : mKeyboardLayoutMap.keySet()) {
if (!availableKeyboardLayouts.contains(mKeyboardLayoutMap.get(key))) {
@@ -582,27 +441,7 @@ final class PersistentDataStore {
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (parser.getName().equals("keyboard-layout")) {
- String descriptor = parser.getAttributeValue(null, "descriptor");
- if (descriptor == null) {
- throw new XmlPullParserException(
- "Missing descriptor attribute on keyboard-layout.");
- }
- String current = parser.getAttributeValue(null, "current");
- if (mKeyboardLayouts.contains(descriptor)) {
- throw new XmlPullParserException(
- "Found duplicate keyboard layout.");
- }
-
- mKeyboardLayouts.add(descriptor);
- if (current != null && current.equals("true")) {
- if (mCurrentKeyboardLayout != null) {
- throw new XmlPullParserException(
- "Found multiple current keyboard layouts.");
- }
- mCurrentKeyboardLayout = descriptor;
- }
- } else if (parser.getName().equals("keyed-keyboard-layout")) {
+ if (parser.getName().equals("keyed-keyboard-layout")) {
String key = parser.getAttributeValue(null, "key");
if (key == null) {
throw new XmlPullParserException(
@@ -676,27 +515,9 @@ final class PersistentDataStore {
}
}
}
-
- // Maintain invariant that layouts are sorted.
- Collections.sort(mKeyboardLayouts);
-
- // Maintain invariant that there is always a current keyboard layout unless
- // there are none installed.
- if (mCurrentKeyboardLayout == null && !mKeyboardLayouts.isEmpty()) {
- mCurrentKeyboardLayout = mKeyboardLayouts.get(0);
- }
}
public void saveToXml(TypedXmlSerializer serializer) throws IOException {
- for (String layout : mKeyboardLayouts) {
- serializer.startTag(null, "keyboard-layout");
- serializer.attribute(null, "descriptor", layout);
- if (layout.equals(mCurrentKeyboardLayout)) {
- serializer.attributeBoolean(null, "current", true);
- }
- serializer.endTag(null, "keyboard-layout");
- }
-
for (String key : mKeyboardLayoutMap.keySet()) {
serializer.startTag(null, "keyed-keyboard-layout");
serializer.attribute(null, "key", key);
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 0cd7654f70ea..dfb2b0a750e3 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -157,6 +157,11 @@ public class MediaSession2Record extends MediaSessionRecordImpl {
}
@Override
+ public void expireTempEngaged() {
+ // NA as MediaSession2 doesn't support UserEngagementStates for FGS.
+ }
+
+ @Override
public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
KeyEvent ke, int sequenceId, ResultReceiver cb) {
// TODO(jaewan): Implement.
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index eb4e6e44c8a4..8f164d361a56 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -24,6 +24,7 @@ import static android.media.session.MediaController.PlaybackInfo.PLAYBACK_TYPE_L
import static android.media.session.MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -85,6 +86,8 @@ import com.android.server.LocalServices;
import com.android.server.uri.UriGrantsManagerInternal;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -225,6 +228,49 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
private int mPolicies;
+ private @UserEngagementState int mUserEngagementState = USER_DISENGAGED;
+
+ @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARY_ENGAGED, USER_DISENGAGED})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface UserEngagementState {}
+
+ /**
+ * Indicates that the session is active and in one of the user engaged states.
+ *
+ * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
+ */
+ private static final int USER_PERMANENTLY_ENGAGED = 0;
+
+ /**
+ * Indicates that the session is active and in {@link PlaybackState#STATE_PAUSED} state.
+ *
+ * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
+ */
+ private static final int USER_TEMPORARY_ENGAGED = 1;
+
+ /**
+ * Indicates that the session is either not active or in one of the user disengaged states
+ *
+ * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
+ */
+ private static final int USER_DISENGAGED = 2;
+
+ /**
+ * Indicates the duration of the temporary engaged states.
+ *
+ * <p>Some {@link MediaSession} states like {@link PlaybackState#STATE_PAUSED} are temporarily
+ * engaged, meaning the corresponding session is only considered in an engaged state for the
+ * duration of this timeout, and only if coming from an engaged state.
+ *
+ * <p>For example, if a session is transitioning from a user-engaged state {@link
+ * PlaybackState#STATE_PLAYING} to a temporary user-engaged state {@link
+ * PlaybackState#STATE_PAUSED}, then the session will be considered in a user-engaged state for
+ * the duration of this timeout, starting at the transition instant. However, a temporary
+ * user-engaged state is not considered user-engaged when transitioning from a non-user engaged
+ * state {@link PlaybackState#STATE_STOPPED}.
+ */
+ private static final int TEMP_USER_ENGAGED_TIMEOUT = 600000;
+
public MediaSessionRecord(
int ownerPid,
int ownerUid,
@@ -548,6 +594,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
mSessionCb.mCb.asBinder().unlinkToDeath(this, 0);
mDestroyed = true;
mPlaybackState = null;
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
mHandler.post(MessageHandler.MSG_DESTROYED);
}
}
@@ -559,6 +606,12 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
}
+ @Override
+ public void expireTempEngaged() {
+ mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout);
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+ }
+
/**
* Sends media button.
*
@@ -849,7 +902,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
}
if (deadCallbackHolders != null) {
- mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ removeControllerHoldersSafely(deadCallbackHolders);
}
}
@@ -876,7 +929,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
}
if (deadCallbackHolders != null) {
- mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ removeControllerHoldersSafely(deadCallbackHolders);
}
}
@@ -911,7 +964,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
}
if (deadCallbackHolders != null) {
- mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ removeControllerHoldersSafely(deadCallbackHolders);
}
}
@@ -938,7 +991,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
}
if (deadCallbackHolders != null) {
- mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ removeControllerHoldersSafely(deadCallbackHolders);
}
}
@@ -965,7 +1018,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
}
if (deadCallbackHolders != null) {
- mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ removeControllerHoldersSafely(deadCallbackHolders);
}
}
@@ -992,7 +1045,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
}
if (deadCallbackHolders != null) {
- mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ removeControllerHoldersSafely(deadCallbackHolders);
}
}
@@ -1017,7 +1070,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
}
if (deadCallbackHolders != null) {
- mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ removeControllerHoldersSafely(deadCallbackHolders);
}
}
@@ -1042,7 +1095,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
}
// After notifying clear all listeners
- mControllerCallbackHolders.clear();
+ removeControllerHoldersSafely(null);
}
private PlaybackState getStateWithUpdatedPosition() {
@@ -1090,6 +1143,17 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
return -1;
}
+ private void removeControllerHoldersSafely(
+ Collection<ISessionControllerCallbackHolder> holders) {
+ synchronized (mLock) {
+ if (holders == null) {
+ mControllerCallbackHolders.clear();
+ } else {
+ mControllerCallbackHolders.removeAll(holders);
+ }
+ }
+ }
+
private PlaybackInfo getVolumeAttributes() {
int volumeType;
AudioAttributes attributes;
@@ -1118,6 +1182,11 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
};
+ private final Runnable mHandleTempEngagedSessionTimeout =
+ () -> {
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+ };
+
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
private static boolean componentNameExists(
@NonNull ComponentName componentName, @NonNull Context context, int userId) {
@@ -1134,6 +1203,40 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
return !resolveInfos.isEmpty();
}
+ private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) {
+ int oldUserEngagedState = mUserEngagementState;
+ int newUserEngagedState;
+ if (!isActive() || mPlaybackState == null) {
+ newUserEngagedState = USER_DISENGAGED;
+ } else if (isActive() && mPlaybackState.isActive()) {
+ newUserEngagedState = USER_PERMANENTLY_ENGAGED;
+ } else if (mPlaybackState.getState() == PlaybackState.STATE_PAUSED) {
+ newUserEngagedState =
+ oldUserEngagedState == USER_PERMANENTLY_ENGAGED || !isTimeoutExpired
+ ? USER_TEMPORARY_ENGAGED
+ : USER_DISENGAGED;
+ } else {
+ newUserEngagedState = USER_DISENGAGED;
+ }
+ if (oldUserEngagedState == newUserEngagedState) {
+ return;
+ }
+
+ if (newUserEngagedState == USER_TEMPORARY_ENGAGED) {
+ mHandler.postDelayed(mHandleTempEngagedSessionTimeout, TEMP_USER_ENGAGED_TIMEOUT);
+ } else if (oldUserEngagedState == USER_TEMPORARY_ENGAGED) {
+ mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout);
+ }
+
+ boolean wasUserEngaged = oldUserEngagedState != USER_DISENGAGED;
+ boolean isNowUserEngaged = newUserEngagedState != USER_DISENGAGED;
+ mUserEngagementState = newUserEngagedState;
+ if (wasUserEngaged != isNowUserEngaged) {
+ mService.onSessionUserEngagementStateChange(
+ /* mediaSessionRecord= */ this, /* isUserEngaged= */ isNowUserEngaged);
+ }
+ }
+
private final class SessionStub extends ISession.Stub {
@Override
public void destroySession() throws RemoteException {
@@ -1171,8 +1274,10 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
.logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK,
callingUid, callingPid);
}
-
- mIsActive = active;
+ synchronized (mLock) {
+ mIsActive = active;
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
+ }
long token = Binder.clearCallingIdentity();
try {
mService.onSessionActiveStateChanged(MediaSessionRecord.this, mPlaybackState);
@@ -1330,6 +1435,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
&& TRANSITION_PRIORITY_STATES.contains(newState));
synchronized (mLock) {
mPlaybackState = state;
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
}
final long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 09991995099e..b57b14835987 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -196,6 +196,12 @@ public abstract class MediaSessionRecordImpl {
*/
public abstract boolean isClosed();
+ /**
+ * Note: This method is only used for testing purposes If the session is temporary engaged, the
+ * timeout will expire and it will become disengaged.
+ */
+ public abstract void expireTempEngaged();
+
@Override
public final boolean equals(Object o) {
if (this == o) return true;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 53c32cf31d21..2ca76a578f53 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -367,11 +367,13 @@ public class MediaSessionService extends SystemService implements Monitor {
}
boolean isUserEngaged = isUserEngaged(record, playbackState);
- Log.d(TAG, "onSessionActiveStateChanged: "
- + "record=" + record
- + "playbackState=" + playbackState
- + "allowRunningInForeground=" + isUserEngaged);
- setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged);
+ Log.d(
+ TAG,
+ "onSessionActiveStateChanged:"
+ + " record="
+ + record
+ + " playbackState="
+ + playbackState);
reportMediaInteractionEvent(record, isUserEngaged);
mHandler.postSessionsChanged(record);
}
@@ -479,11 +481,13 @@ public class MediaSessionService extends SystemService implements Monitor {
}
user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
boolean isUserEngaged = isUserEngaged(record, playbackState);
- Log.d(TAG, "onSessionPlaybackStateChanged: "
- + "record=" + record
- + "playbackState=" + playbackState
- + "allowRunningInForeground=" + isUserEngaged);
- setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged);
+ Log.d(
+ TAG,
+ "onSessionPlaybackStateChanged:"
+ + " record="
+ + record
+ + " playbackState="
+ + playbackState);
reportMediaInteractionEvent(record, isUserEngaged);
}
}
@@ -650,33 +654,54 @@ public class MediaSessionService extends SystemService implements Monitor {
session.close();
Log.d(TAG, "destroySessionLocked: record=" + session);
- setForegroundServiceAllowance(session, /* allowRunningInForeground= */ false);
+
reportMediaInteractionEvent(session, /* userEngaged= */ false);
mHandler.postSessionsChanged(session);
}
- private void setForegroundServiceAllowance(
- MediaSessionRecordImpl record, boolean allowRunningInForeground) {
- if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
- return;
- }
- ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
- record.getForegroundServiceDelegationOptions();
- if (foregroundServiceDelegationOptions == null) {
- return;
- }
- if (allowRunningInForeground) {
- onUserSessionEngaged(record);
+ void onSessionUserEngagementStateChange(
+ MediaSessionRecordImpl mediaSessionRecord, boolean isUserEngaged) {
+ if (isUserEngaged) {
+ addUserEngagedSession(mediaSessionRecord);
+ startFgsIfSessionIsLinkedToNotification(mediaSessionRecord);
} else {
- onUserDisengaged(record);
+ removeUserEngagedSession(mediaSessionRecord);
+ stopFgsIfNoSessionIsLinkedToNotification(mediaSessionRecord);
}
}
- private void onUserSessionEngaged(MediaSessionRecordImpl mediaSessionRecord) {
+ private void addUserEngagedSession(MediaSessionRecordImpl mediaSessionRecord) {
synchronized (mLock) {
int uid = mediaSessionRecord.getUid();
mUserEngagedSessionsForFgs.putIfAbsent(uid, new HashSet<>());
mUserEngagedSessionsForFgs.get(uid).add(mediaSessionRecord);
+ }
+ }
+
+ private void removeUserEngagedSession(MediaSessionRecordImpl mediaSessionRecord) {
+ synchronized (mLock) {
+ int uid = mediaSessionRecord.getUid();
+ Set<MediaSessionRecordImpl> mUidUserEngagedSessionsForFgs =
+ mUserEngagedSessionsForFgs.get(uid);
+ if (mUidUserEngagedSessionsForFgs == null) {
+ return;
+ }
+
+ mUidUserEngagedSessionsForFgs.remove(mediaSessionRecord);
+ if (mUidUserEngagedSessionsForFgs.isEmpty()) {
+ mUserEngagedSessionsForFgs.remove(uid);
+ }
+ }
+ }
+
+ private void startFgsIfSessionIsLinkedToNotification(
+ MediaSessionRecordImpl mediaSessionRecord) {
+ Log.d(TAG, "startFgsIfSessionIsLinkedToNotification: record=" + mediaSessionRecord);
+ if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+ return;
+ }
+ synchronized (mLock) {
+ int uid = mediaSessionRecord.getUid();
for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) {
if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) {
mActivityManagerInternal.startForegroundServiceDelegate(
@@ -688,30 +713,34 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
- private void onUserDisengaged(MediaSessionRecordImpl mediaSessionRecord) {
+ private void stopFgsIfNoSessionIsLinkedToNotification(
+ MediaSessionRecordImpl mediaSessionRecord) {
+ Log.d(TAG, "stopFgsIfNoSessionIsLinkedToNotification: record=" + mediaSessionRecord);
+ if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+ return;
+ }
synchronized (mLock) {
int uid = mediaSessionRecord.getUid();
- if (mUserEngagedSessionsForFgs.containsKey(uid)) {
- mUserEngagedSessionsForFgs.get(uid).remove(mediaSessionRecord);
- if (mUserEngagedSessionsForFgs.get(uid).isEmpty()) {
- mUserEngagedSessionsForFgs.remove(uid);
- }
+ ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
+ mediaSessionRecord.getForegroundServiceDelegationOptions();
+ if (foregroundServiceDelegationOptions == null) {
+ return;
}
- boolean shouldStopFgs = true;
- for (MediaSessionRecordImpl sessionRecord :
+ for (MediaSessionRecordImpl record :
mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
- for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid,
- Set.of())) {
- if (sessionRecord.isLinkedToNotification(mediaNotification)) {
- shouldStopFgs = false;
+ for (Notification mediaNotification :
+ mMediaNotifications.getOrDefault(uid, Set.of())) {
+ if (record.isLinkedToNotification(mediaNotification)) {
+ // A user engaged session linked with a media notification is found.
+ // We shouldn't call stop FGS in this case.
+ return;
}
}
}
- if (shouldStopFgs) {
- mActivityManagerInternal.stopForegroundServiceDelegate(
- mediaSessionRecord.getForegroundServiceDelegationOptions());
- }
+
+ mActivityManagerInternal.stopForegroundServiceDelegate(
+ foregroundServiceDelegationOptions);
}
}
@@ -2502,7 +2531,6 @@ public class MediaSessionService extends SystemService implements Monitor {
}
MediaSessionRecord session = null;
MediaButtonReceiverHolder mediaButtonReceiverHolder = null;
-
if (mCustomMediaKeyDispatcher != null) {
MediaSession.Token token = mCustomMediaKeyDispatcher.getMediaSession(
keyEvent, uid, asSystemService);
@@ -2630,6 +2658,18 @@ public class MediaSessionService extends SystemService implements Monitor {
&& streamType <= AudioManager.STREAM_NOTIFICATION;
}
+ @Override
+ public void expireTempEngagedSessions() {
+ synchronized (mLock) {
+ for (Set<MediaSessionRecordImpl> uidSessions :
+ mUserEngagedSessionsForFgs.values()) {
+ for (MediaSessionRecordImpl sessionRecord : uidSessions) {
+ sessionRecord.expireTempEngaged();
+ }
+ }
+ }
+ }
+
private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
private final String mPackageName;
private final int mPid;
@@ -3127,7 +3167,6 @@ public class MediaSessionService extends SystemService implements Monitor {
super.onNotificationPosted(sbn);
Notification postedNotification = sbn.getNotification();
int uid = sbn.getUid();
-
if (!postedNotification.isMediaNotification()) {
return;
}
@@ -3138,8 +3177,8 @@ public class MediaSessionService extends SystemService implements Monitor {
mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
mediaSessionRecord.getForegroundServiceDelegationOptions();
- if (mediaSessionRecord.isLinkedToNotification(postedNotification)
- && foregroundServiceDelegationOptions != null) {
+ if (foregroundServiceDelegationOptions != null
+ && mediaSessionRecord.isLinkedToNotification(postedNotification)) {
mActivityManagerInternal.startForegroundServiceDelegate(
foregroundServiceDelegationOptions,
/* connection= */ null);
@@ -3173,21 +3212,7 @@ public class MediaSessionService extends SystemService implements Monitor {
return;
}
- boolean shouldStopFgs = true;
- for (MediaSessionRecordImpl mediaSessionRecord :
- mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
- for (Notification mediaNotification :
- mMediaNotifications.getOrDefault(uid, Set.of())) {
- if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) {
- shouldStopFgs = false;
- }
- }
- }
- if (shouldStopFgs
- && notificationRecord.getForegroundServiceDelegationOptions() != null) {
- mActivityManagerInternal.stopForegroundServiceDelegate(
- notificationRecord.getForegroundServiceDelegationOptions());
- }
+ stopFgsIfNoSessionIsLinkedToNotification(notificationRecord);
}
}
diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java
index a56380827f2c..a20de3198d2c 100644
--- a/services/core/java/com/android/server/media/MediaShellCommand.java
+++ b/services/core/java/com/android/server/media/MediaShellCommand.java
@@ -92,6 +92,8 @@ public class MediaShellCommand extends ShellCommand {
runMonitor();
} else if (cmd.equals("volume")) {
runVolume();
+ } else if (cmd.equals("expire-temp-engaged-sessions")) {
+ expireTempEngagedSessions();
} else {
showError("Error: unknown command '" + cmd + "'");
return -1;
@@ -367,4 +369,8 @@ public class MediaShellCommand extends ShellCommand {
private void runVolume() throws Exception {
VolumeCtrl.run(this);
}
+
+ private void expireTempEngagedSessions() throws Exception {
+ mSessionService.expireTempEngagedSessions();
+ }
}
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 4eb8b2b980cb..c8fd7e47d80a 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -184,6 +184,16 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
throwInvalidBugreportFileForCallerException(
bugreportFile, callingInfo.second);
}
+
+ boolean keepBugreportOnRetrieval = false;
+ if (onboardingBugreportV2Enabled()) {
+ keepBugreportOnRetrieval = mBugreportFilesToPersist.contains(
+ bugreportFile);
+ }
+
+ if (!keepBugreportOnRetrieval) {
+ bugreportFilesForUid.remove(bugreportFile);
+ }
} else {
ArraySet<String> bugreportFilesForCaller = mBugreportFiles.get(callingInfo);
if (bugreportFilesForCaller != null
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 3eeeae7dc260..80a5f3a4c579 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1085,11 +1085,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean isUpdateOwnershipEnforcementEnabled =
mPm.isUpdateOwnershipEnforcementAvailable()
&& existingUpdateOwnerPackageName != null;
+ // For an installation that un-archives an app, if the installer doesn't have the
+ // INSTALL_PACKAGES permission, the user should have already been prompted to confirm the
+ // un-archive request. There's no need for another confirmation during the installation.
+ final boolean isInstallUnarchive =
+ (params.installFlags & PackageManager.INSTALL_UNARCHIVE) != 0;
// Device owners and affiliated profile owners are allowed to silently install packages, so
// the permission check is waived if the installer is the device owner.
final boolean noUserActionNecessary = isInstallerRoot || isInstallerSystem
- || isInstallerDeviceOwnerOrAffiliatedProfileOwner() || isEmergencyInstall;
+ || isInstallerDeviceOwnerOrAffiliatedProfileOwner() || isEmergencyInstall
+ || isInstallUnarchive;
if (noUserActionNecessary) {
return userActionNotTypicallyNeededResponse;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7db83d7dcc6f..b4919a4fb9ff 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -174,7 +174,6 @@ import android.service.dreams.IDreamManager;
import android.service.vr.IPersistentVrStateCallbacks;
import android.speech.RecognizerIntent;
import android.telecom.TelecomManager;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.MathUtils;
import android.util.MutableBoolean;
@@ -4121,14 +4120,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private void handleSwitchKeyboardLayout(@NonNull KeyEvent event, int direction,
IBinder focusedToken) {
- if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) {
- IBinder targetWindowToken =
- mWindowManagerInternal.getTargetWindowTokenFromInputToken(focusedToken);
- InputMethodManagerInternal.get().onSwitchKeyboardLayoutShortcut(direction,
- event.getDisplayId(), targetWindowToken);
- } else {
- mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
- }
+ IBinder targetWindowToken =
+ mWindowManagerInternal.getTargetWindowTokenFromInputToken(focusedToken);
+ InputMethodManagerInternal.get().onSwitchKeyboardLayoutShortcut(direction,
+ event.getDisplayId(), targetWindowToken);
}
private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 2623025cacf1..9ca4e273ac39 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -251,12 +251,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
*/
public int getCameraLensCoverState();
- /**
- * Switch the keyboard layout for the given device.
- * Direction should be +1 or -1 to go to the next or previous keyboard layout.
- */
- public void switchKeyboardLayout(int deviceId, int direction);
-
public void shutdown(boolean confirm);
public void reboot(boolean confirm);
public void rebootSafeMode(boolean confirm);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 8c4c0de71781..802f196adf3b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -42,6 +42,7 @@ import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_LOCK_ORIG;
import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir;
import static com.android.server.wallpaper.WallpaperUtils.makeWallpaperIdLocked;
import static com.android.window.flags.Flags.multiCrop;
+import static com.android.window.flags.Flags.offloadColorExtraction;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -380,7 +381,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
// Outside of the lock since it will synchronize itself
- notifyWallpaperColorsChanged(wallpaper);
+ if (!offloadColorExtraction()) notifyWallpaperColorsChanged(wallpaper);
}
@Override
@@ -403,12 +404,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper) {
+ notifyWallpaperColorsChanged(wallpaper, wallpaper.mWhich);
+ }
+
+ private void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
if (DEBUG) {
Slog.i(TAG, "Notifying wallpaper colors changed");
}
if (wallpaper.connection != null) {
wallpaper.connection.forEachDisplayConnector(connector ->
- notifyWallpaperColorsChangedOnDisplay(wallpaper, connector.mDisplayId));
+ notifyWallpaperColorsChangedOnDisplay(wallpaper, connector.mDisplayId, which));
}
}
@@ -425,6 +430,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private void notifyWallpaperColorsChangedOnDisplay(@NonNull WallpaperData wallpaper,
int displayId) {
+ notifyWallpaperColorsChangedOnDisplay(wallpaper, displayId, wallpaper.mWhich);
+ }
+
+ private void notifyWallpaperColorsChangedOnDisplay(@NonNull WallpaperData wallpaper,
+ int displayId, int which) {
boolean needsExtraction;
synchronized (mLock) {
final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
@@ -449,8 +459,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
notify = extractColors(wallpaper);
}
if (notify) {
- notifyColorListeners(getAdjustedWallpaperColorsOnDimming(wallpaper),
- wallpaper.mWhich, wallpaper.userId, displayId);
+ notifyColorListeners(getAdjustedWallpaperColorsOnDimming(wallpaper), which,
+ wallpaper.userId, displayId);
}
}
@@ -504,6 +514,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
* @return true unless the wallpaper changed during the color computation
*/
private boolean extractColors(WallpaperData wallpaper) {
+ if (offloadColorExtraction()) return !mImageWallpaper.equals(wallpaper.wallpaperComponent);
String cropFile = null;
boolean defaultImageWallpaper = false;
int wallpaperId;
@@ -1148,10 +1159,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
synchronized (mLock) {
// Do not broadcast changes on ImageWallpaper since it's handled
// internally by this class.
- if (mImageWallpaper.equals(mWallpaper.wallpaperComponent)) {
+ boolean isImageWallpaper = mImageWallpaper.equals(mWallpaper.wallpaperComponent);
+ if (isImageWallpaper && (!offloadColorExtraction() || primaryColors == null)) {
return;
}
mWallpaper.primaryColors = primaryColors;
+ // only save the colors for ImageWallpaper - for live wallpapers, the colors
+ // are always recomputed after a reboot.
+ if (offloadColorExtraction() && isImageWallpaper) {
+ saveSettingsLocked(mWallpaper.userId);
+ }
}
notifyWallpaperColorsChangedOnDisplay(mWallpaper, displayId);
}
@@ -1177,7 +1194,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
try {
// This will trigger onComputeColors in the wallpaper engine.
// It's fine to be locked in here since the binder is oneway.
- connector.mEngine.requestWallpaperColors();
+ if (!offloadColorExtraction() || mWallpaper.primaryColors == null) {
+ connector.mEngine.requestWallpaperColors();
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Failed to request wallpaper colors", e);
}
@@ -1811,6 +1830,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// Offload color extraction to another thread since switchUser will be called
// from the main thread.
FgThread.getHandler().post(() -> {
+ if (offloadColorExtraction()) return;
notifyWallpaperColorsChanged(systemWallpaper);
if (lockWallpaper != systemWallpaper) notifyWallpaperColorsChanged(lockWallpaper);
notifyWallpaperColorsChanged(mFallbackWallpaper);
@@ -2722,8 +2742,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
});
// Need to extract colors again to re-calculate dark hints after
// applying dimming.
- wp.mIsColorExtractedFromDim = true;
- pendingColorExtraction.add(wp);
+ if (!offloadColorExtraction()) {
+ wp.mIsColorExtractedFromDim = true;
+ pendingColorExtraction.add(wp);
+ }
changed = true;
}
}
@@ -2732,7 +2754,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
for (WallpaperData wp: pendingColorExtraction) {
- notifyWallpaperColorsChanged(wp);
+ if (!offloadColorExtraction()) notifyWallpaperColorsChanged(wp);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2927,6 +2949,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
wallpaper.allowBackup = allowBackup;
wallpaper.mWallpaperDimAmount = getWallpaperDimAmount();
+ if (offloadColorExtraction()) wallpaper.primaryColors = null;
}
return pfd;
} finally {
@@ -3069,6 +3092,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
boolean shouldNotifyColors = false;
+
+ // If the lockscreen wallpaper is set to the same as the home screen, notify that the
+ // lockscreen wallpaper colors changed, even if we don't bind a new wallpaper engine.
+ boolean shouldNotifyLockscreenColors = false;
boolean bindSuccess;
final WallpaperData newWallpaper;
@@ -3114,7 +3141,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
bindSuccess = bindWallpaperComponentLocked(name, /* force */
forceRebind, /* fromUser */ true, newWallpaper, reply);
if (bindSuccess) {
- if (!same) {
+ if (!same || (offloadColorExtraction() && forceRebind)) {
newWallpaper.primaryColors = null;
} else {
if (newWallpaper.connection != null) {
@@ -3138,6 +3165,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
newWallpaper.wallpaperId = makeWallpaperIdLocked();
notifyCallbacksLocked(newWallpaper);
shouldNotifyColors = true;
+ if (offloadColorExtraction()) {
+ shouldNotifyColors = false;
+ shouldNotifyLockscreenColors = !force && same && !systemIsBoth
+ && which == (FLAG_SYSTEM | FLAG_LOCK);
+ }
if (which == (FLAG_SYSTEM | FLAG_LOCK)) {
if (DEBUG) {
@@ -3166,6 +3198,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (shouldNotifyColors) {
notifyWallpaperColorsChanged(newWallpaper);
}
+ if (shouldNotifyLockscreenColors) {
+ notifyWallpaperColorsChanged(newWallpaper, FLAG_LOCK);
+ }
+
return bindSuccess;
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index f6681c571090..f7b4a6748411 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -779,6 +780,10 @@ class BackNavigationController {
&& wc.asTaskFragment() == null) {
continue;
}
+ // Only care if visibility changed.
+ if (targets.get(i).getTransitMode(wc) == TRANSIT_CHANGE) {
+ continue;
+ }
// WC can be visible due to setLaunchBehind
if (wc.isVisibleRequested()) {
mTmpOpenApps.add(wc);
@@ -843,14 +848,14 @@ class BackNavigationController {
* @param targets The final animation targets derived in transition.
* @param finishedTransition The finished transition target.
*/
- boolean onTransitionFinish(ArrayList<Transition.ChangeInfo> targets,
+ void onTransitionFinish(ArrayList<Transition.ChangeInfo> targets,
@NonNull Transition finishedTransition) {
if (finishedTransition == mWaitTransitionFinish) {
clearBackAnimations();
}
if (!mBackAnimationInProgress || mPendingAnimationBuilder == null) {
- return false;
+ return;
}
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Handling the deferred animation after transition finished");
@@ -878,7 +883,7 @@ class BackNavigationController {
+ " open: " + Arrays.toString(mPendingAnimationBuilder.mOpenTargets)
+ " close: " + mPendingAnimationBuilder.mCloseTarget);
cancelPendingAnimation();
- return false;
+ return;
}
// Ensure the final animation targets which hidden by transition could be visible.
@@ -887,9 +892,14 @@ class BackNavigationController {
wc.prepareSurfaces();
}
- scheduleAnimation(mPendingAnimationBuilder);
- mPendingAnimationBuilder = null;
- return true;
+ // The pending builder could be cleared due to prepareSurfaces
+ // => updateNonSystemOverlayWindowsVisibilityIfNeeded
+ // => setForceHideNonSystemOverlayWindowIfNeeded
+ // => updateFocusedWindowLocked => onFocusWindowChanged.
+ if (mPendingAnimationBuilder != null) {
+ scheduleAnimation(mPendingAnimationBuilder);
+ mPendingAnimationBuilder = null;
+ }
}
private void cancelPendingAnimation() {
@@ -1552,15 +1562,17 @@ class BackNavigationController {
return this;
}
+ // WC must be Activity/TaskFragment/Task
boolean containTarget(@NonNull WindowContainer wc) {
if (mOpenTargets != null) {
for (int i = mOpenTargets.length - 1; i >= 0; --i) {
- if (wc == mOpenTargets[i] || mOpenTargets[i].hasChild(wc)) {
+ if (wc == mOpenTargets[i] || mOpenTargets[i].hasChild(wc)
+ || wc.hasChild(mOpenTargets[i])) {
return true;
}
}
}
- return wc == mCloseTarget || mCloseTarget.hasChild(wc);
+ return wc == mCloseTarget || mCloseTarget.hasChild(wc) || wc.hasChild(mCloseTarget);
}
/**
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index b8bb258aa2ce..0ad601de95ec 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -61,6 +61,7 @@ import android.view.WindowManager;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
@@ -225,13 +226,16 @@ class KeyguardController {
if (keyguardShowing) {
state.mDismissalRequested = false;
}
- if (goingAwayRemoved) {
- // Keyguard dismiss is canceled. Send a transition to undo the changes and clean up
- // before holding the sleep token again.
+ if (goingAwayRemoved || (keyguardShowing && Flags.keyguardAppearTransition())) {
+ // Keyguard decided to show or stopped going away. Send a transition to animate back
+ // to the locked state before holding the sleep token again
final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
dc.requestTransitionAndLegacyPrepare(
TRANSIT_TO_FRONT, TRANSIT_FLAG_KEYGUARD_APPEARING);
- mWindowManager.executeAppTransition();
+ if (Flags.keyguardAppearTransition()) {
+ dc.mWallpaperController.adjustWallpaperWindows();
+ }
+ dc.executeAppTransition();
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index c8cb934783ef..59bda54eb089 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -857,10 +857,6 @@ class WallpaperController {
}
public void updateWallpaperTokens(boolean keyguardLocked) {
- if (DEBUG_WALLPAPER) {
- Slog.v(TAG, "Wallpaper vis: target " + mWallpaperTarget + " prev="
- + mPrevWallpaperTarget);
- }
updateWallpaperTokens(mWallpaperTarget != null || mPrevWallpaperTarget != null,
keyguardLocked);
}
@@ -869,6 +865,8 @@ class WallpaperController {
* Change the visibility of the top wallpaper to {@param visibility} and hide all the others.
*/
private void updateWallpaperTokens(boolean visibility, boolean keyguardLocked) {
+ ProtoLog.v(WM_DEBUG_WALLPAPER, "updateWallpaperTokens requestedVisibility=%b on"
+ + " keyguardLocked=%b", visibility, keyguardLocked);
WindowState topWallpaper = mFindResults.getTopWallpaper(keyguardLocked);
WallpaperWindowToken topWallpaperToken =
topWallpaper == null ? null : topWallpaper.mToken.asWallpaperToken();
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 55eeaf22cca8..5c24eee63317 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -274,6 +274,12 @@ class WallpaperWindowToken extends WindowToken {
}
@Override
+ boolean isSyncFinished(BLASTSyncEngine.SyncGroup group) {
+ // TODO(b/233286785): Support sync state for wallpaper. See WindowState#prepareSync.
+ return !mVisibleRequested || !hasVisibleNotDrawnWallpaper();
+ }
+
+ @Override
public String toString() {
if (stringName == null) {
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index aeefc34f2cad..8a68afbd501f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3685,12 +3685,6 @@ public class WindowManagerService extends IWindowManager.Stub
// Called by window manager policy. Not exposed externally.
@Override
- public void switchKeyboardLayout(int deviceId, int direction) {
- mInputManager.switchKeyboardLayout(deviceId, direction);
- }
-
- // Called by window manager policy. Not exposed externally.
- @Override
public void shutdown(boolean confirm) {
// Pass in the UI context, since ShutdownThread requires it (to show UI).
ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(),
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 6ac2774941e1..aac567c2e455 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -202,6 +202,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// Whether this process has ever started a service with the BIND_INPUT_METHOD permission.
private volatile boolean mHasImeService;
+ /**
+ * Whether this process can use realtime prioirity (SCHED_FIFO) for its UI and render threads
+ * when this process is SCHED_GROUP_TOP_APP.
+ */
+ private final boolean mUseFifoUiScheduling;
+
/** Whether {@link #mActivities} is not empty. */
private volatile boolean mHasActivities;
/** All activities running in the process (exclude destroying). */
@@ -340,6 +346,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// TODO(b/151161907): Remove after support for display-independent (raw) SysUi configs.
mIsActivityConfigOverrideAllowed = false;
}
+ mUseFifoUiScheduling = com.android.window.flags.Flags.fifoPriorityForMajorUiProcesses()
+ && (isSysUiPackage || mAtm.isCallerRecents(uid));
mCanUseSystemGrammaticalGender = mAtm.mGrammaticalManagerInternal != null
&& mAtm.mGrammaticalManagerInternal.canGetSystemGrammaticalGender(mUid,
@@ -1901,6 +1909,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
}
+ /** Returns {@code true} if the process prefers to use fifo scheduling. */
+ public boolean useFifoUiScheduling() {
+ return mUseFifoUiScheduling;
+ }
+
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
public void onTopProcChanged() {
if (mAtm.mVrController.isInterestingToSchedGroup()) {
@@ -2078,6 +2091,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
pw.println();
}
+ if (mUseFifoUiScheduling) {
+ pw.println(prefix + " mUseFifoUiScheduling=true");
+ }
final int stateFlags = mActivityStateFlags;
if (stateFlags != ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index a7430e563904..419bcb8650a7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -38,6 +38,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.am.ActivityManagerInternalTest.CustomThread;
import static com.android.server.am.ActivityManagerService.Injector;
@@ -692,6 +693,31 @@ public class ActivityManagerServiceTest {
assertEquals(uid, -1);
}
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testFifoSwitch() {
+ addUidRecord(TEST_UID, TEST_PACKAGE);
+ final ProcessRecord fifoProc = mAms.getProcessRecordLocked(TEST_PACKAGE, TEST_UID);
+ final var wpc = fifoProc.getWindowProcessController();
+ spyOn(wpc);
+ doReturn(true).when(wpc).useFifoUiScheduling();
+ fifoProc.makeActive(fifoProc.getThread(), mAms.mProcessStats);
+ assertTrue(fifoProc.useFifoUiScheduling());
+ assertTrue(mAms.mSpecifiedFifoProcesses.contains(fifoProc));
+
+ // If there is a request to use more CPU resource (e.g. camera), the current fifo process
+ // should switch the capability of using fifo.
+ final UidRecord uidRecord = addUidRecord(TEST_UID + 1, TEST_PACKAGE + 1);
+ uidRecord.setCurProcState(PROCESS_STATE_TOP);
+ mAms.adjustFifoProcessesIfNeeded(uidRecord.getUid(), false /* allowSpecifiedFifo */);
+ assertFalse(fifoProc.useFifoUiScheduling());
+ mAms.adjustFifoProcessesIfNeeded(uidRecord.getUid(), true /* allowSpecifiedFifo */);
+ assertTrue(fifoProc.useFifoUiScheduling());
+
+ fifoProc.makeInactive(mAms.mProcessStats);
+ assertFalse(mAms.mSpecifiedFifoProcesses.contains(fifoProc));
+ }
+
@Test
public void testGlobalIsolatedUidAllocator() {
final IsolatedUidRange globalUidRange = mAms.mProcessList.mGlobalIsolatedUids;
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 52df010bd588..eac99298559e 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -85,7 +85,6 @@ import android.os.VibratorInfo;
import android.os.test.TestLooper;
import android.service.dreams.DreamManagerInternal;
import android.telecom.TelecomManager;
-import android.util.FeatureFlagUtils;
import android.view.Display;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -743,15 +742,8 @@ class TestPhoneWindowManager {
void assertSwitchKeyboardLayout(int direction, int displayId) {
mTestLooper.dispatchAll();
- if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) {
- verify(mInputMethodManagerInternal).onSwitchKeyboardLayoutShortcut(eq(direction),
- eq(displayId), eq(mImeTargetWindowToken));
- verify(mWindowManagerFuncsImpl, never()).switchKeyboardLayout(anyInt(), anyInt());
- } else {
- verify(mWindowManagerFuncsImpl).switchKeyboardLayout(anyInt(), eq(direction));
- verify(mInputMethodManagerInternal, never())
- .onSwitchKeyboardLayoutShortcut(anyInt(), anyInt(), any());
- }
+ verify(mInputMethodManagerInternal).onSwitchKeyboardLayoutShortcut(eq(direction),
+ eq(displayId), eq(mImeTargetWindowToken));
}
void assertTakeBugreport() {
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
index 511c94849681..13902184ac6e 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
@@ -17,8 +17,12 @@
package com.android.server.wm.flicker.activityembedding.rotation
import android.platform.test.annotations.Presubmit
+import android.tools.Position
+import android.tools.datatypes.Rect
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.Condition
+import android.tools.traces.DeviceStateDump
import android.tools.traces.component.ComponentNameMatcher
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.setRotation
@@ -30,7 +34,14 @@ abstract class RotationTransition(flicker: LegacyFlickerTest) : ActivityEmbeddin
override val transition: FlickerBuilder.() -> Unit = {
setup { this.setRotation(flicker.scenario.startRotation) }
teardown { testApp.exit(wmHelper) }
- transitions { this.setRotation(flicker.scenario.endRotation) }
+ transitions {
+ this.setRotation(flicker.scenario.endRotation)
+ if (!flicker.scenario.isTablet) {
+ wmHelper.StateSyncBuilder()
+ .add(navBarInPosition(flicker.scenario.isGesturalNavigation))
+ .waitForAndVerify()
+ }
+ }
}
/** {@inheritDoc} */
@@ -76,4 +87,37 @@ abstract class RotationTransition(flicker: LegacyFlickerTest) : ActivityEmbeddin
appLayerRotates_StartingPos()
appLayerRotates_EndingPos()
}
+
+ private fun navBarInPosition(isGesturalNavigation: Boolean): Condition<DeviceStateDump> {
+ return Condition("navBarPosition") { dump ->
+ val display =
+ dump.layerState.displays.filterNot { it.isOff }.minByOrNull { it.id }
+ ?: error("There is no display!")
+ val displayArea = display.layerStackSpace
+ val navBarPosition = display.navBarPosition(isGesturalNavigation)
+ val navBarRegion = dump.layerState
+ .getLayerWithBuffer(ComponentNameMatcher.NAV_BAR)
+ ?.visibleRegion?.bounds ?: Rect.EMPTY
+
+ when (navBarPosition) {
+ Position.TOP ->
+ navBarRegion.top == displayArea.top &&
+ navBarRegion.left == displayArea.left &&
+ navBarRegion.right == displayArea.right
+ Position.BOTTOM ->
+ navBarRegion.bottom == displayArea.bottom &&
+ navBarRegion.left == displayArea.left &&
+ navBarRegion.right == displayArea.right
+ Position.LEFT ->
+ navBarRegion.left == displayArea.left &&
+ navBarRegion.top == displayArea.top &&
+ navBarRegion.bottom == displayArea.bottom
+ Position.RIGHT ->
+ navBarRegion.right == displayArea.right &&
+ navBarRegion.top == displayArea.top &&
+ navBarRegion.bottom == displayArea.bottom
+ else -> error("Unknown position $navBarPosition")
+ }
+ }
+ }
}
diff --git a/tests/FlickerTests/README.md b/tests/FlickerTests/README.md
index 6b28fdf8a8ef..7429250f5cc0 100644
--- a/tests/FlickerTests/README.md
+++ b/tests/FlickerTests/README.md
@@ -7,82 +7,17 @@ The tests are organized in packages according to the transitions they test (e.g.
## Adding a Test
-By default tests should inherit from `RotationTestBase` or `NonRotationTestBase` and must override the variable `transitionToRun` (Kotlin) or the function `getTransitionToRun()` (Java).
-Only tests that are not supported by these classes should inherit directly from the `FlickerTestBase` class.
+By default, tests should inherit from `TestBase` and override the variable `transition` (Kotlin) or the function `getTransition()` (Java).
-### Rotation animations and transitions
+Inheriting from this class ensures the common assertions will be executed, namely:
-Tests that rotate the device should inherit from `RotationTestBase`.
-Tests that inherit from the class automatically receive start and end rotation values.
-Moreover, these tests inherit the following checks:
* all regions on the screen are covered
* status bar is always visible
-* status bar rotates
+* status bar is at the correct position at the start and end of the transition
* nav bar is always visible
-* nav bar is rotates
+* nav bar is at the correct position at the start and end of the transition
The default tests can be disabled by overriding the respective methods and including an `@Ignore` annotation.
-### Non-Rotation animations and transitions
+For more examples of how a test looks like check `ChangeAppRotationTest` within the `Rotation` subdirectory.
-`NonRotationTestBase` was created to make it easier to write tests that do not involve rotation (e.g., `Pip`, `split screen` or `IME`).
-Tests that inherit from the class are automatically executed twice: once in portrait and once in landscape mode and the assertions are checked independently.
-Moreover, these tests inherit the following checks:
-* all regions on the screen are covered
-* status bar is always visible
-* nav bar is always visible
-
-The default tests can be disabled by overriding the respective methods and including an `@Ignore` annotation.
-
-### Exceptional cases
-
-Tests that rotate the device should inherit from `RotationTestBase`.
-This class allows the test to be freely configured and does not provide any assertions.
-
-
-### Example
-
-Start by defining common or error prone transitions using `TransitionRunner`.
-```kotlin
-@LargeTest
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class MyTest(
- beginRotationName: String,
- beginRotation: Int
-) : NonRotationTestBase(beginRotationName, beginRotation) {
- init {
- mTestApp = MyAppHelper(InstrumentationRegistry.getInstrumentation())
- }
-
- override val transitionToRun: TransitionRunner
- get() = TransitionRunner.newBuilder()
- .withTag("myTest")
- .recordAllRuns()
- .runBefore { device.pressHome() }
- .runBefore { device.waitForIdle() }
- .run { testApp.open() }
- .runAfter{ testApp.exit() }
- .repeat(2)
- .includeJankyRuns()
- .build()
-
- @Test
- fun myWMTest() {
- checkResults {
- WmTraceSubject.assertThat(it)
- .showsAppWindow(MyTestApp)
- .forAllEntries()
- }
- }
-
- @Test
- fun mySFTest() {
- checkResults {
- LayersTraceSubject.assertThat(it)
- .showsLayer(MyTestApp)
- .forAllEntries()
- }
- }
-}
-```
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index e60764f137af..80282c309320 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -33,7 +33,6 @@ import android.icu.util.ULocale
import android.os.Bundle
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.util.proto.ProtoOutputStream
import android.view.InputDevice
import android.view.inputmethod.InputMethodInfo
@@ -47,9 +46,7 @@ import com.android.test.input.R
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
-import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
-import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -234,631 +231,326 @@ class KeyboardLayoutManagerTests {
}
@Test
- fun testDefaultUi_getKeyboardLayouts() {
- NewSettingsApiFlag(false).use {
- val keyboardLayouts = keyboardLayoutManager.keyboardLayouts
- assertNotEquals(
- "Default UI: Keyboard layout API should not return empty array",
- 0,
- keyboardLayouts.size
- )
- assertTrue(
- "Default UI: Keyboard layout API should provide English(US) layout",
- hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
- )
- }
+ fun testGetKeyboardLayouts() {
+ val keyboardLayouts = keyboardLayoutManager.keyboardLayouts
+ assertNotEquals(
+ "Keyboard layout API should not return empty array",
+ 0,
+ keyboardLayouts.size
+ )
+ assertTrue(
+ "Keyboard layout API should provide English(US) layout",
+ hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ )
}
@Test
- fun testNewUi_getKeyboardLayouts() {
- NewSettingsApiFlag(true).use {
- val keyboardLayouts = keyboardLayoutManager.keyboardLayouts
- assertNotEquals(
- "New UI: Keyboard layout API should not return empty array",
- 0,
- keyboardLayouts.size
- )
- assertTrue(
- "New UI: Keyboard layout API should provide English(US) layout",
- hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
- )
- }
+ fun testGetKeyboardLayout() {
+ val keyboardLayout =
+ keyboardLayoutManager.getKeyboardLayout(ENGLISH_US_LAYOUT_DESCRIPTOR)
+ assertEquals("getKeyboardLayout API should return correct Layout from " +
+ "available layouts",
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
+ keyboardLayout!!.descriptor
+ )
}
@Test
- fun testDefaultUi_getKeyboardLayoutsForInputDevice() {
- NewSettingsApiFlag(false).use {
- val keyboardLayouts =
- keyboardLayoutManager.getKeyboardLayoutsForInputDevice(keyboardDevice.identifier)
- assertNotEquals(
- "Default UI: getKeyboardLayoutsForInputDevice API should not return empty array",
- 0,
- keyboardLayouts.size
- )
- assertTrue(
- "Default UI: getKeyboardLayoutsForInputDevice API should provide English(US) " +
- "layout",
- hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
- )
+ fun testGetSetKeyboardLayoutForInputDevice_withImeInfo() {
+ val imeSubtype = createImeSubtype()
- val vendorSpecificKeyboardLayouts =
- keyboardLayoutManager.getKeyboardLayoutsForInputDevice(
- vendorSpecificKeyboardDevice.identifier
- )
- assertEquals(
- "Default UI: getKeyboardLayoutsForInputDevice API should return only vendor " +
- "specific layout",
- 1,
- vendorSpecificKeyboardLayouts.size
- )
- assertEquals(
- "Default UI: getKeyboardLayoutsForInputDevice API should return vendor specific " +
- "layout",
- VENDOR_SPECIFIC_LAYOUT_DESCRIPTOR,
- vendorSpecificKeyboardLayouts[0].descriptor
+ keyboardLayoutManager.setKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
+ ENGLISH_UK_LAYOUT_DESCRIPTOR
+ )
+ var result =
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
)
- }
- }
+ assertEquals(
+ "getKeyboardLayoutForInputDevice API should return the set layout",
+ ENGLISH_UK_LAYOUT_DESCRIPTOR,
+ result.layoutDescriptor
+ )
- @Test
- fun testNewUi_getKeyboardLayoutsForInputDevice() {
- NewSettingsApiFlag(true).use {
- val keyboardLayouts = keyboardLayoutManager.keyboardLayouts
- assertNotEquals(
- "New UI: getKeyboardLayoutsForInputDevice API should not return empty array",
- 0,
- keyboardLayouts.size
- )
- assertTrue(
- "New UI: getKeyboardLayoutsForInputDevice API should provide English(US) " +
- "layout",
- hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ // This should replace previously set layout
+ keyboardLayoutManager.setKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ result =
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
)
- }
+ assertEquals(
+ "getKeyboardLayoutForInputDevice API should return the last set layout",
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
+ result.layoutDescriptor
+ )
}
@Test
- fun testDefaultUi_getSetCurrentKeyboardLayoutForInputDevice() {
- NewSettingsApiFlag(false).use {
- assertNull(
- "Default UI: getCurrentKeyboardLayoutForInputDevice API should return null if " +
- "nothing was set",
- keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
- keyboardDevice.identifier
- )
- )
-
- keyboardLayoutManager.setCurrentKeyboardLayoutForInputDevice(
- keyboardDevice.identifier,
- ENGLISH_US_LAYOUT_DESCRIPTOR
- )
- val keyboardLayout =
- keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
- keyboardDevice.identifier
- )
- assertEquals(
- "Default UI: getCurrentKeyboardLayoutForInputDevice API should return the set " +
- "layout",
- ENGLISH_US_LAYOUT_DESCRIPTOR,
- keyboardLayout
+ fun testGetKeyboardLayoutListForInputDevice() {
+ // Check Layouts for "hi-Latn". It should return all 'Latn' keyboard layouts
+ var keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtypeForLanguageTag("hi-Latn")
+ )
+ assertNotEquals(
+ "getKeyboardLayoutListForInputDevice API should return the list of " +
+ "supported layouts with matching script code",
+ 0,
+ keyboardLayouts.size
+ )
+ assertTrue("getKeyboardLayoutListForInputDevice API should return a list " +
+ "containing English(US) layout for hi-Latn",
+ containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ )
+ assertTrue("getKeyboardLayoutListForInputDevice API should return a list " +
+ "containing English(No script code) layout for hi-Latn",
+ containsLayout(
+ keyboardLayouts,
+ createLayoutDescriptor("keyboard_layout_english_without_script_code")
)
- }
- }
+ )
- @Test
- fun testNewUi_getSetCurrentKeyboardLayoutForInputDevice() {
- NewSettingsApiFlag(true).use {
- keyboardLayoutManager.setCurrentKeyboardLayoutForInputDevice(
- keyboardDevice.identifier,
- ENGLISH_US_LAYOUT_DESCRIPTOR
+ // Check Layouts for "hi" which by default uses 'Deva' script.
+ keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtypeForLanguageTag("hi")
)
- assertNull(
- "New UI: getCurrentKeyboardLayoutForInputDevice API should always return null " +
- "even after setCurrentKeyboardLayoutForInputDevice",
- keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
- keyboardDevice.identifier
- )
- )
- }
- }
+ assertEquals("getKeyboardLayoutListForInputDevice API should return empty " +
+ "list if no supported layouts available",
+ 0,
+ keyboardLayouts.size
+ )
- @Test
- fun testDefaultUi_getEnabledKeyboardLayoutsForInputDevice() {
- NewSettingsApiFlag(false).use {
- keyboardLayoutManager.addKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR
+ // If user manually selected some layout, always provide it in the layout list
+ val imeSubtype = createImeSubtypeForLanguageTag("hi")
+ keyboardLayoutManager.setKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ imeSubtype
)
-
- val keyboardLayouts =
- keyboardLayoutManager.getEnabledKeyboardLayoutsForInputDevice(
- keyboardDevice.identifier
- )
- assertEquals(
- "Default UI: getEnabledKeyboardLayoutsForInputDevice API should return added " +
- "layout",
+ assertEquals("getKeyboardLayoutListForInputDevice API should return user " +
+ "selected layout even if the script is incompatible with IME",
1,
- keyboardLayouts.size
- )
- assertEquals(
- "Default UI: getEnabledKeyboardLayoutsForInputDevice API should return " +
- "English(US) layout",
- ENGLISH_US_LAYOUT_DESCRIPTOR,
- keyboardLayouts[0]
- )
- assertEquals(
- "Default UI: getCurrentKeyboardLayoutForInputDevice API should return " +
- "English(US) layout (Auto select the first enabled layout)",
- ENGLISH_US_LAYOUT_DESCRIPTOR,
- keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
- keyboardDevice.identifier
- )
- )
-
- keyboardLayoutManager.removeKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR
- )
- assertEquals(
- "Default UI: getKeyboardLayoutsForInputDevice API should return 0 layouts",
- 0,
- keyboardLayoutManager.getEnabledKeyboardLayoutsForInputDevice(
- keyboardDevice.identifier
- ).size
- )
- assertNull(
- "Default UI: getCurrentKeyboardLayoutForInputDevice API should return null after " +
- "the enabled layout is removed",
- keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
- keyboardDevice.identifier
- )
- )
- }
- }
-
- @Test
- fun testNewUi_getEnabledKeyboardLayoutsForInputDevice() {
- NewSettingsApiFlag(true).use {
- keyboardLayoutManager.addKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR
- )
-
- assertEquals(
- "New UI: getEnabledKeyboardLayoutsForInputDevice API should return always return " +
- "an empty array",
- 0,
- keyboardLayoutManager.getEnabledKeyboardLayoutsForInputDevice(
- keyboardDevice.identifier
- ).size
- )
- assertNull(
- "New UI: getCurrentKeyboardLayoutForInputDevice API should always return null",
- keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
- keyboardDevice.identifier
- )
- )
- }
- }
-
- @Test
- fun testDefaultUi_switchKeyboardLayout() {
- NewSettingsApiFlag(false).use {
- keyboardLayoutManager.addKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR
- )
- keyboardLayoutManager.addKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, ENGLISH_UK_LAYOUT_DESCRIPTOR
- )
- assertEquals(
- "Default UI: getCurrentKeyboardLayoutForInputDevice API should return " +
- "English(US) layout",
- ENGLISH_US_LAYOUT_DESCRIPTOR,
- keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
- keyboardDevice.identifier
- )
- )
-
- keyboardLayoutManager.switchKeyboardLayout(DEVICE_ID, 1)
-
- // Throws null pointer because trying to show toast using TestLooper
- assertThrows(NullPointerException::class.java) { testLooper.dispatchAll() }
- assertEquals("Default UI: getCurrentKeyboardLayoutForInputDevice API should return " +
- "English(UK) layout",
- ENGLISH_UK_LAYOUT_DESCRIPTOR,
- keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
- keyboardDevice.identifier
- )
- )
- }
- }
-
- @Test
- fun testNewUi_switchKeyboardLayout() {
- NewSettingsApiFlag(true).use {
- keyboardLayoutManager.addKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR
- )
- keyboardLayoutManager.addKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, ENGLISH_UK_LAYOUT_DESCRIPTOR
- )
-
- keyboardLayoutManager.switchKeyboardLayout(DEVICE_ID, 1)
- testLooper.dispatchAll()
-
- assertNull("New UI: getCurrentKeyboardLayoutForInputDevice API should always return " +
- "null",
- keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
- keyboardDevice.identifier
- )
- )
- }
- }
-
- @Test
- fun testDefaultUi_getKeyboardLayout() {
- NewSettingsApiFlag(false).use {
- val keyboardLayout =
- keyboardLayoutManager.getKeyboardLayout(ENGLISH_US_LAYOUT_DESCRIPTOR)
- assertEquals("Default UI: getKeyboardLayout API should return correct Layout from " +
- "available layouts",
- ENGLISH_US_LAYOUT_DESCRIPTOR,
- keyboardLayout!!.descriptor
- )
- }
- }
-
- @Test
- fun testNewUi_getKeyboardLayout() {
- NewSettingsApiFlag(true).use {
- val keyboardLayout =
- keyboardLayoutManager.getKeyboardLayout(ENGLISH_US_LAYOUT_DESCRIPTOR)
- assertEquals("New UI: getKeyboardLayout API should return correct Layout from " +
- "available layouts",
- ENGLISH_US_LAYOUT_DESCRIPTOR,
- keyboardLayout!!.descriptor
- )
- }
- }
-
- @Test
- fun testDefaultUi_getSetKeyboardLayoutForInputDevice_WithImeInfo() {
- NewSettingsApiFlag(false).use {
- val imeSubtype = createImeSubtype()
- keyboardLayoutManager.setKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
- ENGLISH_UK_LAYOUT_DESCRIPTOR
- )
- assertEquals(
- "Default UI: getKeyboardLayoutForInputDevice API should always return " +
- "KeyboardLayoutSelectionResult.FAILED",
- KeyboardLayoutSelectionResult.FAILED,
- keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
- )
- )
- }
- }
-
- @Test
- fun testNewUi_getSetKeyboardLayoutForInputDevice_withImeInfo() {
- NewSettingsApiFlag(true).use {
- val imeSubtype = createImeSubtype()
-
- keyboardLayoutManager.setKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
- ENGLISH_UK_LAYOUT_DESCRIPTOR
- )
- var result =
- keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
- )
- assertEquals(
- "New UI: getKeyboardLayoutForInputDevice API should return the set layout",
- ENGLISH_UK_LAYOUT_DESCRIPTOR,
- result.layoutDescriptor
- )
-
- // This should replace previously set layout
- keyboardLayoutManager.setKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
- ENGLISH_US_LAYOUT_DESCRIPTOR
- )
- result =
- keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
- )
- assertEquals(
- "New UI: getKeyboardLayoutForInputDevice API should return the last set layout",
- ENGLISH_US_LAYOUT_DESCRIPTOR,
- result.layoutDescriptor
- )
- }
- }
-
- @Test
- fun testDefaultUi_getKeyboardLayoutListForInputDevice() {
- NewSettingsApiFlag(false).use {
- val keyboardLayouts =
- keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtype()
- )
- assertEquals("Default UI: getKeyboardLayoutListForInputDevice API should always " +
- "return empty array",
- 0,
- keyboardLayouts.size
- )
- }
- }
+ keyboardLayouts.size
+ )
- @Test
- fun testNewUi_getKeyboardLayoutListForInputDevice() {
- NewSettingsApiFlag(true).use {
- // Check Layouts for "hi-Latn". It should return all 'Latn' keyboard layouts
- var keyboardLayouts =
- keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ // Special case Japanese: UScript ignores provided script code for certain language tags
+ // Should manually match provided script codes and then rely on Uscript to derive
+ // script from language tags and match those.
+ keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTag("hi-Latn")
- )
- assertNotEquals(
- "New UI: getKeyboardLayoutListForInputDevice API should return the list of " +
- "supported layouts with matching script code",
- 0,
- keyboardLayouts.size
- )
- assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " +
- "containing English(US) layout for hi-Latn",
- containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ createImeSubtypeForLanguageTag("ja-Latn-JP")
)
- assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " +
- "containing English(No script code) layout for hi-Latn",
- containsLayout(
- keyboardLayouts,
- createLayoutDescriptor("keyboard_layout_english_without_script_code")
- )
+ assertNotEquals(
+ "getKeyboardLayoutListForInputDevice API should return the list of " +
+ "supported layouts with matching script code for ja-Latn-JP",
+ 0,
+ keyboardLayouts.size
+ )
+ assertTrue("getKeyboardLayoutListForInputDevice API should return a list " +
+ "containing English(US) layout for ja-Latn-JP",
+ containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ )
+ assertTrue("getKeyboardLayoutListForInputDevice API should return a list " +
+ "containing English(No script code) layout for ja-Latn-JP",
+ containsLayout(
+ keyboardLayouts,
+ createLayoutDescriptor("keyboard_layout_english_without_script_code")
)
+ )
- // Check Layouts for "hi" which by default uses 'Deva' script.
- keyboardLayouts =
- keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ // If script code not explicitly provided for Japanese should rely on Uscript to find
+ // derived script code and hence no suitable layout will be found.
+ keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTag("hi")
- )
- assertEquals("New UI: getKeyboardLayoutListForInputDevice API should return empty " +
- "list if no supported layouts available",
- 0,
- keyboardLayouts.size
- )
-
- // If user manually selected some layout, always provide it in the layout list
- val imeSubtype = createImeSubtypeForLanguageTag("hi")
- keyboardLayoutManager.setKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
- ENGLISH_US_LAYOUT_DESCRIPTOR
- )
- keyboardLayouts =
- keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- imeSubtype
- )
- assertEquals("New UI: getKeyboardLayoutListForInputDevice API should return user " +
- "selected layout even if the script is incompatible with IME",
- 1,
- keyboardLayouts.size
- )
-
- // Special case Japanese: UScript ignores provided script code for certain language tags
- // Should manually match provided script codes and then rely on Uscript to derive
- // script from language tags and match those.
- keyboardLayouts =
- keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTag("ja-Latn-JP")
- )
- assertNotEquals(
- "New UI: getKeyboardLayoutListForInputDevice API should return the list of " +
- "supported layouts with matching script code for ja-Latn-JP",
- 0,
- keyboardLayouts.size
- )
- assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " +
- "containing English(US) layout for ja-Latn-JP",
- containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
- )
- assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " +
- "containing English(No script code) layout for ja-Latn-JP",
- containsLayout(
- keyboardLayouts,
- createLayoutDescriptor("keyboard_layout_english_without_script_code")
- )
- )
-
- // If script code not explicitly provided for Japanese should rely on Uscript to find
- // derived script code and hence no suitable layout will be found.
- keyboardLayouts =
- keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTag("ja-JP")
- )
- assertEquals(
- "New UI: getKeyboardLayoutListForInputDevice API should return empty list of " +
- "supported layouts with matching script code for ja-JP",
- 0,
- keyboardLayouts.size
- )
-
- // If IME doesn't have a corresponding language tag, then should show all available
- // layouts no matter the script code.
- keyboardLayouts =
- keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, null
- )
- assertNotEquals(
- "New UI: getKeyboardLayoutListForInputDevice API should return all layouts if" +
- "language tag or subtype not provided",
- 0,
- keyboardLayouts.size
- )
- assertTrue("New UI: getKeyboardLayoutListForInputDevice API should contain Latin " +
- "layouts if language tag or subtype not provided",
- containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ createImeSubtypeForLanguageTag("ja-JP")
)
- assertTrue("New UI: getKeyboardLayoutListForInputDevice API should contain Cyrillic " +
- "layouts if language tag or subtype not provided",
- containsLayout(
- keyboardLayouts,
- createLayoutDescriptor("keyboard_layout_russian")
- )
- )
- }
- }
+ assertEquals(
+ "getKeyboardLayoutListForInputDevice API should return empty list of " +
+ "supported layouts with matching script code for ja-JP",
+ 0,
+ keyboardLayouts.size
+ )
- @Test
- fun testNewUi_getDefaultKeyboardLayoutForInputDevice_withImeLanguageTag() {
- NewSettingsApiFlag(true).use {
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTag("en-US"),
- ENGLISH_US_LAYOUT_DESCRIPTOR
- )
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTag("en-GB"),
- ENGLISH_UK_LAYOUT_DESCRIPTOR
- )
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTag("de"),
- GERMAN_LAYOUT_DESCRIPTOR
- )
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTag("fr-FR"),
- createLayoutDescriptor("keyboard_layout_french")
- )
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTag("ru"),
+ // If IME doesn't have a corresponding language tag, then should show all available
+ // layouts no matter the script code.
+ keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, null
+ )
+ assertNotEquals(
+ "getKeyboardLayoutListForInputDevice API should return all layouts if" +
+ "language tag or subtype not provided",
+ 0,
+ keyboardLayouts.size
+ )
+ assertTrue("getKeyboardLayoutListForInputDevice API should contain Latin " +
+ "layouts if language tag or subtype not provided",
+ containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ )
+ assertTrue("getKeyboardLayoutListForInputDevice API should contain Cyrillic " +
+ "layouts if language tag or subtype not provided",
+ containsLayout(
+ keyboardLayouts,
createLayoutDescriptor("keyboard_layout_russian")
)
- assertEquals(
- "New UI: getDefaultKeyboardLayoutForInputDevice should return " +
- "KeyboardLayoutSelectionResult.FAILED when no layout available",
- KeyboardLayoutSelectionResult.FAILED,
- keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTag("it")
- )
- )
- assertEquals(
- "New UI: getDefaultKeyboardLayoutForInputDevice should return " +
- "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" +
- "available",
- KeyboardLayoutSelectionResult.FAILED,
- keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTag("en-Deva")
- )
- )
- }
+ )
}
@Test
- fun testNewUi_getDefaultKeyboardLayoutForInputDevice_withImeLanguageTagAndLayoutType() {
- NewSettingsApiFlag(true).use {
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTagAndLayoutType("en-US", "qwerty"),
- ENGLISH_US_LAYOUT_DESCRIPTOR
- )
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTagAndLayoutType("en-US", "dvorak"),
- createLayoutDescriptor("keyboard_layout_english_us_dvorak")
- )
- // Try to match layout type even if country doesn't match
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTagAndLayoutType("en-GB", "dvorak"),
- createLayoutDescriptor("keyboard_layout_english_us_dvorak")
- )
- // Choose layout based on layout type priority, if layout type is not provided by IME
- // (Qwerty > Dvorak > Extended)
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTagAndLayoutType("en-US", ""),
- ENGLISH_US_LAYOUT_DESCRIPTOR
- )
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTagAndLayoutType("en-GB", "qwerty"),
- ENGLISH_UK_LAYOUT_DESCRIPTOR
- )
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTagAndLayoutType("de", "qwertz"),
- GERMAN_LAYOUT_DESCRIPTOR
- )
- // Wrong layout type should match with language if provided layout type not available
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTagAndLayoutType("de", "qwerty"),
- GERMAN_LAYOUT_DESCRIPTOR
- )
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTagAndLayoutType("fr-FR", "azerty"),
- createLayoutDescriptor("keyboard_layout_french")
- )
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTagAndLayoutType("ru", "qwerty"),
- createLayoutDescriptor("keyboard_layout_russian_qwerty")
- )
- // If layout type is empty then prioritize KCM with empty layout type
- assertCorrectLayout(
- keyboardDevice,
- createImeSubtypeForLanguageTagAndLayoutType("ru", ""),
- createLayoutDescriptor("keyboard_layout_russian")
+ fun testGetDefaultKeyboardLayoutForInputDevice_withImeLanguageTag() {
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTag("en-US"),
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTag("en-GB"),
+ ENGLISH_UK_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTag("de"),
+ GERMAN_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTag("fr-FR"),
+ createLayoutDescriptor("keyboard_layout_french")
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTag("ru"),
+ createLayoutDescriptor("keyboard_layout_russian")
+ )
+ assertEquals(
+ "getDefaultKeyboardLayoutForInputDevice should return " +
+ "KeyboardLayoutSelectionResult.FAILED when no layout available",
+ KeyboardLayoutSelectionResult.FAILED,
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtypeForLanguageTag("it")
)
- assertEquals("New UI: getDefaultKeyboardLayoutForInputDevice should return " +
+ )
+ assertEquals(
+ "getDefaultKeyboardLayoutForInputDevice should return " +
"KeyboardLayoutSelectionResult.FAILED when no layout for script code is" +
"available",
- KeyboardLayoutSelectionResult.FAILED,
- keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTagAndLayoutType("en-Deva-US", "")
- )
+ KeyboardLayoutSelectionResult.FAILED,
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtypeForLanguageTag("en-Deva")
)
- }
+ )
}
@Test
- fun testNewUi_getDefaultKeyboardLayoutForInputDevice_withHwLanguageTagAndLayoutType() {
- NewSettingsApiFlag(true).use {
- val frenchSubtype = createImeSubtypeForLanguageTagAndLayoutType("fr", "azerty")
- // Should return English dvorak even if IME current layout is French, since HW says the
- // keyboard is a Dvorak keyboard
- assertCorrectLayout(
- englishDvorakKeyboardDevice,
- frenchSubtype,
- createLayoutDescriptor("keyboard_layout_english_us_dvorak")
+ fun testGetDefaultKeyboardLayoutForInputDevice_withImeLanguageTagAndLayoutType() {
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("en-US", "qwerty"),
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("en-US", "dvorak"),
+ createLayoutDescriptor("keyboard_layout_english_us_dvorak")
+ )
+ // Try to match layout type even if country doesn't match
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("en-GB", "dvorak"),
+ createLayoutDescriptor("keyboard_layout_english_us_dvorak")
+ )
+ // Choose layout based on layout type priority, if layout type is not provided by IME
+ // (Qwerty > Dvorak > Extended)
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("en-US", ""),
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("en-GB", "qwerty"),
+ ENGLISH_UK_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("de", "qwertz"),
+ GERMAN_LAYOUT_DESCRIPTOR
+ )
+ // Wrong layout type should match with language if provided layout type not available
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("de", "qwerty"),
+ GERMAN_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("fr-FR", "azerty"),
+ createLayoutDescriptor("keyboard_layout_french")
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("ru", "qwerty"),
+ createLayoutDescriptor("keyboard_layout_russian_qwerty")
+ )
+ // If layout type is empty then prioritize KCM with empty layout type
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("ru", ""),
+ createLayoutDescriptor("keyboard_layout_russian")
+ )
+ assertEquals("getDefaultKeyboardLayoutForInputDevice should return " +
+ "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" +
+ "available",
+ KeyboardLayoutSelectionResult.FAILED,
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtypeForLanguageTagAndLayoutType("en-Deva-US", "")
)
+ )
+ }
- // Back to back changing HW keyboards with same product and vendor ID but different
- // language and layout type should configure the layouts correctly.
- assertCorrectLayout(
- englishQwertyKeyboardDevice,
- frenchSubtype,
- createLayoutDescriptor("keyboard_layout_english_us")
- )
+ @Test
+ fun testGetDefaultKeyboardLayoutForInputDevice_withHwLanguageTagAndLayoutType() {
+ val frenchSubtype = createImeSubtypeForLanguageTagAndLayoutType("fr", "azerty")
+ // Should return English dvorak even if IME current layout is French, since HW says the
+ // keyboard is a Dvorak keyboard
+ assertCorrectLayout(
+ englishDvorakKeyboardDevice,
+ frenchSubtype,
+ createLayoutDescriptor("keyboard_layout_english_us_dvorak")
+ )
- // Fallback to IME information if the HW provided layout script is incompatible with the
- // provided IME subtype
- assertCorrectLayout(
- englishDvorakKeyboardDevice,
- createImeSubtypeForLanguageTagAndLayoutType("ru", ""),
- createLayoutDescriptor("keyboard_layout_russian")
- )
- }
+ // Back to back changing HW keyboards with same product and vendor ID but different
+ // language and layout type should configure the layouts correctly.
+ assertCorrectLayout(
+ englishQwertyKeyboardDevice,
+ frenchSubtype,
+ createLayoutDescriptor("keyboard_layout_english_us")
+ )
+
+ // Fallback to IME information if the HW provided layout script is incompatible with the
+ // provided IME subtype
+ assertCorrectLayout(
+ englishDvorakKeyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("ru", ""),
+ createLayoutDescriptor("keyboard_layout_russian")
+ )
}
@Test
@@ -867,27 +559,25 @@ class KeyboardLayoutManagerTests {
KeyboardLayoutManager.ImeInfo(0, imeInfo,
createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz")))
Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
- NewSettingsApiFlag(true).use {
- keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
- ExtendedMockito.verify {
- FrameworkStatsLog.write(
- ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
- ArgumentMatchers.anyBoolean(),
- ArgumentMatchers.eq(keyboardDevice.vendorId),
- ArgumentMatchers.eq(keyboardDevice.productId),
- ArgumentMatchers.eq(
- createByteArray(
- KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
- LAYOUT_TYPE_DEFAULT,
- GERMAN_LAYOUT_NAME,
- KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
- "de-Latn",
- LAYOUT_TYPE_QWERTZ
- ),
+ keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.eq(keyboardDevice.vendorId),
+ ArgumentMatchers.eq(keyboardDevice.productId),
+ ArgumentMatchers.eq(
+ createByteArray(
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ LAYOUT_TYPE_DEFAULT,
+ GERMAN_LAYOUT_NAME,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+ "de-Latn",
+ LAYOUT_TYPE_QWERTZ
),
- ArgumentMatchers.eq(keyboardDevice.deviceBus),
- )
- }
+ ),
+ ArgumentMatchers.eq(keyboardDevice.deviceBus),
+ )
}
}
@@ -897,27 +587,25 @@ class KeyboardLayoutManagerTests {
KeyboardLayoutManager.ImeInfo(0, imeInfo,
createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz")))
Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
- NewSettingsApiFlag(true).use {
- keyboardLayoutManager.onInputDeviceAdded(englishQwertyKeyboardDevice.id)
- ExtendedMockito.verify {
- FrameworkStatsLog.write(
- ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
- ArgumentMatchers.anyBoolean(),
- ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId),
- ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId),
- ArgumentMatchers.eq(
- createByteArray(
- "en",
- LAYOUT_TYPE_QWERTY,
- ENGLISH_US_LAYOUT_NAME,
- KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE,
- "de-Latn",
- LAYOUT_TYPE_QWERTZ
- )
- ),
- ArgumentMatchers.eq(keyboardDevice.deviceBus),
- )
- }
+ keyboardLayoutManager.onInputDeviceAdded(englishQwertyKeyboardDevice.id)
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId),
+ ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId),
+ ArgumentMatchers.eq(
+ createByteArray(
+ "en",
+ LAYOUT_TYPE_QWERTY,
+ ENGLISH_US_LAYOUT_NAME,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ "de-Latn",
+ LAYOUT_TYPE_QWERTZ
+ )
+ ),
+ ArgumentMatchers.eq(keyboardDevice.deviceBus),
+ )
}
}
@@ -925,27 +613,25 @@ class KeyboardLayoutManagerTests {
fun testConfigurationLogged_onInputDeviceAdded_DefaultSelection() {
val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
- NewSettingsApiFlag(true).use {
- keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
- ExtendedMockito.verify {
- FrameworkStatsLog.write(
- ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
- ArgumentMatchers.anyBoolean(),
- ArgumentMatchers.eq(keyboardDevice.vendorId),
- ArgumentMatchers.eq(keyboardDevice.productId),
- ArgumentMatchers.eq(
- createByteArray(
- KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
- LAYOUT_TYPE_DEFAULT,
- "Default",
- KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT,
- KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
- LAYOUT_TYPE_DEFAULT
- ),
+ keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.eq(keyboardDevice.vendorId),
+ ArgumentMatchers.eq(keyboardDevice.productId),
+ ArgumentMatchers.eq(
+ createByteArray(
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ LAYOUT_TYPE_DEFAULT,
+ "Default",
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT,
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ LAYOUT_TYPE_DEFAULT
),
- ArgumentMatchers.eq(keyboardDevice.deviceBus),
- )
- }
+ ),
+ ArgumentMatchers.eq(keyboardDevice.deviceBus),
+ )
}
}
@@ -953,19 +639,17 @@ class KeyboardLayoutManagerTests {
fun testConfigurationNotLogged_onInputDeviceChanged() {
val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
- NewSettingsApiFlag(true).use {
- keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
- ExtendedMockito.verify({
- FrameworkStatsLog.write(
- ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
- ArgumentMatchers.anyBoolean(),
- ArgumentMatchers.anyInt(),
- ArgumentMatchers.anyInt(),
- ArgumentMatchers.any(ByteArray::class.java),
- ArgumentMatchers.anyInt(),
- )
- }, Mockito.times(0))
- }
+ keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
+ ExtendedMockito.verify({
+ FrameworkStatsLog.write(
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any(ByteArray::class.java),
+ ArgumentMatchers.anyInt(),
+ )
+ }, Mockito.times(0))
}
@Test
@@ -975,18 +659,16 @@ class KeyboardLayoutManagerTests {
Mockito.doReturn(false).`when`(keyboardLayoutManager).isVirtualDevice(
ArgumentMatchers.eq(keyboardDevice.id)
)
- NewSettingsApiFlag(true).use {
- keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
- ExtendedMockito.verify(
- notificationManager,
- Mockito.times(1)
- ).notifyAsUser(
- ArgumentMatchers.isNull(),
- ArgumentMatchers.anyInt(),
- ArgumentMatchers.any(),
- ArgumentMatchers.any()
- )
- }
+ keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
+ ExtendedMockito.verify(
+ notificationManager,
+ Mockito.times(1)
+ ).notifyAsUser(
+ ArgumentMatchers.isNull(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any(),
+ ArgumentMatchers.any()
+ )
}
@Test
@@ -996,18 +678,16 @@ class KeyboardLayoutManagerTests {
Mockito.doReturn(true).`when`(keyboardLayoutManager).isVirtualDevice(
ArgumentMatchers.eq(keyboardDevice.id)
)
- NewSettingsApiFlag(true).use {
- keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
- ExtendedMockito.verify(
- notificationManager,
- Mockito.never()
- ).notifyAsUser(
- ArgumentMatchers.isNull(),
- ArgumentMatchers.anyInt(),
- ArgumentMatchers.any(),
- ArgumentMatchers.any()
- )
- }
+ keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
+ ExtendedMockito.verify(
+ notificationManager,
+ Mockito.never()
+ ).notifyAsUser(
+ ArgumentMatchers.isNull(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any(),
+ ArgumentMatchers.any()
+ )
}
private fun assertCorrectLayout(
@@ -1019,7 +699,7 @@ class KeyboardLayoutManagerTests {
device.identifier, USER_ID, imeInfo, imeSubtype
)
assertEquals(
- "New UI: getDefaultKeyboardLayoutForInputDevice should return $expectedLayout",
+ "getDefaultKeyboardLayoutForInputDevice should return $expectedLayout",
expectedLayout,
result.layoutDescriptor
)
@@ -1123,21 +803,4 @@ class KeyboardLayoutManagerTests {
info.serviceInfo.name = RECEIVER_NAME
return info
}
-
- private inner class NewSettingsApiFlag constructor(enabled: Boolean) : AutoCloseable {
- init {
- Settings.Global.putString(
- context.contentResolver,
- "settings_new_keyboard_ui", enabled.toString()
- )
- }
-
- override fun close() {
- Settings.Global.putString(
- context.contentResolver,
- "settings_new_keyboard_ui",
- ""
- )
- }
- }
}