diff options
973 files changed, 17569 insertions, 11258 deletions
diff --git a/apct-tests/perftests/core/OWNERS b/apct-tests/perftests/core/OWNERS index 8fb057ddcbce..6abab6e27f8e 100644 --- a/apct-tests/perftests/core/OWNERS +++ b/apct-tests/perftests/core/OWNERS @@ -3,4 +3,12 @@ include /graphics/java/android/graphics/fonts/OWNERS # Bug component: 568761 per-file /apct-tests/perftests/core/res/* = felkachang@google.com,zyy@google.com +per-file /apct-tests/perftests/core/res/* = file:/core/java/android/content/om/OWNERS +per-file /apct-tests/perftests/core/src/android/content/res/* = felkachang@google.com +per-file /apct-tests/perftests/core/src/android/content/res/* = file:/core/java/android/content/res/OWNERS + + +# Bug component: 568631 +per-file /apct-tests/perftests/core/src/android/content/om/* = felkachang@google.com +per-file /apct-tests/perftests/core/src/android/content/om/* = file:/core/java/android/content/om/OWNERS diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 29ab45533667..e60ed4ade9b7 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -727,8 +727,11 @@ public final class JobServiceContext implements ServiceConnection { // Exception-throwing-can down the road to JobParameters.completeWork >:( return true; } - mService.mJobs.touchJob(mRunningJob); - return mRunningJob.completeWorkLocked(workId); + if (mRunningJob.completeWorkLocked(workId)) { + mService.mJobs.touchJob(mRunningJob); + return true; + } + return false; } } finally { Binder.restoreCallingIdentity(ident); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index 1971a11ca98a..537a67039a82 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -828,6 +828,10 @@ public final class JobStatus { } } + /** + * Returns {@code true} if the JobWorkItem queue was updated, + * and {@code false} if nothing changed. + */ public boolean completeWorkLocked(int workId) { if (executingWork != null) { final int N = executingWork.size(); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING index a6a3aafdb4f4..6a4a52a5658b 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING +++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING @@ -4,6 +4,7 @@ "name": "CtsUsageStatsTestCases", "options": [ {"include-filter": "android.app.usage.cts.UsageStatsTest"}, + {"include-filter": "android.app.usage.cts.BroadcastResponseStatsTest"}, {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.MediumTest"}, @@ -19,18 +20,6 @@ ] } ], - "presubmit-large": [ - { - "name": "CtsUsageStatsTestCases", - "options": [ - {"include-filter": "android.app.usage.cts.BroadcastResponseStatsTest"}, - {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, - {"exclude-annotation": "androidx.test.filters.FlakyTest"}, - {"exclude-annotation": "androidx.test.filters.MediumTest"}, - {"exclude-annotation": "androidx.test.filters.LargeTest"} - ] - } - ], "postsubmit": [ { "name": "CtsUsageStatsTestCases" diff --git a/core/api/current.txt b/core/api/current.txt index 8a6bb7b7a08b..0536947ac07b 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -6784,7 +6784,7 @@ package android.app { method public boolean areNotificationsEnabled(); method public boolean areNotificationsPaused(); method public boolean canNotifyAsPackage(@NonNull String); - method public boolean canSendFullScreenIntent(); + method public boolean canUseFullScreenIntent(); method public void cancel(int); method public void cancel(@Nullable String, int); method public void cancelAll(); @@ -7269,6 +7269,13 @@ package android.app { method public void onSharedElementsReady(); } + public final class StartForegroundCalledOnStoppedServiceException extends java.lang.IllegalStateException implements android.os.Parcelable { + ctor public StartForegroundCalledOnStoppedServiceException(@NonNull String); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.StartForegroundCalledOnStoppedServiceException> CREATOR; + } + public class StatusBarManager { method @RequiresPermission(android.Manifest.permission.LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE) public boolean canLaunchCaptureContentActivityForNote(@NonNull android.app.Activity); method public void requestAddTileService(@NonNull android.content.ComponentName, @NonNull CharSequence, @NonNull android.graphics.drawable.Icon, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); @@ -19527,9 +19534,9 @@ package android.hardware.display { public final class DisplayManager { method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int); - method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, float, @Nullable android.view.Surface, int); method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable android.hardware.display.VirtualDisplay.Callback, @Nullable android.os.Handler); - method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, float, @Nullable android.view.Surface, int, @Nullable android.os.Handler, @Nullable android.hardware.display.VirtualDisplay.Callback); + method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig); + method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig, @Nullable android.os.Handler, @Nullable android.hardware.display.VirtualDisplay.Callback); method public android.view.Display getDisplay(int); method public android.view.Display[] getDisplays(); method public android.view.Display[] getDisplays(String); @@ -19584,6 +19591,30 @@ package android.hardware.display { method public void onStopped(); } + public final class VirtualDisplayConfig implements android.os.Parcelable { + method public int describeContents(); + method public int getDensityDpi(); + method @NonNull public java.util.List<java.lang.String> getDisplayCategories(); + method public int getFlags(); + method public int getHeight(); + method @NonNull public String getName(); + method public float getRequestedRefreshRate(); + method @Nullable public android.view.Surface getSurface(); + method public int getWidth(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.VirtualDisplayConfig> CREATOR; + } + + public static final class VirtualDisplayConfig.Builder { + ctor public VirtualDisplayConfig.Builder(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int); + method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder addDisplayCategory(@NonNull String); + method @NonNull public android.hardware.display.VirtualDisplayConfig build(); + method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCategories(@NonNull java.util.List<java.lang.String>); + method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setFlags(int); + method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setRequestedRefreshRate(@FloatRange(from=0.0f) float); + method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setSurface(@Nullable android.view.Surface); + } + } package android.hardware.fingerprint { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 77fb8d09ed97..9bab8cd8a94b 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1699,7 +1699,7 @@ package android.app.backup { method @RequiresPermission(android.Manifest.permission.BACKUP) public boolean isBackupEnabled(); method @RequiresPermission(android.Manifest.permission.BACKUP) public boolean isBackupServiceActive(android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.BACKUP) public String[] listAllTransports(); - method @NonNull public void reportDelayedRestoreResult(@NonNull android.app.backup.BackupRestoreEventLogger); + method public void reportDelayedRestoreResult(@NonNull android.app.backup.BackupRestoreEventLogger); method @RequiresPermission(android.Manifest.permission.BACKUP) public int requestBackup(String[], android.app.backup.BackupObserver); method @RequiresPermission(android.Manifest.permission.BACKUP) public int requestBackup(String[], android.app.backup.BackupObserver, android.app.backup.BackupManagerMonitor, int); method @Deprecated public int requestRestore(android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor); @@ -3217,8 +3217,8 @@ package android.companion.virtual { method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); method @NonNull public android.content.Context createContext(); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback); - method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); - method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @NonNull java.util.List<java.lang.String>, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); + method @Deprecated @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); + method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualDpad createVirtualDpad(@NonNull android.hardware.input.VirtualDpadConfig); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.input.VirtualKeyboardConfig); method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); @@ -3347,10 +3347,15 @@ package android.companion.virtual.sensor { public interface VirtualSensorCallback { method public void onConfigurationChanged(@NonNull android.companion.virtual.sensor.VirtualSensor, boolean, @NonNull java.time.Duration, @NonNull java.time.Duration); + method public default void onDirectChannelConfigured(@IntRange(from=1) int, @NonNull android.companion.virtual.sensor.VirtualSensor, int, @IntRange(from=1) int); + method public default void onDirectChannelCreated(@IntRange(from=1) int, @NonNull android.os.SharedMemory); + method public default void onDirectChannelDestroyed(@IntRange(from=1) int); } public final class VirtualSensorConfig implements android.os.Parcelable { method public int describeContents(); + method public int getDirectChannelTypesSupported(); + method public int getHighestDirectReportRateLevel(); method @NonNull public String getName(); method public int getType(); method @Nullable public String getVendor(); @@ -3361,6 +3366,8 @@ package android.companion.virtual.sensor { public static final class VirtualSensorConfig.Builder { ctor public VirtualSensorConfig.Builder(int, @NonNull String); method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build(); + method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setDirectChannelTypesSupported(int); + method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setHighestDirectReportRateLevel(int); method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setVendor(@Nullable String); } @@ -10021,22 +10028,23 @@ package android.net.wifi.sharedconnectivity.app { public final class KnownNetwork implements android.os.Parcelable { method public int describeContents(); - method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo getDeviceInfo(); + method @Nullable public android.net.wifi.sharedconnectivity.app.DeviceInfo getDeviceInfo(); method public int getNetworkSource(); - method @NonNull public int[] getSecurityTypes(); + method @NonNull public java.util.Set<java.lang.Integer> getSecurityTypes(); method @NonNull public String getSsid(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.KnownNetwork> CREATOR; - field public static final int NETWORK_SOURCE_CLOUD_SELF = 1; // 0x1 - field public static final int NETWORK_SOURCE_NEARBY_SELF = 0; // 0x0 + field public static final int NETWORK_SOURCE_CLOUD_SELF = 2; // 0x2 + field public static final int NETWORK_SOURCE_NEARBY_SELF = 1; // 0x1 + field public static final int NETWORK_SOURCE_UNKNOWN = 0; // 0x0 } public static final class KnownNetwork.Builder { ctor public KnownNetwork.Builder(); + method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder addSecurityType(int); method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork build(); - method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setDeviceInfo(@NonNull android.net.wifi.sharedconnectivity.app.DeviceInfo); + method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setDeviceInfo(@Nullable android.net.wifi.sharedconnectivity.app.DeviceInfo); method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setNetworkSource(int); - method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setSecurityTypes(@NonNull int[]); method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setSsid(@NonNull String); } @@ -10072,17 +10080,17 @@ package android.net.wifi.sharedconnectivity.app { } public class SharedConnectivityManager { - method public boolean connectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork); - method public boolean connectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork); - method public boolean disconnectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork); - method public boolean forgetKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork); - method @Nullable public android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus getKnownNetworkConnectionStatus(); - method @NonNull public java.util.List<android.net.wifi.sharedconnectivity.app.KnownNetwork> getKnownNetworks(); - method @Nullable public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState getSettingsState(); - method @Nullable public android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus getTetherNetworkConnectionStatus(); - method @NonNull public java.util.List<android.net.wifi.sharedconnectivity.app.TetherNetwork> getTetherNetworks(); - method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback); - method public boolean unregisterCallback(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean connectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean connectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean disconnectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean forgetKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus getKnownNetworkConnectionStatus(); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.sharedconnectivity.app.KnownNetwork> getKnownNetworks(); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState getSettingsState(); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus getTetherNetworkConnectionStatus(); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.sharedconnectivity.app.TetherNetwork> getTetherNetworks(); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean unregisterCallback(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback); } public final class SharedConnectivitySettingsState implements android.os.Parcelable { @@ -10105,7 +10113,7 @@ package android.net.wifi.sharedconnectivity.app { method public long getDeviceId(); method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo getDeviceInfo(); method @Nullable public String getHotspotBssid(); - method @Nullable public int[] getHotspotSecurityTypes(); + method @NonNull public java.util.Set<java.lang.Integer> getHotspotSecurityTypes(); method @Nullable public String getHotspotSsid(); method @NonNull public String getNetworkName(); method public int getNetworkType(); @@ -10119,11 +10127,11 @@ package android.net.wifi.sharedconnectivity.app { public static final class TetherNetwork.Builder { ctor public TetherNetwork.Builder(); + method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder addHotspotSecurityType(int); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork build(); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setDeviceId(long); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setDeviceInfo(@NonNull android.net.wifi.sharedconnectivity.app.DeviceInfo); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotBssid(@NonNull String); - method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotSecurityTypes(@NonNull int[]); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotSsid(@NonNull String); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setNetworkName(@NonNull String); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setNetworkType(int); @@ -13255,22 +13263,18 @@ package android.service.voice { public abstract class VisualQueryDetectionService extends android.app.Service implements android.service.voice.SandboxedDetectionServiceBase { ctor public VisualQueryDetectionService(); + method public final void finishQuery() throws java.lang.IllegalStateException; + method public final void gainedAttention(); + method public final void lostAttention(); method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent); - method public void onStartDetection(@NonNull android.service.voice.VisualQueryDetectionService.Callback); + method public void onStartDetection(); method public void onStopDetection(); method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer); + method public final void rejectQuery() throws java.lang.IllegalStateException; + method public final void streamQuery(@NonNull String) throws java.lang.IllegalStateException; field public static final String SERVICE_INTERFACE = "android.service.voice.VisualQueryDetectionService"; } - public static final class VisualQueryDetectionService.Callback { - ctor public VisualQueryDetectionService.Callback(); - method public void onAttentionGained(); - method public void onAttentionLost(); - method public void onQueryDetected(@NonNull String) throws java.lang.IllegalStateException; - method public void onQueryFinished() throws java.lang.IllegalStateException; - method public void onQueryRejected() throws java.lang.IllegalStateException; - } - public final class VisualQueryDetectionServiceFailure extends android.service.voice.DetectorFailure { method public int getErrorCode(); method public int getSuggestedAction(); @@ -17283,9 +17287,9 @@ package android.view.accessibility { public final class AccessibilityManager { method public int getAccessibilityWindowId(@Nullable android.os.IBinder); method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut(); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public boolean registerDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy); + method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_ACCESSIBILITY, android.Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean registerDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy); method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull android.app.RemoteAction, int); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public boolean unregisterDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy); + method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_ACCESSIBILITY, android.Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean unregisterDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy); method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int); } diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 6422865c043a..3615435b7d75 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -198,6 +198,26 @@ import java.util.function.Consumer; * possible for a node to contain outdated information because the window content may change at any * time. * </p> + * <h3>Drawing Accessibility Overlays</h3> + * <p>Accessibility services can draw overlays on top of existing screen contents. + * Accessibility overlays can be used to visually highlight items on the screen + * e.g. indicate the current item with accessibility focus. + * Overlays can also offer the user a way to interact with the service directly and quickly + * customize the service's behavior.</p> + * <p>Accessibility overlays can be attached to a particular window or to the display itself. + * Attaching an overlay to a window allows the overly to move, grow and shrink as the window does. + * The overlay will maintain the same relative position within the window bounds as the window + * moves. The overlay will also maintain the same relative position within the window bounds if + * the window is resized. + * To attach an overlay to a window, use {@link attachAccessibilityOverlayToWindow}. + * Attaching an overlay to the display means that the overlay is independent of the active + * windows on that display. + * To attach an overlay to a display, use {@link attachAccessibilityOverlayToDisplay}. </p> + * <p> When positioning an overlay that is attached to a window, the service must use window + * coordinates. In order to position an overlay on top of an existing UI element it is necessary + * to know the bounds of that element in window coordinates. To find the bounds in window + * coordinates of an element, find the corresponding {@link AccessibilityNodeInfo} as discussed + * above and call {@link AccessibilityNodeInfo#getBoundsInWindow}. </p> * <h3>Notification strategy</h3> * <p> * All accessibility services are notified of all events they have requested, regardless of their @@ -3421,22 +3441,28 @@ public abstract class AccessibilityService extends Service { } /** - * Attaches a {@link android.view.SurfaceControl} containing an accessibility + * <p>Attaches a {@link android.view.SurfaceControl} containing an accessibility * overlay to the * specified display. This type of overlay should be used for content that does * not need to * track the location and size of Views in the currently active app e.g. service * configuration - * or general service UI. To remove this overlay and free the associated + * or general service UI.</p> + * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}. + * To embed the View into a {@link android.view.SurfaceControl}, create a + * {@link android.view.SurfaceControlViewHost} and attach the View using + * {@link android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by + * calling <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.</p> + * <p>To remove this overlay and free the associated * resources, use - * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>. - * If the specified overlay has already been attached to the specified display + * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.</p> + * <p>If the specified overlay has already been attached to the specified display * this method does nothing. * If the specified overlay has already been attached to a previous display this * function will transfer the overlay to the new display. * Services can attach multiple overlays. Use * <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>. - * to coordinate the order of the overlays on screen. + * to coordinate the order of the overlays on screen.</p> * * @param displayId the display to which the SurfaceControl should be attached. * @param sc the SurfaceControl containing the overlay content @@ -3456,20 +3482,24 @@ public abstract class AccessibilityService extends Service { } /** - * Attaches an accessibility overlay {@link android.view.SurfaceControl} to the + * <p>Attaches an accessibility overlay {@link android.view.SurfaceControl} to the * specified * window. This method should be used when you want the overlay to move and - * resize as the parent - * window moves and resizes. To remove this overlay and free the associated - * resources, use - * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>. - * If the specified overlay has already been attached to the specified window + * resize as the parent window moves and resizes.</p> + * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}. + * To embed the View into a {@link android.view.SurfaceControl}, create a + * {@link android.view.SurfaceControlViewHost} and attach the View using + * {@link android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by + * calling <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.</p> + * <p>To remove this overlay and free the associated resources, use + * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.</p> + * <p>If the specified overlay has already been attached to the specified window * this method does nothing. * If the specified overlay has already been attached to a previous window this * function will transfer the overlay to the new window. * Services can attach multiple overlays. Use * <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>. - * to coordinate the order of the overlays on screen. + * to coordinate the order of the overlays on screen.</p> * * @param accessibilityWindowId The window id, from * {@link AccessibilityWindowInfo#getId()}. diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index b9f38d3c8907..d810f055e96e 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -968,14 +968,6 @@ public abstract class ActivityManagerInternal { public abstract void stopForegroundServiceDelegate(@NonNull ServiceConnection connection); /** - * Called by PowerManager. Return whether a given procstate is allowed to hold - * wake locks in deep doze. Because it's called with the power manager lock held, we can't - * hold AM locks in it. - * @hide - */ - public abstract boolean canHoldWakeLocksInDeepDoze(int uid, int procstate); - - /** * Same as {@link android.app.IActivityManager#startProfile(int userId)}, but it would succeed * even if the profile is disabled - it should only be called by * {@link com.android.server.devicepolicy.DevicePolicyManagerService} when starting a profile diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b0929b5d4f64..dfdfd0e2054e 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -36,6 +36,7 @@ import static android.window.ConfigurationHelper.isDifferentDisplay; import static android.window.ConfigurationHelper.shouldUpdateResources; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; +import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL; import android.annotation.NonNull; import android.annotation.Nullable; @@ -49,6 +50,7 @@ import android.app.assist.AssistStructure; import android.app.backup.BackupAgent; import android.app.backup.BackupAnnotations.BackupDestination; import android.app.backup.BackupAnnotations.OperationType; +import android.app.compat.CompatChanges; import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ActivityLifecycleItem.LifecycleState; import android.app.servertransaction.ActivityRelaunchItem; @@ -206,6 +208,7 @@ import com.android.internal.content.ReferrerIntent; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.BinderInternal; import com.android.internal.os.RuntimeInit; +import com.android.internal.os.SafeZipPathValidatorCallback; import com.android.internal.os.SomeArgs; import com.android.internal.policy.DecorView; import com.android.internal.util.ArrayUtils; @@ -219,6 +222,7 @@ import dalvik.system.AppSpecializationHooks; import dalvik.system.CloseGuard; import dalvik.system.VMDebug; import dalvik.system.VMRuntime; +import dalvik.system.ZipPathValidator; import libcore.io.ForwardingOs; import libcore.io.IoUtils; @@ -554,9 +558,6 @@ public final class ActivityThread extends ClientTransactionHandler boolean hideForNow; Configuration createdConfig; Configuration overrideConfig; - // TODO(b/263402465): pass deviceId directly in LaunchActivityItem#execute - // The deviceId assigned by the server when this activity was first started. - int mDeviceId; // Used for consolidating configs before sending on to Activity. private Configuration tmpConfig = new Configuration(); // Callback used for updating activity override config and camera compat control state. @@ -619,7 +620,7 @@ public final class ActivityThread extends ClientTransactionHandler } public ActivityClientRecord(IBinder token, Intent intent, int ident, - ActivityInfo info, Configuration overrideConfig, int deviceId, + ActivityInfo info, Configuration overrideConfig, String referrer, IVoiceInteractor voiceInteractor, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions, @@ -641,7 +642,6 @@ public final class ActivityThread extends ClientTransactionHandler this.isForward = isForward; this.profilerInfo = profilerInfo; this.overrideConfig = overrideConfig; - this.mDeviceId = deviceId; this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo); mActivityOptions = activityOptions; mLaunchedFromBubble = launchedFromBubble; @@ -3872,7 +3872,7 @@ public final class ActivityThread extends ClientTransactionHandler */ @Override public Activity handleLaunchActivity(ActivityClientRecord r, - PendingTransactionActions pendingActions, Intent customIntent) { + PendingTransactionActions pendingActions, int deviceId, Intent customIntent) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); @@ -3885,7 +3885,7 @@ public final class ActivityThread extends ClientTransactionHandler // Make sure we are running with the most recent config. mConfigurationController.handleConfigurationChanged(null, null); - updateDeviceIdForNonUIContexts(r.mDeviceId); + updateDeviceIdForNonUIContexts(deviceId); if (localLOGV) Slog.v( TAG, "Handling launch of " + r); @@ -5942,7 +5942,7 @@ public final class ActivityThread extends ClientTransactionHandler r.startsNotResumed = startsNotResumed; r.overrideConfig = overrideConfig; - handleLaunchActivity(r, pendingActions, customIntent); + handleLaunchActivity(r, pendingActions, mLastReportedDeviceId, customIntent); } @Override @@ -6703,6 +6703,11 @@ public final class ActivityThread extends ClientTransactionHandler // Let libcore handle any compat changes after installing the list of compat changes. AppSpecializationHooks.handleCompatChangesBeforeBindingApplication(); + // Initialize the zip path validator callback depending on the targetSdk. + // This has to be after AppCompatCallbacks#install() so that the Compat + // checks work accordingly. + initZipPathValidatorCallback(); + mBoundApplication = data; mConfigurationController.setConfiguration(data.config); mConfigurationController.setCompatConfiguration(data.config); @@ -7070,6 +7075,18 @@ public final class ActivityThread extends ClientTransactionHandler } } + /** + * If targetSDK >= U: set the safe zip path validator callback which disallows dangerous zip + * entry names. + * Otherwise: clear the callback to the default validation. + */ + private void initZipPathValidatorCallback() { + if (CompatChanges.isChangeEnabled(VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL)) { + ZipPathValidator.setCallback(new SafeZipPathValidatorCallback()); + } else { + ZipPathValidator.clearCallback(); + } + } private void handleSetContentCaptureOptionsCallback(String packageName) { if (mContentCaptureOptionsCallback != null) { diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index a99815c20fe6..6301ad7f1278 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -556,12 +556,7 @@ public class ApplicationPackageManager extends PackageManager { @Override public ActivityInfo getActivityInfo(ComponentName className, ComponentInfoFlags flags) throws NameNotFoundException { - return getActivityInfoAsUser(className, flags, getUserId()); - } - - @Override - public ActivityInfo getActivityInfoAsUser(ComponentName className, - ComponentInfoFlags flags, @UserIdInt int userId) throws NameNotFoundException { + final int userId = getUserId(); try { ActivityInfo ai = mPM.getActivityInfo(className, updateFlagsForComponent(flags.getValue(), userId, null), userId); diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index fe40a4ce3d13..f48181b95892 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -60,7 +60,6 @@ public class BroadcastOptions extends ComponentOptions { private String[] mRequireNoneOfPermissions; private long mRequireCompatChangeId = CHANGE_INVALID; private long mIdForResponseEvent; - private @Nullable IntentFilter mRemoveMatchingFilter; private @DeliveryGroupPolicy int mDeliveryGroupPolicy; private @Nullable String mDeliveryGroupMatchingKey; private @Nullable BundleMerger mDeliveryGroupExtrasMerger; @@ -190,12 +189,6 @@ public class BroadcastOptions extends ComponentOptions { "android:broadcast.idForResponseEvent"; /** - * Corresponds to {@link #setRemoveMatchingFilter}. - */ - private static final String KEY_REMOVE_MATCHING_FILTER = - "android:broadcast.removeMatchingFilter"; - - /** * Corresponds to {@link #setDeliveryGroupPolicy(int)}. */ private static final String KEY_DELIVERY_GROUP_POLICY = @@ -274,18 +267,6 @@ public class BroadcastOptions extends ComponentOptions { } /** - * {@hide} - * @deprecated use {@link #setDeliveryGroupMatchingFilter(IntentFilter)} instead. - */ - @Deprecated - public static @NonNull BroadcastOptions makeRemovingMatchingFilter( - @NonNull IntentFilter removeMatchingFilter) { - BroadcastOptions opts = new BroadcastOptions(); - opts.setRemoveMatchingFilter(removeMatchingFilter); - return opts; - } - - /** * Creates a new {@code BroadcastOptions} with no options initially set. */ public BroadcastOptions() { @@ -315,8 +296,6 @@ public class BroadcastOptions extends ComponentOptions { mRequireNoneOfPermissions = opts.getStringArray(KEY_REQUIRE_NONE_OF_PERMISSIONS); mRequireCompatChangeId = opts.getLong(KEY_REQUIRE_COMPAT_CHANGE_ID, CHANGE_INVALID); mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT); - mRemoveMatchingFilter = opts.getParcelable(KEY_REMOVE_MATCHING_FILTER, - IntentFilter.class); mDeliveryGroupPolicy = opts.getInt(KEY_DELIVERY_GROUP_POLICY, DELIVERY_GROUP_POLICY_ALL); mDeliveryGroupMatchingKey = opts.getString(KEY_DELIVERY_GROUP_KEY); @@ -797,31 +776,6 @@ public class BroadcastOptions extends ComponentOptions { } /** - * When enqueuing this broadcast, remove all pending broadcasts previously - * sent by this app which match the given filter. - * <p> - * For example, sending {@link Intent#ACTION_SCREEN_ON} would typically want - * to remove any pending {@link Intent#ACTION_SCREEN_OFF} broadcasts. - * - * @hide - * @deprecated use {@link #setDeliveryGroupMatchingFilter(IntentFilter)} instead. - */ - @Deprecated - public void setRemoveMatchingFilter(@NonNull IntentFilter removeMatchingFilter) { - mRemoveMatchingFilter = Objects.requireNonNull(removeMatchingFilter); - } - - /** @hide */ - public void clearRemoveMatchingFilter() { - mRemoveMatchingFilter = null; - } - - /** @hide */ - public @Nullable IntentFilter getRemoveMatchingFilter() { - return mRemoveMatchingFilter; - } - - /** * Set delivery group policy for this broadcast to specify how multiple broadcasts belonging to * the same delivery group has to be handled. * @@ -1092,9 +1046,6 @@ public class BroadcastOptions extends ComponentOptions { if (mIdForResponseEvent != 0) { b.putLong(KEY_ID_FOR_RESPONSE_EVENT, mIdForResponseEvent); } - if (mRemoveMatchingFilter != null) { - b.putParcelable(KEY_REMOVE_MATCHING_FILTER, mRemoveMatchingFilter); - } if (mDeliveryGroupPolicy != DELIVERY_GROUP_POLICY_ALL) { b.putInt(KEY_DELIVERY_GROUP_POLICY, mDeliveryGroupPolicy); } diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index 6bb38e7294b0..49fb794a0a25 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -188,7 +188,7 @@ public abstract class ClientTransactionHandler { /** Perform activity launch. */ public abstract Activity handleLaunchActivity(@NonNull ActivityClientRecord r, - PendingTransactionActions pendingActions, Intent customIntent); + PendingTransactionActions pendingActions, int deviceId, Intent customIntent); /** Perform activity start. */ public abstract void handleStartActivity(@NonNull ActivityClientRecord r, diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index e3ec4937ba2c..4713a3135976 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2814,7 +2814,7 @@ class ContextImpl extends Context { public @NonNull Context createDeviceContext(int deviceId) { if (deviceId != Context.DEVICE_ID_DEFAULT) { VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class); - if (!vdm.isValidVirtualDeviceId(deviceId)) { + if (vdm == null || !vdm.isValidVirtualDeviceId(deviceId)) { throw new IllegalArgumentException( "Not a valid ID of the default device or any virtual device: " + deviceId); } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index f653e132f7e3..5c38c42fe21f 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -341,12 +341,6 @@ interface IActivityManager { in String message, boolean force, int exceptionTypeId); void crashApplicationWithTypeWithExtras(int uid, int initialPid, in String packageName, int userId, in String message, boolean force, int exceptionTypeId, in Bundle extras); - /** @deprecated -- use getProviderMimeTypeAsync */ - @UnsupportedAppUsage(maxTargetSdk = 29, publicAlternatives = - "Use {@link android.content.ContentResolver#getType} public API instead.") - String getProviderMimeType(in Uri uri, int userId); - - oneway void getProviderMimeTypeAsync(in Uri uri, int userId, in RemoteCallback resultCallback); oneway void getMimeTypeFilterAsync(in Uri uri, int userId, in RemoteCallback resultCallback); // Cause the specified process to dump the specified heap. boolean dumpHeap(in String process, int userId, boolean managed, boolean mallocInfo, diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java index 69693fc3977b..f4cd60d08804 100644 --- a/core/java/android/app/LocaleConfig.java +++ b/core/java/android/app/LocaleConfig.java @@ -38,7 +38,10 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Set; @@ -263,6 +266,43 @@ public class LocaleConfig implements Parcelable { }; /** + * Compare whether the LocaleConfig is the same. + * + * <p>If the elements of {@code mLocales} in LocaleConfig are the same but arranged in different + * positions, they are also considered to be the same LocaleConfig. + * + * @param other The {@link LocaleConfig} to compare for. + * + * @return true if the LocaleConfig is the same, false otherwise. + * + * @hide + */ + public boolean isSameLocaleConfig(@Nullable LocaleConfig other) { + if (other == this) { + return true; + } + + if (other != null) { + if (mStatus != other.mStatus) { + return false; + } + LocaleList otherLocales = other.mLocales; + if (mLocales == null && otherLocales == null) { + return true; + } else if (mLocales != null && otherLocales != null) { + List<String> hostStrList = Arrays.asList(mLocales.toLanguageTags().split(",")); + List<String> targetStrList = Arrays.asList( + otherLocales.toLanguageTags().split(",")); + Collections.sort(hostStrList); + Collections.sort(targetStrList); + return hostStrList.equals(targetStrList); + } + } + + return false; + } + + /** * Compare whether the locale is existed in the {@code mLocales} of the LocaleConfig. * * @param locale The {@link Locale} to compare for. diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 91efa755e4c9..d2f2c3c4e95a 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -873,7 +873,7 @@ public class NotificationManager { * permission to your manifest, and use * {@link android.provider.Settings#ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT}. */ - public boolean canSendFullScreenIntent() { + public boolean canUseFullScreenIntent() { final int result = PermissionChecker.checkPermissionForPreflight(mContext, android.Manifest.permission.USE_FULL_SCREEN_INTENT, mContext.getAttributionSource()); diff --git a/core/java/android/app/StartForegroundCalledOnStoppedServiceException.java b/core/java/android/app/StartForegroundCalledOnStoppedServiceException.java new file mode 100644 index 000000000000..55885a65a265 --- /dev/null +++ b/core/java/android/app/StartForegroundCalledOnStoppedServiceException.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Exception thrown when {@link Service#startForeground} is called on a service that's not + * actually started. + */ +public final class StartForegroundCalledOnStoppedServiceException + extends IllegalStateException implements Parcelable { + /** + * Constructor. + */ + public StartForegroundCalledOnStoppedServiceException(@NonNull String message) { + super(message); + } + + StartForegroundCalledOnStoppedServiceException(@NonNull Parcel source) { + super(source.readString()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(getMessage()); + } + + public static final @NonNull Creator<StartForegroundCalledOnStoppedServiceException> + CREATOR = new Creator<StartForegroundCalledOnStoppedServiceException>() { + @NonNull + public StartForegroundCalledOnStoppedServiceException createFromParcel( + Parcel source) { + return new StartForegroundCalledOnStoppedServiceException(source); + } + + @NonNull + public StartForegroundCalledOnStoppedServiceException[] newArray(int size) { + return new StartForegroundCalledOnStoppedServiceException[size]; + } + }; +} diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 6d80a44d3d60..83e015357589 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -877,6 +877,10 @@ public final class SystemServiceRegistry { @Override public VirtualDeviceManager createService(ContextImpl ctx) throws ServiceNotFoundException { + if (!ctx.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) { + return null; + } IVirtualDeviceManager service = IVirtualDeviceManager.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.VIRTUAL_DEVICE_SERVICE)); return new VirtualDeviceManager(service, ctx.getOuterContext()); @@ -1648,6 +1652,7 @@ public final class SystemServiceRegistry { case Context.ETHERNET_SERVICE: case Context.CONTEXTHUB_SERVICE: case Context.VIRTUALIZATION_SERVICE: + case Context.VIRTUAL_DEVICE_SERVICE: return null; } Slog.wtf(TAG, "Manager wrapper not available: " + name); diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 5a2f2616dc13..303ada0fec9c 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -453,6 +453,7 @@ public class TaskInfo { && Objects.equals(shouldDockBigOverlays, that.shouldDockBigOverlays) && Objects.equals(displayCutoutInsets, that.displayCutoutInsets) && getWindowingMode() == that.getWindowingMode() + && configuration.uiMode == that.configuration.uiMode && Objects.equals(taskDescription, that.taskDescription) && isFocused == that.isFocused && isVisible == that.isVisible @@ -481,6 +482,7 @@ public class TaskInfo { .equals(that.configuration.windowConfiguration.getBounds())) && (!hasCompatUI() || configuration.getLayoutDirection() == that.configuration.getLayoutDirection()) + && (!hasCompatUI() || configuration.uiMode == that.configuration.uiMode) && (!hasCompatUI() || isVisible == that.isVisible); } diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index e750f498248a..5848521b3045 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -1091,7 +1091,6 @@ public class BackupManager { * * @hide */ - @NonNull @SystemApi public void reportDelayedRestoreResult(@NonNull BackupRestoreEventLogger logger) { checkServiceBinder(); diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index f56c8c3b0bdb..dcac59c19df4 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -656,11 +656,14 @@ public class BackupTransport { } /** - * Ask the transport for a {@link IBackupManagerMonitor} instance which will be used by the + * Ask the transport for a {@link BackupManagerMonitor} instance which will be used by the * framework to report logging events back to the transport. * * <p>Backups requested from outside the framework may pass in a monitor with the request, * however backups initiated by the framework will call this method to retrieve one. + * + * @return {@link BackupManagerMonitor} or {@code null} if the transport implementation does not + * wish to receive the logging events. */ @Nullable public BackupManagerMonitor getBackupManagerMonitor() { diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index 3d0aa2540068..5833f1b87254 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -96,11 +96,11 @@ public class LaunchActivityItem extends ClientTransactionItem { PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, - mOverrideConfig, mDeviceId, mReferrer, mVoiceInteractor, mState, mPersistentState, + mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble, mTaskFragmentToken); - client.handleLaunchActivity(r, pendingActions, null /* customIntent */); + client.handleLaunchActivity(r, pendingActions, mDeviceId, null /* customIntent */); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java index c8f7d100a398..bfab61f3ae90 100644 --- a/core/java/android/app/servertransaction/TransactionExecutor.java +++ b/core/java/android/app/servertransaction/TransactionExecutor.java @@ -32,6 +32,7 @@ import static android.app.servertransaction.TransactionExecutorHelper.transactio import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; +import android.content.Context; import android.os.IBinder; import android.util.IntArray; import android.util.Slog; @@ -218,7 +219,7 @@ public class TransactionExecutor { switch (state) { case ON_CREATE: mTransactionHandler.handleLaunchActivity(r, mPendingActions, - null /* customIntent */); + Context.DEVICE_ID_INVALID, null /* customIntent */); break; case ON_START: mTransactionHandler.handleStartActivity(r, mPendingActions, diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 4d2c7cb3d1aa..6cc4c8a24c48 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -89,14 +89,6 @@ public final class VirtualDeviceManager { private static final String TAG = "VirtualDeviceManager"; - private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS = - DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC - | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT - | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY - | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL - | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH - | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS; - /** * Broadcast Action: A Virtual Device was removed. * @@ -539,7 +531,11 @@ public final class VirtualDeviceManager { * not create the virtual display. * * @see DisplayManager#createVirtualDisplay + * + * @deprecated use {@link #createVirtualDisplay(VirtualDisplayConfig, Executor, + * VirtualDisplay.Callback)} */ + @Deprecated @Nullable public VirtualDisplay createVirtualDisplay( @IntRange(from = 1) int width, @@ -552,30 +548,16 @@ public final class VirtualDeviceManager { VirtualDisplayConfig config = new VirtualDisplayConfig.Builder( getVirtualDisplayName(), width, height, densityDpi) .setSurface(surface) - .setFlags(getVirtualDisplayFlags(flags)) + .setFlags(flags) .build(); - return createVirtualDisplayInternal(config, executor, callback); + return createVirtualDisplay(config, executor, callback); } /** * Creates a virtual display for this virtual device. All displays created on the same * device belongs to the same display group. * - * @param width The width of the virtual display in pixels, must be greater than 0. - * @param height The height of the virtual display in pixels, must be greater than 0. - * @param densityDpi The density of the virtual display in dpi, must be greater than 0. - * @param displayCategories The categories of the virtual display, indicating the type of - * activities allowed to run on the display. Activities can declare their type using - * {@link android.content.pm.ActivityInfo#requiredDisplayCategory}. - * @param surface The surface to which the content of the virtual display should - * be rendered, or null if there is none initially. The surface can also be set later using - * {@link VirtualDisplay#setSurface(Surface)}. - * @param flags A combination of virtual display flags accepted by - * {@link DisplayManager#createVirtualDisplay}. In addition, the following flags are - * automatically set for all virtual devices: - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC VIRTUAL_DISPLAY_FLAG_PUBLIC} and - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY - * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}. + * @param config The configuration of the display. * @param executor The executor on which {@code callback} will be invoked. This is ignored * if {@code callback} is {@code null}. If {@code callback} is specified, this executor must * not be null. @@ -587,28 +569,6 @@ public final class VirtualDeviceManager { */ @Nullable public VirtualDisplay createVirtualDisplay( - @IntRange(from = 1) int width, - @IntRange(from = 1) int height, - @IntRange(from = 1) int densityDpi, - @NonNull List<String> displayCategories, - @Nullable Surface surface, - @VirtualDisplayFlag int flags, - @Nullable @CallbackExecutor Executor executor, - @Nullable VirtualDisplay.Callback callback) { - VirtualDisplayConfig config = new VirtualDisplayConfig.Builder( - getVirtualDisplayName(), width, height, densityDpi) - .setDisplayCategories(displayCategories) - .setSurface(surface) - .setFlags(getVirtualDisplayFlags(flags)) - .build(); - return createVirtualDisplayInternal(config, executor, callback); - } - - /** - * @hide - */ - @Nullable - private VirtualDisplay createVirtualDisplayInternal( @NonNull VirtualDisplayConfig config, @Nullable @CallbackExecutor Executor executor, @Nullable VirtualDisplay.Callback callback) { @@ -897,16 +857,6 @@ public final class VirtualDeviceManager { } } - /** - * Returns the display flags that should be added to a particular virtual display. - * Additional device-level flags from {@link - * com.android.server.companion.virtual.VirtualDeviceImpl#getBaseVirtualDisplayFlags()} will - * be added by DisplayManagerService. - */ - private int getVirtualDisplayFlags(int flags) { - return DEFAULT_VIRTUAL_DISPLAY_FLAGS | flags; - } - private String getVirtualDisplayName() { try { // Currently this just use the device ID, which means all of the virtual displays diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index d8076b5c0fd7..9f3b60148004 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -35,6 +35,7 @@ import android.companion.virtual.sensor.VirtualSensorConfig; import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; +import android.os.SharedMemory; import android.os.UserHandle; import android.util.ArraySet; import android.util.SparseArray; @@ -577,6 +578,25 @@ public final class VirtualDeviceParams implements Parcelable { mExecutor.execute(() -> mCallback.onConfigurationChanged( sensor, enabled, samplingPeriod, batchReportingLatency)); } + + @Override + public void onDirectChannelCreated(int channelHandle, + @NonNull SharedMemory sharedMemory) { + mExecutor.execute( + () -> mCallback.onDirectChannelCreated(channelHandle, sharedMemory)); + } + + @Override + public void onDirectChannelDestroyed(int channelHandle) { + mExecutor.execute(() -> mCallback.onDirectChannelDestroyed(channelHandle)); + } + + @Override + public void onDirectChannelConfigured(int channelHandle, @NonNull VirtualSensor sensor, + int rateLevel, int reportToken) { + mExecutor.execute(() -> mCallback.onDirectChannelConfigured( + channelHandle, sensor, rateLevel, reportToken)); + } } /** diff --git a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl index 7da9c3224400..3cb0572f3350 100644 --- a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl +++ b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl @@ -17,6 +17,7 @@ package android.companion.virtual.sensor; import android.companion.virtual.sensor.VirtualSensor; +import android.os.SharedMemory; /** * Interface for notifying the sensor owner about whether and how sensor events should be injected. @@ -36,4 +37,31 @@ oneway interface IVirtualSensorCallback { */ void onConfigurationChanged(in VirtualSensor sensor, boolean enabled, int samplingPeriodMicros, int batchReportLatencyMicros); + + /** + * Called when a sensor direct channel is created. + * + * @param channelHandle Identifier of the channel that was created. + * @param sharedMemory The shared memory region for the direct sensor channel. + */ + void onDirectChannelCreated(int channelHandle, in SharedMemory sharedMemory); + + /** + * Called when a sensor direct channel is destroyed. + * + * @param channelHandle Identifier of the channel that was destroyed. + */ + void onDirectChannelDestroyed(int channelHandle); + + /** + * Called when a sensor direct channel is configured. + * + * @param channelHandle Identifier of the channel that was configured. + * @param sensor The sensor, for which the channel was configured. + * @param rateLevel The rate level used to configure the direct sensor channel. + * @param reportToken A positive sensor report token, used to differentiate between events from + * different sensors within the same channel. + */ + void onDirectChannelConfigured(int channelHandle, in VirtualSensor sensor, int rateLevel, + int reportToken); } diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java index e09718941302..f7af283a749b 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java @@ -17,8 +17,13 @@ package android.companion.virtual.sensor; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.hardware.Sensor; +import android.hardware.SensorDirectChannel; +import android.os.MemoryFile; +import android.os.SharedMemory; import java.time.Duration; @@ -50,4 +55,74 @@ public interface VirtualSensorCallback { */ void onConfigurationChanged(@NonNull VirtualSensor sensor, boolean enabled, @NonNull Duration samplingPeriod, @NonNull Duration batchReportLatency); + + /** + * Called when a {@link android.hardware.SensorDirectChannel} is created. + * + * <p>The {@link android.hardware.SensorManager} instance used to create the direct channel must + * be associated with the virtual device. + * + * <p>A typical order of callback invocations is: + * <ul> + * <li>{@code onDirectChannelCreated} - the channel handle and the associated shared memory + * should be stored by the virtual device</li> + * <li>{@code onDirectChannelConfigured} with a positive {@code rateLevel} - the virtual + * device should start writing to the shared memory for the associated channel with the + * requested parameters.</li> + * <li>{@code onDirectChannelConfigured} with a {@code rateLevel = RATE_STOP} - the virtual + * device should stop writing to the shared memory for the associated channel.</li> + * <li>{@code onDirectChannelDestroyed} - the shared memory associated with the channel + * handle should be closed.</li> + * </ul> + * + * @param channelHandle Identifier of the newly created channel. + * @param sharedMemory writable shared memory region. + * + * @see android.hardware.SensorManager#createDirectChannel(MemoryFile) + * @see #onDirectChannelConfigured + * @see #onDirectChannelDestroyed + */ + default void onDirectChannelCreated(@IntRange(from = 1) int channelHandle, + @NonNull SharedMemory sharedMemory) {} + + /** + * Called when a {@link android.hardware.SensorDirectChannel} is destroyed. + * + * <p>The virtual device must perform any clean-up and close the shared memory that was + * received with the {@link #onDirectChannelCreated} callback and the corresponding + * {@code channelHandle}. + * + * @param channelHandle Identifier of the channel that was destroyed. + * + * @see SensorDirectChannel#close() + */ + default void onDirectChannelDestroyed(@IntRange(from = 1) int channelHandle) {} + + /** + * Called when a {@link android.hardware.SensorDirectChannel} is configured. + * + * <p>Sensor events for the corresponding sensor should be written at the indicated rate to the + * shared memory region that was received with the {@link #onDirectChannelCreated} callback and + * the corresponding {@code channelHandle}. The events should be written in the correct format + * and with the provided {@code reportToken} until the channel is reconfigured with + * {@link SensorDirectChannel#RATE_STOP}. + * + * <p>The sensor must support direct channel in order for this callback to be invoked. Only + * {@link MemoryFile} sensor direct channels are supported for virtual sensors. + * + * @param channelHandle Identifier of the channel that was configured. + * @param sensor The sensor, for which the channel was configured. + * @param rateLevel The rate level used to configure the direct sensor channel. + * @param reportToken A positive sensor report token, used to differentiate between events from + * different sensors within the same channel. + * + * @see VirtualSensorConfig.Builder#setHighestDirectReportRateLevel(int) + * @see VirtualSensorConfig.Builder#setDirectChannelTypesSupported(int) + * @see android.hardware.SensorManager#createDirectChannel(MemoryFile) + * @see #onDirectChannelCreated + * @see SensorDirectChannel#configure(Sensor, int) + */ + default void onDirectChannelConfigured(@IntRange(from = 1) int channelHandle, + @NonNull VirtualSensor sensor, @SensorDirectChannel.RateLevel int rateLevel, + @IntRange(from = 1) int reportToken) {} } diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java index 6d45365ebbd4..ffbdff8c2e3b 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java @@ -21,11 +21,13 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.hardware.Sensor; +import android.hardware.SensorDirectChannel; import android.os.Parcel; import android.os.Parcelable; import java.util.Objects; + /** * Configuration for creation of a virtual sensor. * @see VirtualSensor @@ -33,6 +35,14 @@ import java.util.Objects; */ @SystemApi public final class VirtualSensorConfig implements Parcelable { + private static final String TAG = "VirtualSensorConfig"; + + // Mask for direct mode highest rate level, bit 7, 8, 9. + private static final int DIRECT_REPORT_MASK = 0x380; + private static final int DIRECT_REPORT_SHIFT = 7; + + // Mask for supported direct channel, bit 10, 11 + private static final int DIRECT_CHANNEL_SHIFT = 10; private final int mType; @NonNull @@ -40,16 +50,21 @@ public final class VirtualSensorConfig implements Parcelable { @Nullable private final String mVendor; - private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor) { + private final int mFlags; + + private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor, + int flags) { mType = type; mName = name; mVendor = vendor; + mFlags = flags; } private VirtualSensorConfig(@NonNull Parcel parcel) { mType = parcel.readInt(); mName = parcel.readString8(); mVendor = parcel.readString8(); + mFlags = parcel.readInt(); } @Override @@ -62,6 +77,7 @@ public final class VirtualSensorConfig implements Parcelable { parcel.writeInt(mType); parcel.writeString8(mName); parcel.writeString8(mVendor); + parcel.writeInt(mFlags); } /** @@ -92,22 +108,64 @@ public final class VirtualSensorConfig implements Parcelable { } /** + * Returns the highest supported direct report mode rate level of the sensor. + * + * @see Sensor#getHighestDirectReportRateLevel() + */ + @SensorDirectChannel.RateLevel + public int getHighestDirectReportRateLevel() { + int rateLevel = ((mFlags & DIRECT_REPORT_MASK) >> DIRECT_REPORT_SHIFT); + return rateLevel <= SensorDirectChannel.RATE_VERY_FAST + ? rateLevel : SensorDirectChannel.RATE_VERY_FAST; + } + + /** + * Returns a combination of all supported direct channel types. + * + * @see Builder#setDirectChannelTypesSupported(int) + * @see Sensor#isDirectChannelTypeSupported(int) + */ + public @SensorDirectChannel.MemoryType int getDirectChannelTypesSupported() { + int memoryTypes = 0; + if ((mFlags & (1 << DIRECT_CHANNEL_SHIFT)) > 0) { + memoryTypes |= SensorDirectChannel.TYPE_MEMORY_FILE; + } + if ((mFlags & (1 << (DIRECT_CHANNEL_SHIFT + 1))) > 0) { + memoryTypes |= SensorDirectChannel.TYPE_HARDWARE_BUFFER; + } + return memoryTypes; + } + + /** + * Returns the sensor flags. + * @hide + */ + public int getFlags() { + return mFlags; + } + + /** * Builder for {@link VirtualSensorConfig}. */ public static final class Builder { + private static final int FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED = + 1 << DIRECT_CHANNEL_SHIFT; private final int mType; @NonNull private final String mName; @Nullable private String mVendor; + private int mFlags; + @SensorDirectChannel.RateLevel + int mHighestDirectReportRateLevel; /** * Creates a new builder. * * @param type The type of the sensor, matching {@link Sensor#getType}. * @param name The name of the sensor. Must be unique among all sensors with the same type - * that belong to the same virtual device. + * that belong to the same virtual device. */ public Builder(int type, @NonNull String name) { mType = type; @@ -119,7 +177,19 @@ public final class VirtualSensorConfig implements Parcelable { */ @NonNull public VirtualSensorConfig build() { - return new VirtualSensorConfig(mType, mName, mVendor); + if (mHighestDirectReportRateLevel > 0) { + if ((mFlags & FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED) == 0) { + throw new IllegalArgumentException("Setting direct channel type is required " + + "for sensors with direct channel support."); + } + mFlags |= mHighestDirectReportRateLevel << DIRECT_REPORT_SHIFT; + } + if ((mFlags & FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED) > 0 + && mHighestDirectReportRateLevel == 0) { + throw new IllegalArgumentException("Highest direct report rate level is " + + "required for sensors with direct channel support."); + } + return new VirtualSensorConfig(mType, mName, mVendor, mFlags); } /** @@ -130,6 +200,44 @@ public final class VirtualSensorConfig implements Parcelable { mVendor = vendor; return this; } + + /** + * Sets the highest supported rate level for direct sensor report. + * + * @see VirtualSensorConfig#getHighestDirectReportRateLevel() + */ + @NonNull + public VirtualSensorConfig.Builder setHighestDirectReportRateLevel( + @SensorDirectChannel.RateLevel int rateLevel) { + mHighestDirectReportRateLevel = rateLevel; + return this; + } + + /** + * Sets whether direct sensor channel of the given types is supported. + * + * @param memoryTypes A combination of {@link SensorDirectChannel.MemoryType} flags + * indicating the types of shared memory supported for creating direct channels. Only + * {@link SensorDirectChannel#TYPE_MEMORY_FILE} direct channels may be supported for virtual + * sensors. + * @throws IllegalArgumentException if {@link SensorDirectChannel#TYPE_HARDWARE_BUFFER} is + * set to be supported. + */ + @NonNull + public VirtualSensorConfig.Builder setDirectChannelTypesSupported( + @SensorDirectChannel.MemoryType int memoryTypes) { + if ((memoryTypes & SensorDirectChannel.TYPE_MEMORY_FILE) > 0) { + mFlags |= FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED; + } else { + mFlags &= ~FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED; + } + if ((memoryTypes & ~SensorDirectChannel.TYPE_MEMORY_FILE) > 0) { + throw new IllegalArgumentException( + "Only TYPE_MEMORY_FILE direct channels can be supported for virtual " + + "sensors."); + } + return this; + } } @NonNull diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 795c77ff5105..d3502c5254c8 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -389,6 +389,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall @Override public void getTypeAnonymousAsync(Uri uri, RemoteCallback callback) { + uri = validateIncomingUri(uri); + uri = maybeGetUriWithoutUserId(uri); final Bundle result = new Bundle(); try { result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getTypeAnonymous(uri)); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 658702f2b1cc..d0accb72a0e1 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5703,6 +5703,9 @@ public abstract class Context { * Use with {@link #getSystemService(String)} to retrieve a * {@link android.companion.virtual.VirtualDeviceManager} for managing virtual devices. * + * On devices without {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} + * system feature the {@link #getSystemService(String)} will return {@code null}. + * * @see #getSystemService(String) * @see android.companion.virtual.VirtualDeviceManager */ diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index bd3cf5f0e6bc..afc2285c62c3 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -20,9 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.app.compat.CompatChanges; -import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.net.Uri; import android.os.Build; @@ -185,28 +182,6 @@ public class IntentFilter implements Parcelable { private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0]; /** - * An intent with action set as null used to always pass the action test during intent - * filter matching. This causes a lot of confusion and unexpected intent matches. - * Null action intents should be blocked when either the intent sender or receiver - * application targets U or higher. - * - * mBlockNullAction indicates whether the intent filter owner (intent receiver) is - * targeting U+. This value will be properly set by package manager when IntentFilters are - * passed to an application, so that when an application is trying to perform intent filter - * matching locally, the correct matching algorithm will be chosen. - * - * When an IntentFilter is sent to system server (e.g. for registering runtime receivers), - * the value set in mBlockNullAction will be ignored and overwritten with the correct - * value evaluated based on the Binder calling identity. This makes sure that the - * security enforcement cannot be bypassed by crafting a malicious IntentFilter. - * - * @hide - */ - @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) - public static final long BLOCK_NULL_ACTION_INTENTS = 264497795; - - /** * The filter {@link #setPriority} value at which system high-priority * receivers are placed; that is, receivers that should execute before * application code. Applications should never use filters with this or @@ -2301,7 +2276,6 @@ public class IntentFilter implements Parcelable { String type = resolve ? intent.resolveType(resolver) : intent.getType(); return match(intent.getAction(), type, intent.getScheme(), intent.getData(), intent.getCategories(), logTag, - CompatChanges.isChangeEnabled(BLOCK_NULL_ACTION_INTENTS), false /* supportWildcards */, null /* ignoreActions */, intent.getExtras()); } @@ -2354,7 +2328,6 @@ public class IntentFilter implements Parcelable { Uri data, Set<String> categories, String logTag, boolean supportWildcards, @Nullable Collection<String> ignoreActions) { return match(action, type, scheme, data, categories, logTag, supportWildcards, - CompatChanges.isChangeEnabled(BLOCK_NULL_ACTION_INTENTS), ignoreActions, null /* extras */); } @@ -2366,10 +2339,8 @@ public class IntentFilter implements Parcelable { */ public final int match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag, boolean supportWildcards, - boolean blockNullAction, @Nullable Collection<String> ignoreActions, - @Nullable Bundle extras) { - if ((action == null && blockNullAction) - || !matchAction(action, supportWildcards, ignoreActions)) { + @Nullable Collection<String> ignoreActions, @Nullable Bundle extras) { + if (action != null && !matchAction(action, supportWildcards, ignoreActions)) { if (false) Log.v( logTag, "No matching action " + action + " for " + this); return NO_MATCH_ACTION; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index d927c5e37a80..9388823b1a9c 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5720,17 +5720,6 @@ public abstract class PackageManager { } /** - * @hide - */ - @NonNull - public ActivityInfo getActivityInfoAsUser(@NonNull ComponentName component, - @NonNull ComponentInfoFlags flags, @UserIdInt int userId) - throws NameNotFoundException { - throw new UnsupportedOperationException( - "getActivityInfoAsUser not implemented in subclass"); - } - - /** * Retrieve all of the information we know about a particular receiver * class. * diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING index 45e0efb46616..f440ac7ecbb2 100644 --- a/core/java/android/content/pm/TEST_MAPPING +++ b/core/java/android/content/pm/TEST_MAPPING @@ -29,6 +29,9 @@ }, { "path": "system/apex/tests" + }, + { + "path": "cts/tests/tests/content/pm/SecureFrp" } ], "presubmit": [ @@ -141,17 +144,6 @@ ] }, { - "name": "CtsSecureFrpInstallTestCases", - "options": [ - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "exclude-annotation": "org.junit.Ignore" - } - ] - }, - { "name": "CtsSuspendAppsPermissionTestCases", "options": [ { diff --git a/core/java/android/content/pm/UserPackage.java b/core/java/android/content/pm/UserPackage.java index 7ca92c3d4777..c35e67801b71 100644 --- a/core/java/android/content/pm/UserPackage.java +++ b/core/java/android/content/pm/UserPackage.java @@ -18,14 +18,16 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.UserIdInt; -import android.os.Process; -import android.os.UserHandle; import android.util.SparseArrayMap; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import libcore.util.EmptyArray; + import java.util.Objects; +import java.util.Random; /** * POJO to represent a package for a specific user ID. @@ -34,6 +36,16 @@ import java.util.Objects; */ public final class UserPackage { private static final boolean ENABLE_CACHING = true; + /** + * The maximum number of entries to keep in the cache per user ID. + * The value should ideally be high enough to cover all packages on an end-user device, + * but low enough that stale or invalid packages would eventually (probably) get removed. + * This should benefit components that loop through all packages on a device and use this class, + * since being able to cache the objects for all packages on the device + * means we don't have to keep recreating the objects. + */ + @VisibleForTesting + static final int MAX_NUM_CACHED_ENTRIES_PER_USER = 1000; @UserIdInt public final int userId; @@ -43,11 +55,13 @@ public final class UserPackage { @GuardedBy("sCacheLock") private static final SparseArrayMap<String, UserPackage> sCache = new SparseArrayMap<>(); - private static final class NoPreloadHolder { - /** Set of userIDs to cache objects for. */ - @GuardedBy("sCacheLock") - private static int[] sUserIds = new int[]{UserHandle.getUserId(Process.myUid())}; - } + /** + * Set of userIDs to cache objects for. We start off with an empty set, so there's no caching + * by default. The system will override with a valid set of userIDs in its process so that + * caching becomes active in the system process. + */ + @GuardedBy("sCacheLock") + private static int[] sUserIds = EmptyArray.INT; private UserPackage(int userId, String packageName) { this.userId = userId; @@ -87,13 +101,14 @@ public final class UserPackage { } synchronized (sCacheLock) { - if (!ArrayUtils.contains(NoPreloadHolder.sUserIds, userId)) { + if (!ArrayUtils.contains(sUserIds, userId)) { // Don't cache objects for invalid userIds. return new UserPackage(userId, packageName); } UserPackage up = sCache.get(userId, packageName); if (up == null) { + maybePurgeRandomEntriesLocked(userId); packageName = packageName.intern(); up = new UserPackage(userId, packageName); sCache.add(userId, packageName, up); @@ -121,7 +136,7 @@ public final class UserPackage { userIds = userIds.clone(); synchronized (sCacheLock) { - NoPreloadHolder.sUserIds = userIds; + sUserIds = userIds; for (int u = sCache.numMaps() - 1; u >= 0; --u) { final int userId = sCache.keyAt(u); @@ -131,4 +146,35 @@ public final class UserPackage { } } } + + @VisibleForTesting + public static int numEntriesForUser(int userId) { + synchronized (sCacheLock) { + return sCache.numElementsForKey(userId); + } + } + + /** Purge a random set of entries if the cache size is too large. */ + @GuardedBy("sCacheLock") + private static void maybePurgeRandomEntriesLocked(int userId) { + final int uIdx = sCache.indexOfKey(userId); + if (uIdx < 0) { + return; + } + int numCached = sCache.numElementsForKeyAt(uIdx); + if (numCached < MAX_NUM_CACHED_ENTRIES_PER_USER) { + return; + } + // Purge a random set of 1% of cached elements for the userId. We don't want to use a + // deterministic system of purging because that may cause us to repeatedly remove elements + // that are frequently added and queried more than others. Choosing a random set + // means we will probably eventually remove less useful elements. + // An LRU cache is too expensive for this commonly used utility class. + final Random rand = new Random(); + final int numToPurge = Math.max(1, MAX_NUM_CACHED_ENTRIES_PER_USER / 100); + for (int i = 0; i < numToPurge && numCached > 0; ++i) { + final int removeIdx = rand.nextInt(numCached--); + sCache.deleteAt(uIdx, removeIdx); + } + } } diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java index ffdc7b3807cf..6b09c303e3cd 100644 --- a/core/java/android/content/res/FontScaleConverterFactory.java +++ b/core/java/android/content/res/FontScaleConverterFactory.java @@ -18,6 +18,7 @@ package android.content.res; import android.annotation.NonNull; import android.annotation.Nullable; +import android.util.MathUtils; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; @@ -98,22 +99,81 @@ public class FontScaleConverterFactory { public static FontScaleConverter forScale(float fontScale) { if (fontScale <= 1) { // We don't need non-linear curves for shrinking text or for 100%. - // Also, fontScale==0 should not have a curve either + // Also, fontScale==0 should not have a curve either. + // And ignore negative font scales; that's just silly. return null; } FontScaleConverter lookupTable = get(fontScale); - // TODO(b/247861716): interpolate between two tables when null + if (lookupTable != null) { + return lookupTable; + } + + // Didn't find an exact match: interpolate between two existing tables + final int index = LOOKUP_TABLES.indexOfKey(getKey(fontScale)); + if (index >= 0) { + // This should never happen, should have been covered by get() above. + return LOOKUP_TABLES.valueAt(index); + } + // Didn't find an exact match: interpolate between two existing tables + final int lowerIndex = -(index + 1) - 1; + final int higherIndex = lowerIndex + 1; + if (lowerIndex < 0 || higherIndex >= LOOKUP_TABLES.size()) { + // We have gone beyond our bounds and have nothing to interpolate between. Just give + // them a straight linear table instead. + // This works because when FontScaleConverter encounters a size beyond its bounds, it + // calculates a linear fontScale factor using the ratio of the last element pair. + return new FontScaleConverter(new float[] {1f}, new float[] {fontScale}); + } else { + float startScale = getScaleFromKey(LOOKUP_TABLES.keyAt(lowerIndex)); + float endScale = getScaleFromKey(LOOKUP_TABLES.keyAt(higherIndex)); + float interpolationPoint = MathUtils.constrainedMap( + /* rangeMin= */ 0f, + /* rangeMax= */ 1f, + startScale, + endScale, + fontScale + ); + return createInterpolatedTableBetween( + LOOKUP_TABLES.valueAt(lowerIndex), + LOOKUP_TABLES.valueAt(higherIndex), + interpolationPoint); + } + } + + @NonNull + private static FontScaleConverter createInterpolatedTableBetween( + FontScaleConverter start, + FontScaleConverter end, + float interpolationPoint + ) { + float[] commonSpSizes = new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100f}; + float[] dpInterpolated = new float[commonSpSizes.length]; + + for (int i = 0; i < commonSpSizes.length; i++) { + float sp = commonSpSizes[i]; + float startDp = start.convertSpToDp(sp); + float endDp = end.convertSpToDp(sp); + dpInterpolated[i] = MathUtils.lerp(startDp, endDp, interpolationPoint); + } + + return new FontScaleConverter(commonSpSizes, dpInterpolated); + } + + private static int getKey(float fontScale) { + return (int) (fontScale * SCALE_KEY_MULTIPLIER); + } - return lookupTable; + private static float getScaleFromKey(int key) { + return (float) key / SCALE_KEY_MULTIPLIER; } private static void put(float scaleKey, @NonNull FontScaleConverter fontScaleConverter) { - LOOKUP_TABLES.put((int) (scaleKey * SCALE_KEY_MULTIPLIER), fontScaleConverter); + LOOKUP_TABLES.put(getKey(scaleKey), fontScaleConverter); } @Nullable private static FontScaleConverter get(float scaleKey) { - return LOOKUP_TABLES.get((int) (scaleKey * SCALE_KEY_MULTIPLIER)); + return LOOKUP_TABLES.get(getKey(scaleKey)); } } diff --git a/core/java/android/credentials/CreateCredentialRequest.java b/core/java/android/credentials/CreateCredentialRequest.java index b756a4323842..c89a5c62cd58 100644 --- a/core/java/android/credentials/CreateCredentialRequest.java +++ b/core/java/android/credentials/CreateCredentialRequest.java @@ -244,7 +244,7 @@ public final class CreateCredentialRequest implements Parcelable { /** A builder for {@link CreateCredentialRequest}. */ public static final class Builder { - private boolean mAlwaysSendAppInfoToProvider; + private boolean mAlwaysSendAppInfoToProvider = true; @NonNull private String mType; diff --git a/core/java/android/credentials/ui/CancelUiRequest.java b/core/java/android/credentials/ui/CancelUiRequest.java new file mode 100644 index 000000000000..6bd9de481a79 --- /dev/null +++ b/core/java/android/credentials/ui/CancelUiRequest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.credentials.ui; + +import android.annotation.NonNull; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.AnnotationValidations; + +/** + * A request to cancel any ongoing UI matching this request. + * + * @hide + */ +public final class CancelUiRequest implements Parcelable { + + /** + * The intent extra key for the {@code CancelUiRequest} object when launching the UX + * activities. + */ + @NonNull public static final String EXTRA_CANCEL_UI_REQUEST = + "android.credentials.ui.extra.EXTRA_CANCEL_UI_REQUEST"; + + @NonNull + private final IBinder mToken; + + /** Returns the request token matching the user request that should be cancelled. */ + @NonNull + public IBinder getToken() { + return mToken; + } + + public CancelUiRequest(@NonNull IBinder token) { + mToken = token; + } + + private CancelUiRequest(@NonNull Parcel in) { + mToken = in.readStrongBinder(); + AnnotationValidations.validate(NonNull.class, null, mToken); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeStrongBinder(mToken); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull public static final Creator<CancelUiRequest> CREATOR = new Creator<>() { + @Override + public CancelUiRequest createFromParcel(@NonNull Parcel in) { + return new CancelUiRequest(in); + } + + @Override + public CancelUiRequest[] newArray(int size) { + return new CancelUiRequest[size]; + } + }; +} diff --git a/core/java/android/credentials/ui/IntentFactory.java b/core/java/android/credentials/ui/IntentFactory.java index 3c10e8152efe..dcfef56f86a4 100644 --- a/core/java/android/credentials/ui/IntentFactory.java +++ b/core/java/android/credentials/ui/IntentFactory.java @@ -22,6 +22,7 @@ import android.annotation.TestApi; import android.content.ComponentName; import android.content.Intent; import android.content.res.Resources; +import android.os.IBinder; import android.os.Parcel; import android.os.ResultReceiver; @@ -66,6 +67,25 @@ public class IntentFactory { } /** + * Creates an Intent that cancels any UI matching the given request token id. + * + * @hide + */ + @NonNull + public static Intent createCancelUiIntent(@NonNull IBinder requestToken) { + Intent intent = new Intent(); + ComponentName componentName = + ComponentName.unflattenFromString( + Resources.getSystem() + .getString( + com.android.internal.R.string + .config_credentialManagerDialogComponent)); + intent.setComponent(componentName); + intent.putExtra(CancelUiRequest.EXTRA_CANCEL_UI_REQUEST, new CancelUiRequest(requestToken)); + return intent; + } + + /** * Notify the UI that providers have been enabled/disabled. * * @hide diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 1a3e0b0d9c5a..73157e62cb56 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -92,7 +92,8 @@ public class SystemSensorManager extends SensorManager { private static native boolean nativeIsDataInjectionEnabled(long nativeInstance); private static native int nativeCreateDirectChannel( - long nativeInstance, long size, int channelType, int fd, HardwareBuffer buffer); + long nativeInstance, int deviceId, long size, int channelType, int fd, + HardwareBuffer buffer); private static native void nativeDestroyDirectChannel( long nativeInstance, int channelHandle); private static native int nativeConfigDirectChannel( @@ -695,6 +696,10 @@ public class SystemSensorManager extends SensorManager { /** @hide */ protected SensorDirectChannel createDirectChannelImpl( MemoryFile memoryFile, HardwareBuffer hardwareBuffer) { + int deviceId = mContext.getDeviceId(); + if (isDeviceSensorPolicyDefault(deviceId)) { + deviceId = DEVICE_ID_DEFAULT; + } int id; int type; long size; @@ -713,8 +718,8 @@ public class SystemSensorManager extends SensorManager { } size = memoryFile.length(); - id = nativeCreateDirectChannel( - mNativeInstance, size, SensorDirectChannel.TYPE_MEMORY_FILE, fd, null); + id = nativeCreateDirectChannel(mNativeInstance, deviceId, size, + SensorDirectChannel.TYPE_MEMORY_FILE, fd, null); if (id <= 0) { throw new UncheckedIOException( new IOException("create MemoryFile direct channel failed " + id)); @@ -738,7 +743,7 @@ public class SystemSensorManager extends SensorManager { } size = hardwareBuffer.getWidth(); id = nativeCreateDirectChannel( - mNativeInstance, size, SensorDirectChannel.TYPE_HARDWARE_BUFFER, + mNativeInstance, deviceId, size, SensorDirectChannel.TYPE_HARDWARE_BUFFER, -1, hardwareBuffer); if (id <= 0) { throw new UncheckedIOException( diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index b766cd19cdb0..50dd7a0bc1be 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -989,24 +989,6 @@ public final class DisplayManager { /** * Creates a virtual display. - * - * @see #createVirtualDisplay(String, int, int, int, float, Surface, int, - * Handler, VirtualDisplay.Callback) - */ - @Nullable - public VirtualDisplay createVirtualDisplay(@NonNull String name, - @IntRange(from = 1) int width, - @IntRange(from = 1) int height, - @IntRange(from = 1) int densityDpi, - float requestedRefreshRate, - @Nullable Surface surface, - @VirtualDisplayFlag int flags) { - return createVirtualDisplay(name, width, height, densityDpi, requestedRefreshRate, - surface, flags, null, null); - } - - /** - * Creates a virtual display. * <p> * The content of a virtual display is rendered to a {@link Surface} provided * by the application. @@ -1056,8 +1038,23 @@ public final class DisplayManager { @VirtualDisplayFlag int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { - return createVirtualDisplay(name, width, height, densityDpi, 0.0f, surface, - flags, handler, callback); + final VirtualDisplayConfig.Builder builder = + new VirtualDisplayConfig.Builder(name, width, height, densityDpi); + builder.setFlags(flags); + if (surface != null) { + builder.setSurface(surface); + } + return createVirtualDisplay(builder.build(), handler, callback); + } + + /** + * Creates a virtual display. + * + * @see #createVirtualDisplay(VirtualDisplayConfig, Handler, VirtualDisplay.Callback) + */ + @Nullable + public VirtualDisplay createVirtualDisplay(@NonNull VirtualDisplayConfig config) { + return createVirtualDisplay(config, /*handler=*/null, /*callback=*/null); } /** @@ -1084,21 +1081,7 @@ public final class DisplayManager { * turning off the screen. * </p> * - * @param name The name of the virtual display, must be non-empty. - * @param width The width of the virtual display in pixels, must be greater than 0. - * @param height The height of the virtual display in pixels, must be greater than 0. - * @param densityDpi The density of the virtual display in dpi, must be greater than 0. - * @param requestedRefreshRate The requested refresh rate in frames per second. - * For best results, specify a divisor of the physical refresh rate, e.g., 30 or 60 on - * 120hz display. If an arbitrary refresh rate is specified, the rate will be rounded - * up or down to a divisor of the physical display. If 0 is specified, the virtual - * display is refreshed at the physical display refresh rate. - * @param surface The surface to which the content of the virtual display should - * be rendered, or null if there is none initially. - * @param flags A combination of virtual display flags: - * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION}, - * {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, - * or {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. + * @param config The configuration of the virtual display, must be non-null. * @param handler The handler on which the listener should be invoked, or null * if the listener should be invoked on the calling thread's looper. * @param callback Callback to call when the state of the {@link VirtualDisplay} changes @@ -1106,33 +1089,14 @@ public final class DisplayManager { * not create the virtual display. * * @throws SecurityException if the caller does not have permission to create - * a virtual display with the specified flags. + * a virtual display with flags specified in the configuration. */ @Nullable - public VirtualDisplay createVirtualDisplay(@NonNull String name, - @IntRange(from = 1) int width, - @IntRange(from = 1) int height, - @IntRange(from = 1) int densityDpi, - float requestedRefreshRate, - @Nullable Surface surface, - @VirtualDisplayFlag int flags, + public VirtualDisplay createVirtualDisplay( + @NonNull VirtualDisplayConfig config, @Nullable Handler handler, @Nullable VirtualDisplay.Callback callback) { - if (!ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE && requestedRefreshRate != 0.0f) { - Slog.e(TAG, "Please turn on ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE to use the new api"); - return null; - } - - final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width, - height, densityDpi); - builder.setFlags(flags); - if (surface != null) { - builder.setSurface(surface); - } - if (requestedRefreshRate != 0.0f) { - builder.setRequestedRefreshRate(requestedRefreshRate); - } - return createVirtualDisplay(null /* projection */, builder.build(), callback, handler, + return createVirtualDisplay(null /* projection */, config, callback, handler, null /* windowContext */); } diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java index f6a2e33c7164..6b56a067a198 100644 --- a/core/java/android/hardware/display/VirtualDisplayConfig.java +++ b/core/java/android/hardware/display/VirtualDisplayConfig.java @@ -18,121 +18,44 @@ package android.hardware.display; import static android.view.Display.DEFAULT_DISPLAY; +import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.display.DisplayManager.VirtualDisplayFlag; import android.media.projection.MediaProjection; +import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; +import android.view.Display; import android.view.Surface; -import com.android.internal.util.DataClass; - import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Objects; /** - * Holds configuration used to create {@link VirtualDisplay} instances. See - * {@link MediaProjection#createVirtualDisplay} and - * {@link android.companion.virtual.VirtualDeviceManager.VirtualDevice#createVirtualDisplay}. + * Holds configuration used to create {@link VirtualDisplay} instances. * - * @hide + * @see DisplayManager#createVirtualDisplay(VirtualDisplayConfig, Handler, VirtualDisplay.Callback) + * @see MediaProjection#createVirtualDisplay */ -@DataClass(genParcelable = true, genAidl = true, genBuilder = true) public final class VirtualDisplayConfig implements Parcelable { - /** - * The name of the virtual display, must be non-empty. - */ - @NonNull - private String mName; - - /** - * The width of the virtual display in pixels. Must be greater than 0. - */ - @IntRange(from = 1) - private int mWidth; - - /** - * The height of the virtual display in pixels. Must be greater than 0. - */ - @IntRange(from = 1) - private int mHeight; - - /** - * The density of the virtual display in dpi. Must be greater than 0. - */ - @IntRange(from = 1) - private int mDensityDpi; - - /** - * A combination of virtual display flags. - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC}, - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION}, - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE}, - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, - * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. - */ - @VirtualDisplayFlag - private int mFlags = 0; - - /** - * The surface to which the content of the virtual display should be rendered, or null if - * there is none initially. - */ - @Nullable - private Surface mSurface = null; - - /** - * The unique identifier for the display. Shouldn't be displayed to the user. - * @hide - */ - @Nullable - private String mUniqueId = null; - - /** - * The id of the display that the virtual display should mirror, or - * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially. - */ - private int mDisplayIdToMirror = DEFAULT_DISPLAY; - - /** - * Indicates if WindowManager is responsible for mirroring content to this VirtualDisplay, or - * if DisplayManager should record contents instead. - */ - private boolean mWindowManagerMirroring = false; - - /** - * The display categories. If set, only corresponding activities from the same category can be - * shown on the display. - */ - @DataClass.PluralOf("displayCategory") - @NonNull private List<String> mDisplayCategories = new ArrayList<>(); - - /** - * The refresh rate of a virtual display in frames per second. - * If this value is non-zero, this is the requested refresh rate to set. - * If this value is zero, the system chooses a default refresh rate. - */ - private float mRequestedRefreshRate = 0.0f; - - - // Code below generated by codegen v1.0.23. - // - // DO NOT MODIFY! - // CHECKSTYLE:OFF Generated code - // - // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java - // - // To exclude the generated code from IntelliJ auto-formatting enable (one-time): - // Settings > Editor > Code Style > Formatter Control - //@formatter:off - - - @DataClass.Generated.Member - /* package-private */ VirtualDisplayConfig( + private final String mName; + private final int mWidth; + private final int mHeight; + private final int mDensityDpi; + private final int mFlags; + private final Surface mSurface; + private final String mUniqueId; + private final int mDisplayIdToMirror; + private final boolean mWindowManagerMirroring; + private ArrayList<String> mDisplayCategories = null; + private final float mRequestedRefreshRate; + + private VirtualDisplayConfig( @NonNull String name, @IntRange(from = 1) int width, @IntRange(from = 1) int height, @@ -142,219 +65,200 @@ public final class VirtualDisplayConfig implements Parcelable { @Nullable String uniqueId, int displayIdToMirror, boolean windowManagerMirroring, - @NonNull List<String> displayCategories, + @NonNull ArrayList<String> displayCategories, float requestedRefreshRate) { - this.mName = name; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mName); - this.mWidth = width; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mWidth, - "from", 1); - this.mHeight = height; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mHeight, - "from", 1); - this.mDensityDpi = densityDpi; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mDensityDpi, - "from", 1); - this.mFlags = flags; - com.android.internal.util.AnnotationValidations.validate( - VirtualDisplayFlag.class, null, mFlags); - this.mSurface = surface; - this.mUniqueId = uniqueId; - this.mDisplayIdToMirror = displayIdToMirror; - this.mWindowManagerMirroring = windowManagerMirroring; - this.mDisplayCategories = displayCategories; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mDisplayCategories); - this.mRequestedRefreshRate = requestedRefreshRate; - - // onConstructed(); // You can define this method to get a callback + mName = name; + mWidth = width; + mHeight = height; + mDensityDpi = densityDpi; + mFlags = flags; + mSurface = surface; + mUniqueId = uniqueId; + mDisplayIdToMirror = displayIdToMirror; + mWindowManagerMirroring = windowManagerMirroring; + mDisplayCategories = displayCategories; + mRequestedRefreshRate = requestedRefreshRate; } /** - * The name of the virtual display, must be non-empty. + * Returns the name of the virtual display. */ - @DataClass.Generated.Member - public @NonNull String getName() { + @NonNull + public String getName() { return mName; } /** - * The width of the virtual display in pixels. Must be greater than 0. + * Returns the width of the virtual display in pixels. */ - @DataClass.Generated.Member - public @IntRange(from = 1) int getWidth() { + public int getWidth() { return mWidth; } /** - * The height of the virtual display in pixels. Must be greater than 0. + * Returns the height of the virtual display in pixels. */ - @DataClass.Generated.Member - public @IntRange(from = 1) int getHeight() { + public int getHeight() { return mHeight; } /** - * The density of the virtual display in dpi. Must be greater than 0. + * Returns the density of the virtual display in dpi. */ - @DataClass.Generated.Member - public @IntRange(from = 1) int getDensityDpi() { + public int getDensityDpi() { return mDensityDpi; } /** - * A combination of virtual display flags. - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC}, - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION}, - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE}, - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, - * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. + * Returns the virtual display flags. + * + * @see Builder#setFlags */ - @DataClass.Generated.Member - public @VirtualDisplayFlag int getFlags() { + public int getFlags() { return mFlags; } /** - * The surface to which the content of the virtual display should be rendered, or null if - * there is none initially. + * Returns the surface to which the content of the virtual display should be rendered, if any. + * + * @see Builder#setSurface */ - @DataClass.Generated.Member - public @Nullable Surface getSurface() { + @Nullable + public Surface getSurface() { return mSurface; } /** - * The unique identifier for the display. Shouldn't be displayed to the user. - * + * Returns the unique identifier for the display. Shouldn't be displayed to the user. * @hide */ - @DataClass.Generated.Member - public @Nullable String getUniqueId() { + @Nullable + public String getUniqueId() { return mUniqueId; } /** - * The id of the display that the virtual display should mirror, or - * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially. + * Returns the id of the display that the virtual display should mirror, or + * {@link android.view.Display#DEFAULT_DISPLAY} if there is none. + * @hide */ - @DataClass.Generated.Member public int getDisplayIdToMirror() { return mDisplayIdToMirror; } /** - * Indicates if WindowManager is responsible for mirroring content to this VirtualDisplay, or + * Whether if WindowManager is responsible for mirroring content to this VirtualDisplay, or * if DisplayManager should record contents instead. + * @hide */ - @DataClass.Generated.Member public boolean isWindowManagerMirroring() { return mWindowManagerMirroring; } /** - * The display categories. If set, only corresponding activities from the same category can be - * shown on the display. + * Returns the display categories. + * + * @see Builder#setDisplayCategories */ - @DataClass.Generated.Member - public @NonNull List<String> getDisplayCategories() { - return mDisplayCategories; + @NonNull + public List<String> getDisplayCategories() { + return Collections.unmodifiableList(mDisplayCategories); } /** - * The refresh rate of a virtual display in frames per second. - * If this value is none zero, this is the requested refresh rate to set. - * If this value is zero, the system chooses a default refresh rate. + * Returns the refresh rate of a virtual display in frames per second, or zero if it is using a + * default refresh rate chosen by the system. + * + * @see Builder#setRequestedRefreshRate */ - @DataClass.Generated.Member public float getRequestedRefreshRate() { return mRequestedRefreshRate; } @Override - @DataClass.Generated.Member public void writeToParcel(@NonNull Parcel dest, int flags) { - // You can override field parcelling by defining methods like: - // void parcelFieldName(Parcel dest, int flags) { ... } - - int flg = 0; - if (mWindowManagerMirroring) flg |= 0x100; - if (mSurface != null) flg |= 0x20; - if (mUniqueId != null) flg |= 0x40; - dest.writeInt(flg); - dest.writeString(mName); + dest.writeString8(mName); dest.writeInt(mWidth); dest.writeInt(mHeight); dest.writeInt(mDensityDpi); dest.writeInt(mFlags); - if (mSurface != null) dest.writeTypedObject(mSurface, flags); - if (mUniqueId != null) dest.writeString(mUniqueId); + dest.writeTypedObject(mSurface, flags); + dest.writeString8(mUniqueId); dest.writeInt(mDisplayIdToMirror); + dest.writeBoolean(mWindowManagerMirroring); dest.writeStringList(mDisplayCategories); dest.writeFloat(mRequestedRefreshRate); } @Override - @DataClass.Generated.Member public int describeContents() { return 0; } - /** @hide */ - @SuppressWarnings({"unchecked", "RedundantCast"}) - @DataClass.Generated.Member - /* package-private */ VirtualDisplayConfig(@NonNull Parcel in) { - // You can override field unparcelling by defining methods like: - // static FieldType unparcelFieldName(Parcel in) { ... } - - int flg = in.readInt(); - boolean windowManagerMirroring = (flg & 0x100) != 0; - String name = in.readString(); - int width = in.readInt(); - int height = in.readInt(); - int densityDpi = in.readInt(); - int flags = in.readInt(); - Surface surface = (flg & 0x20) == 0 ? null : (Surface) in.readTypedObject(Surface.CREATOR); - String uniqueId = (flg & 0x40) == 0 ? null : in.readString(); - int displayIdToMirror = in.readInt(); - List<String> displayCategories = new ArrayList<>(); - in.readStringList(displayCategories); - float requestedRefreshRate = in.readFloat(); - - this.mName = name; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mName); - this.mWidth = width; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mWidth, - "from", 1); - this.mHeight = height; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mHeight, - "from", 1); - this.mDensityDpi = densityDpi; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mDensityDpi, - "from", 1); - this.mFlags = flags; - com.android.internal.util.AnnotationValidations.validate( - VirtualDisplayFlag.class, null, mFlags); - this.mSurface = surface; - this.mUniqueId = uniqueId; - this.mDisplayIdToMirror = displayIdToMirror; - this.mWindowManagerMirroring = windowManagerMirroring; - this.mDisplayCategories = displayCategories; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mDisplayCategories); - this.mRequestedRefreshRate = requestedRefreshRate; - - // onConstructed(); // You can define this method to get a callback + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof VirtualDisplayConfig)) { + return false; + } + VirtualDisplayConfig that = (VirtualDisplayConfig) o; + return Objects.equals(mName, that.mName) + && mWidth == that.mWidth + && mHeight == that.mHeight + && mDensityDpi == that.mDensityDpi + && mFlags == that.mFlags + && Objects.equals(mSurface, that.mSurface) + && Objects.equals(mUniqueId, that.mUniqueId) + && mDisplayIdToMirror == that.mDisplayIdToMirror + && mWindowManagerMirroring == that.mWindowManagerMirroring + && Objects.equals(mDisplayCategories, that.mDisplayCategories) + && mRequestedRefreshRate == that.mRequestedRefreshRate; + } + + @Override + public int hashCode() { + int hashCode = Objects.hash( + mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId, + mDisplayIdToMirror, mWindowManagerMirroring, mDisplayCategories, + mRequestedRefreshRate); + return hashCode; + } + + @Override + @NonNull + public String toString() { + return "VirtualDisplayConfig(" + + " mName=" + mName + + " mHeight=" + mHeight + + " mWidth=" + mWidth + + " mDensityDpi=" + mDensityDpi + + " mFlags=" + mFlags + + " mSurface=" + mSurface + + " mUniqueId=" + mUniqueId + + " mDisplayIdToMirror=" + mDisplayIdToMirror + + " mWindowManagerMirroring=" + mWindowManagerMirroring + + " mDisplayCategories=" + mDisplayCategories + + " mRequestedRefreshRate=" + mRequestedRefreshRate + + ")"; + } + + private VirtualDisplayConfig(@NonNull Parcel in) { + mName = in.readString8(); + mWidth = in.readInt(); + mHeight = in.readInt(); + mDensityDpi = in.readInt(); + mFlags = in.readInt(); + mSurface = in.readTypedObject(Surface.CREATOR); + mUniqueId = in.readString8(); + mDisplayIdToMirror = in.readInt(); + mWindowManagerMirroring = in.readBoolean(); + mDisplayCategories = new ArrayList<>(); + in.readStringList(mDisplayCategories); + mRequestedRefreshRate = in.readFloat(); } - @DataClass.Generated.Member - public static final @NonNull Parcelable.Creator<VirtualDisplayConfig> CREATOR + @NonNull + public static final Parcelable.Creator<VirtualDisplayConfig> CREATOR = new Parcelable.Creator<VirtualDisplayConfig>() { @Override public VirtualDisplayConfig[] newArray(int size) { @@ -368,229 +272,161 @@ public final class VirtualDisplayConfig implements Parcelable { }; /** - * A builder for {@link VirtualDisplayConfig} + * A builder for {@link VirtualDisplayConfig}. */ - @SuppressWarnings("WeakerAccess") - @DataClass.Generated.Member public static final class Builder { - - private @NonNull String mName; - private @IntRange(from = 1) int mWidth; - private @IntRange(from = 1) int mHeight; - private @IntRange(from = 1) int mDensityDpi; - private @VirtualDisplayFlag int mFlags; - private @Nullable Surface mSurface; - private @Nullable String mUniqueId; - private int mDisplayIdToMirror; - private boolean mWindowManagerMirroring; - private @NonNull List<String> mDisplayCategories; - private float mRequestedRefreshRate; - - private long mBuilderFieldsSet = 0L; + private final String mName; + private final int mWidth; + private final int mHeight; + private final int mDensityDpi; + private int mFlags = 0; + private Surface mSurface = null; + private String mUniqueId = null; + private int mDisplayIdToMirror = DEFAULT_DISPLAY; + private boolean mWindowManagerMirroring = false; + private ArrayList<String> mDisplayCategories = new ArrayList<>(); + private float mRequestedRefreshRate = 0.0f; /** * Creates a new Builder. * - * @param name - * The name of the virtual display, must be non-empty. - * @param width - * The width of the virtual display in pixels. Must be greater than 0. - * @param height - * The height of the virtual display in pixels. Must be greater than 0. - * @param densityDpi - * The density of the virtual display in dpi. Must be greater than 0. + * @param name The name of the virtual display, must be non-empty. + * @param width The width of the virtual display in pixels. Must be greater than 0. + * @param height The height of the virtual display in pixels. Must be greater than 0. + * @param densityDpi The density of the virtual display in dpi. Must be greater than 0. */ public Builder( @NonNull String name, @IntRange(from = 1) int width, @IntRange(from = 1) int height, @IntRange(from = 1) int densityDpi) { + if (name == null) { + throw new IllegalArgumentException("Virtual display name is required"); + } + if (width <= 0) { + throw new IllegalArgumentException("Virtual display width must be positive"); + } + if (height <= 0) { + throw new IllegalArgumentException("Virtual display height must be positive"); + } + if (densityDpi <= 0) { + throw new IllegalArgumentException("Virtual display density must be positive"); + } mName = name; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mName); mWidth = width; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mWidth, - "from", 1); mHeight = height; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mHeight, - "from", 1); mDensityDpi = densityDpi; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mDensityDpi, - "from", 1); - } - - /** - * The name of the virtual display, must be non-empty. - */ - @DataClass.Generated.Member - public @NonNull Builder setName(@NonNull String value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x1; - mName = value; - return this; - } - - /** - * The width of the virtual display in pixels. Must be greater than 0. - */ - @DataClass.Generated.Member - public @NonNull Builder setWidth(@IntRange(from = 1) int value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x2; - mWidth = value; - return this; - } - - /** - * The height of the virtual display in pixels. Must be greater than 0. - */ - @DataClass.Generated.Member - public @NonNull Builder setHeight(@IntRange(from = 1) int value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x4; - mHeight = value; - return this; - } - - /** - * The density of the virtual display in dpi. Must be greater than 0. - */ - @DataClass.Generated.Member - public @NonNull Builder setDensityDpi(@IntRange(from = 1) int value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x8; - mDensityDpi = value; - return this; } /** - * A combination of virtual display flags. + * Sets the virtual display flags, a combination of * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC}, * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION}, * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE}, * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. */ - @DataClass.Generated.Member - public @NonNull Builder setFlags(@VirtualDisplayFlag int value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x10; - mFlags = value; + @NonNull + public Builder setFlags(@VirtualDisplayFlag int flags) { + mFlags = flags; return this; } /** - * The surface to which the content of the virtual display should be rendered, or null if - * there is none initially. + * Sets the surface to which the content of the virtual display should be rendered. + * + * <p>The surface can also be set after the display creation using + * {@link VirtualDisplay#setSurface(Surface)}. */ - @DataClass.Generated.Member - public @NonNull Builder setSurface(@NonNull Surface value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x20; - mSurface = value; + @NonNull + public Builder setSurface(@Nullable Surface surface) { + mSurface = surface; return this; } /** - * The unique identifier for the display. Shouldn't be displayed to the user. - * + * Sets the unique identifier for the display. * @hide */ - @DataClass.Generated.Member - public @NonNull Builder setUniqueId(@NonNull String value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x40; - mUniqueId = value; + @NonNull + public Builder setUniqueId(@Nullable String uniqueId) { + mUniqueId = uniqueId; return this; } /** - * The id of the display that the virtual display should mirror, or - * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially. + * Sets the id of the display that the virtual display should mirror. + * @hide */ - @DataClass.Generated.Member - public @NonNull Builder setDisplayIdToMirror(int value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x80; - mDisplayIdToMirror = value; + @NonNull + public Builder setDisplayIdToMirror(int displayIdToMirror) { + mDisplayIdToMirror = displayIdToMirror; return this; } /** - * Indicates if WindowManager is responsible for mirroring content to this VirtualDisplay, or - * if DisplayManager should record contents instead. + * Sets whether WindowManager is responsible for mirroring content to this VirtualDisplay. + * If unset or false, DisplayManager should record contents instead. + * @hide */ - @DataClass.Generated.Member - public @NonNull Builder setWindowManagerMirroring(boolean value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x100; - mWindowManagerMirroring = value; + @NonNull + public Builder setWindowManagerMirroring(boolean windowManagerMirroring) { + mWindowManagerMirroring = windowManagerMirroring; return this; } /** - * The display categories. If set, only corresponding activities from the same category can be - * shown on the display. + * Sets the display categories. + * + * <p>The categories of the display indicate the type of activities allowed to run on that + * display. Activities can declare a display category using + * {@link android.content.pm.ActivityInfo#requiredDisplayCategory}. */ - @DataClass.Generated.Member - public @NonNull Builder setDisplayCategories(@NonNull List<String> value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x200; - mDisplayCategories = value; + @NonNull + public Builder setDisplayCategories(@NonNull List<String> displayCategories) { + mDisplayCategories.clear(); + mDisplayCategories.addAll(Objects.requireNonNull(displayCategories)); return this; } - /** @see #setDisplayCategories */ - @DataClass.Generated.Member - public @NonNull Builder addDisplayCategory(@NonNull String value) { - if (mDisplayCategories == null) setDisplayCategories(new ArrayList<>()); - mDisplayCategories.add(value); + /** + * Adds a display category. + * + * @see #setDisplayCategories + */ + @NonNull + public Builder addDisplayCategory(@NonNull String displayCategory) { + mDisplayCategories.add(Objects.requireNonNull(displayCategory)); return this; } /** - * The refresh rate of a virtual display in frames per second. - * If this value is none zero, this is the requested refresh rate to set. - * If this value is zero, the system chooses a default refresh rate. + * Sets the refresh rate of a virtual display in frames per second. + * + * <p>For best results, specify a divisor of the physical refresh rate, e.g., 30 or 60 on + * a 120hz display. If an arbitrary refresh rate is specified, the rate will be rounded up + * down to a divisor of the physical display. If unset or zero, the virtual display will be + * refreshed at the physical display refresh rate. + * + * @see Display#getRefreshRate() */ - @DataClass.Generated.Member - public @NonNull Builder setRequestedRefreshRate(float value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x400; - mRequestedRefreshRate = value; + @NonNull + public Builder setRequestedRefreshRate( + @FloatRange(from = 0.0f) float requestedRefreshRate) { + if (requestedRefreshRate < 0.0f) { + throw new IllegalArgumentException( + "Virtual display requested refresh rate must be non-negative"); + } + mRequestedRefreshRate = requestedRefreshRate; return this; } - /** Builds the instance. This builder should not be touched after calling this! */ - public @NonNull VirtualDisplayConfig build() { - checkNotUsed(); - mBuilderFieldsSet |= 0x800; // Mark builder used - - if ((mBuilderFieldsSet & 0x10) == 0) { - mFlags = 0; - } - if ((mBuilderFieldsSet & 0x20) == 0) { - mSurface = null; - } - if ((mBuilderFieldsSet & 0x40) == 0) { - mUniqueId = null; - } - if ((mBuilderFieldsSet & 0x80) == 0) { - mDisplayIdToMirror = DEFAULT_DISPLAY; - } - if ((mBuilderFieldsSet & 0x100) == 0) { - mWindowManagerMirroring = false; - } - if ((mBuilderFieldsSet & 0x200) == 0) { - mDisplayCategories = new ArrayList<>(); - } - if ((mBuilderFieldsSet & 0x400) == 0) { - mRequestedRefreshRate = 0.0f; - } - VirtualDisplayConfig o = new VirtualDisplayConfig( + /** + * Builds the {@link VirtualDisplayConfig} instance. + */ + @NonNull + public VirtualDisplayConfig build() { + return new VirtualDisplayConfig( mName, mWidth, mHeight, @@ -602,27 +438,6 @@ public final class VirtualDisplayConfig implements Parcelable { mWindowManagerMirroring, mDisplayCategories, mRequestedRefreshRate); - return o; - } - - private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x800) != 0) { - throw new IllegalStateException( - "This Builder should not be reused. Use a new Builder instance instead"); - } } } - - @DataClass.Generated( - time = 1671047069703L, - codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java", - inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate @android.hardware.display.DisplayManager.VirtualDisplayFlag int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate int mDisplayIdToMirror\nprivate boolean mWindowManagerMirroring\nprivate @com.android.internal.util.DataClass.PluralOf(\"displayCategory\") @android.annotation.NonNull java.util.List<java.lang.String> mDisplayCategories\nprivate float mRequestedRefreshRate\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)") - @Deprecated - private void __metadata() {} - - - //@formatter:on - // End of generated code - } diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 09540132fe0d..793a70e93f56 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -660,7 +660,7 @@ public final class ApduServiceInfo implements Parcelable { * @param proto the ProtoOutputStream to write to */ public void dumpDebug(ProtoOutputStream proto) { - getComponent().dumpDebug(proto, ApduServiceInfoProto.COMPONENT_NAME); + Utils.dumpDebugComponentName(getComponent(), proto, ApduServiceInfoProto.COMPONENT_NAME); proto.write(ApduServiceInfoProto.DESCRIPTION, getDescription()); proto.write(ApduServiceInfoProto.ON_HOST, mOnHost); if (!mOnHost) { diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java index f8f7dfe034b5..7a36b269240c 100644 --- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java +++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java @@ -340,7 +340,7 @@ public final class NfcFServiceInfo implements Parcelable { * @param proto the ProtoOutputStream to write to */ public void dumpDebug(ProtoOutputStream proto) { - getComponent().dumpDebug(proto, NfcFServiceInfoProto.COMPONENT_NAME); + Utils.dumpDebugComponentName(getComponent(), proto, NfcFServiceInfoProto.COMPONENT_NAME); proto.write(NfcFServiceInfoProto.DESCRIPTION, getDescription()); proto.write(NfcFServiceInfoProto.SYSTEM_CODE, getSystemCode()); proto.write(NfcFServiceInfoProto.NFCID2, getNfcid2()); diff --git a/core/java/android/nfc/cardemulation/Utils.java b/core/java/android/nfc/cardemulation/Utils.java new file mode 100644 index 000000000000..202e1cfb48f6 --- /dev/null +++ b/core/java/android/nfc/cardemulation/Utils.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc.cardemulation; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.ComponentNameProto; +import android.util.proto.ProtoOutputStream; + +/** @hide */ +public final class Utils { + private Utils() { + } + + /** Copied from {@link ComponentName#dumpDebug(ProtoOutputStream, long)} */ + public static void dumpDebugComponentName( + @NonNull ComponentName componentName, @NonNull ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(ComponentNameProto.PACKAGE_NAME, componentName.getPackageName()); + proto.write(ComponentNameProto.CLASS_NAME, componentName.getClassName()); + proto.end(token); + } +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 54e4909c12af..d7cd61567cc7 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -99,7 +99,6 @@ import android.widget.Editor; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; -import com.android.internal.widget.ILockSettings; import java.io.IOException; import java.lang.annotation.ElementType; @@ -116,6 +115,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * The Settings provider contains global system-level device preferences. @@ -2978,19 +2978,22 @@ public final class Settings { } private static final class GenerationTracker { - private final MemoryIntArray mArray; - private final Runnable mErrorHandler; + @NonNull private final String mName; + @NonNull private final MemoryIntArray mArray; + @NonNull private final Consumer<String> mErrorHandler; private final int mIndex; private int mCurrentGeneration; - public GenerationTracker(@NonNull MemoryIntArray array, int index, - int generation, Runnable errorHandler) { + GenerationTracker(@NonNull String name, @NonNull MemoryIntArray array, int index, + int generation, Consumer<String> errorHandler) { + mName = name; mArray = array; mIndex = index; mErrorHandler = errorHandler; mCurrentGeneration = generation; } + // This method also updates the obsolete generation code stored locally public boolean isGenerationChanged() { final int currentGeneration = readCurrentGeneration(); if (currentGeneration >= 0) { @@ -3011,9 +3014,7 @@ public final class Settings { return mArray.get(mIndex); } catch (IOException e) { Log.e(TAG, "Error getting current generation", e); - if (mErrorHandler != null) { - mErrorHandler.run(); - } + mErrorHandler.accept(mName); } return -1; } @@ -3023,9 +3024,6 @@ public final class Settings { mArray.close(); } catch (IOException e) { Log.e(TAG, "Error closing backing array", e); - if (mErrorHandler != null) { - mErrorHandler.run(); - } } } } @@ -3088,8 +3086,21 @@ public final class Settings { private final ArraySet<String> mAllFields; private final ArrayMap<String, Integer> mReadableFieldsWithMaxTargetSdk; + // Mapping from the name of a setting (or the prefix of a namespace) to a generation tracker @GuardedBy("this") - private GenerationTracker mGenerationTracker; + private ArrayMap<String, GenerationTracker> mGenerationTrackers = new ArrayMap<>(); + + private Consumer<String> mGenerationTrackerErrorHandler = (String name) -> { + synchronized (NameValueCache.this) { + Log.e(TAG, "Error accessing generation tracker - removing"); + final GenerationTracker tracker = mGenerationTrackers.get(name); + if (tracker != null) { + tracker.destroy(); + mGenerationTrackers.remove(name); + } + mValues.remove(name); + } + }; <T extends NameValueTable> NameValueCache(Uri uri, String getCommand, String setCommand, String deleteCommand, ContentProviderHolder providerHolder, @@ -3178,6 +3189,43 @@ public final class Settings { @UnsupportedAppUsage public String getStringForUser(ContentResolver cr, String name, final int userHandle) { + final boolean isSelf = (userHandle == UserHandle.myUserId()); + int currentGeneration = -1; + boolean needsGenerationTracker = false; + + if (isSelf) { + synchronized (NameValueCache.this) { + final GenerationTracker generationTracker = mGenerationTrackers.get(name); + if (generationTracker != null) { + if (generationTracker.isGenerationChanged()) { + if (DEBUG) { + Log.i(TAG, "Generation changed for setting:" + name + + " type:" + mUri.getPath() + + " in package:" + cr.getPackageName() + + " and user:" + userHandle); + } + mValues.remove(name); + } else if (mValues.containsKey(name)) { + if (DEBUG) { + Log.i(TAG, "Cache hit for setting:" + name); + } + return mValues.get(name); + } + currentGeneration = generationTracker.getCurrentGeneration(); + } else { + needsGenerationTracker = true; + } + } + } else { + if (DEBUG || LOCAL_LOGV) { + Log.v(TAG, "get setting for user " + userHandle + + " by user " + UserHandle.myUserId() + " so skipping cache"); + } + } + if (DEBUG) { + Log.i(TAG, "Cache miss for setting:" + name + " for user:" + userHandle); + } + // Check if the target settings key is readable. Reject if the caller is not system and // is trying to access a settings key defined in the Settings.Secure, Settings.System or // Settings.Global and is not annotated as @Readable. @@ -3211,31 +3259,6 @@ public final class Settings { } } - final boolean isSelf = (userHandle == UserHandle.myUserId()); - int currentGeneration = -1; - if (isSelf) { - synchronized (NameValueCache.this) { - if (mGenerationTracker != null) { - if (mGenerationTracker.isGenerationChanged()) { - if (DEBUG) { - Log.i(TAG, "Generation changed for type:" - + mUri.getPath() + " in package:" - + cr.getPackageName() +" and user:" + userHandle); - } - mValues.clear(); - } else if (mValues.containsKey(name)) { - return mValues.get(name); - } - if (mGenerationTracker != null) { - currentGeneration = mGenerationTracker.getCurrentGeneration(); - } - } - } - } else { - if (LOCAL_LOGV) Log.v(TAG, "get setting for user " + userHandle - + " by user " + UserHandle.myUserId() + " so skipping cache"); - } - IContentProvider cp = mProviderHolder.getProvider(cr); // Try the fast path first, not using query(). If this @@ -3244,24 +3267,17 @@ public final class Settings { // interface. if (mCallGetCommand != null) { try { - Bundle args = null; + Bundle args = new Bundle(); if (!isSelf) { - args = new Bundle(); args.putInt(CALL_METHOD_USER_KEY, userHandle); } - boolean needsGenerationTracker = false; - synchronized (NameValueCache.this) { - if (isSelf && mGenerationTracker == null) { - needsGenerationTracker = true; - if (args == null) { - args = new Bundle(); - } - args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null); - if (DEBUG) { - Log.i(TAG, "Requested generation tracker for type: "+ mUri.getPath() - + " in package:" + cr.getPackageName() +" and user:" - + userHandle); - } + if (needsGenerationTracker) { + args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null); + if (DEBUG) { + Log.i(TAG, "Requested generation tracker for setting:" + name + + " type:" + mUri.getPath() + + " in package:" + cr.getPackageName() + + " and user:" + userHandle); } } Bundle b; @@ -3298,33 +3314,24 @@ public final class Settings { final int generation = b.getInt( CALL_METHOD_GENERATION_KEY, 0); if (DEBUG) { - Log.i(TAG, "Received generation tracker for type:" - + mUri.getPath() + " in package:" - + cr.getPackageName() + " and user:" - + userHandle + " with index:" + index); - } - if (mGenerationTracker != null) { - mGenerationTracker.destroy(); + Log.i(TAG, "Received generation tracker for setting:" + + name + + " type:" + mUri.getPath() + + " in package:" + cr.getPackageName() + + " and user:" + userHandle + + " with index:" + index); } - mGenerationTracker = new GenerationTracker(array, index, - generation, () -> { - synchronized (NameValueCache.this) { - Log.e(TAG, "Error accessing generation" - + " tracker - removing"); - if (mGenerationTracker != null) { - GenerationTracker generationTracker = - mGenerationTracker; - mGenerationTracker = null; - generationTracker.destroy(); - mValues.clear(); - } - } - }); + mGenerationTrackers.put(name, new GenerationTracker(name, + array, index, generation, + mGenerationTrackerErrorHandler)); currentGeneration = generation; } } - if (mGenerationTracker != null && currentGeneration == - mGenerationTracker.getCurrentGeneration()) { + if (mGenerationTrackers.get(name) != null && currentGeneration + == mGenerationTrackers.get(name).getCurrentGeneration()) { + if (DEBUG) { + Log.i(TAG, "Updating cache for setting:" + name); + } mValues.put(name, value); } } @@ -3367,15 +3374,14 @@ public final class Settings { String value = c.moveToNext() ? c.getString(0) : null; synchronized (NameValueCache.this) { - if (mGenerationTracker != null - && currentGeneration == mGenerationTracker.getCurrentGeneration()) { + if (mGenerationTrackers.get(name) != null && currentGeneration + == mGenerationTrackers.get(name).getCurrentGeneration()) { + if (DEBUG) { + Log.i(TAG, "Updating cache for setting:" + name + " using query"); + } mValues.put(name, value); } } - if (LOCAL_LOGV) { - Log.v(TAG, "cache miss [" + mUri.getLastPathSegment() + "]: " + - name + " = " + (value == null ? "(null)" : value)); - } return value; } catch (RemoteException e) { Log.w(TAG, "Can't get key " + name + " from " + mUri, e); @@ -3409,18 +3415,29 @@ public final class Settings { Config.enforceReadPermission(namespace); ArrayMap<String, String> keyValues = new ArrayMap<>(); int currentGeneration = -1; + boolean needsGenerationTracker = false; synchronized (NameValueCache.this) { - if (mGenerationTracker != null) { - if (mGenerationTracker.isGenerationChanged()) { + final GenerationTracker generationTracker = mGenerationTrackers.get(prefix); + if (generationTracker != null) { + if (generationTracker.isGenerationChanged()) { if (DEBUG) { - Log.i(TAG, "Generation changed for type:" + mUri.getPath() + Log.i(TAG, "Generation changed for prefix:" + prefix + + " type:" + mUri.getPath() + " in package:" + cr.getPackageName()); } - mValues.clear(); + for (int i = 0; i < mValues.size(); ++i) { + String key = mValues.keyAt(i); + if (key.startsWith(prefix)) { + mValues.remove(key); + } + } } else { boolean prefixCached = mValues.containsKey(prefix); if (prefixCached) { + if (DEBUG) { + Log.i(TAG, "Cache hit for prefix:" + prefix); + } if (!names.isEmpty()) { for (String name : names) { if (mValues.containsKey(name)) { @@ -3440,9 +3457,9 @@ public final class Settings { return keyValues; } } - if (mGenerationTracker != null) { - currentGeneration = mGenerationTracker.getCurrentGeneration(); - } + currentGeneration = generationTracker.getCurrentGeneration(); + } else { + needsGenerationTracker = true; } } @@ -3450,20 +3467,20 @@ public final class Settings { // No list command specified, return empty map return keyValues; } + if (DEBUG) { + Log.i(TAG, "Cache miss for prefix:" + prefix); + } IContentProvider cp = mProviderHolder.getProvider(cr); try { Bundle args = new Bundle(); args.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix); - boolean needsGenerationTracker = false; - synchronized (NameValueCache.this) { - if (mGenerationTracker == null) { - needsGenerationTracker = true; - args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null); - if (DEBUG) { - Log.i(TAG, "Requested generation tracker for type: " - + mUri.getPath() + " in package:" + cr.getPackageName()); - } + if (needsGenerationTracker) { + args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null); + if (DEBUG) { + Log.i(TAG, "Requested generation tracker for prefix:" + prefix + + " type: " + mUri.getPath() + + " in package:" + cr.getPackageName()); } } @@ -3516,32 +3533,22 @@ public final class Settings { final int generation = b.getInt( CALL_METHOD_GENERATION_KEY, 0); if (DEBUG) { - Log.i(TAG, "Received generation tracker for type:" - + mUri.getPath() + " in package:" - + cr.getPackageName() + " with index:" + index); + Log.i(TAG, "Received generation tracker for prefix:" + prefix + + " type:" + mUri.getPath() + + " in package:" + cr.getPackageName() + + " with index:" + index); } - if (mGenerationTracker != null) { - mGenerationTracker.destroy(); - } - mGenerationTracker = new GenerationTracker(array, index, - generation, () -> { - synchronized (NameValueCache.this) { - Log.e(TAG, "Error accessing generation tracker" - + " - removing"); - if (mGenerationTracker != null) { - GenerationTracker generationTracker = - mGenerationTracker; - mGenerationTracker = null; - generationTracker.destroy(); - mValues.clear(); - } - } - }); + mGenerationTrackers.put(prefix, + new GenerationTracker(prefix, array, index, generation, + mGenerationTrackerErrorHandler)); currentGeneration = generation; } } - if (mGenerationTracker != null && currentGeneration - == mGenerationTracker.getCurrentGeneration()) { + if (mGenerationTrackers.get(prefix) != null && currentGeneration + == mGenerationTrackers.get(prefix).getCurrentGeneration()) { + if (DEBUG) { + Log.i(TAG, "Updating cache for prefix:" + prefix); + } // cache the complete list of flags for the namespace mValues.putAll(flagsToValues); // Adding the prefix as a signal that the prefix is cached. @@ -3557,11 +3564,11 @@ public final class Settings { public void clearGenerationTrackerForTest() { synchronized (NameValueCache.this) { - if (mGenerationTracker != null) { - mGenerationTracker.destroy(); + for (int i = 0; i < mGenerationTrackers.size(); i++) { + mGenerationTrackers.valueAt(i).destroy(); } + mGenerationTrackers.clear(); mValues.clear(); - mGenerationTracker = null; } } } @@ -6184,9 +6191,6 @@ public final class Settings { sProviderHolder, Secure.class); - private static ILockSettings sLockSettings = null; - - private static boolean sIsSystemProcess; @UnsupportedAppUsage private static final HashSet<String> MOVED_TO_LOCK_SETTINGS; @UnsupportedAppUsage @@ -6350,35 +6354,25 @@ public final class Settings { return Global.getStringForUser(resolver, name, userHandle); } - if (MOVED_TO_LOCK_SETTINGS.contains(name)) { - synchronized (Secure.class) { - if (sLockSettings == null) { - sLockSettings = ILockSettings.Stub.asInterface( - (IBinder) ServiceManager.getService("lock_settings")); - sIsSystemProcess = Process.myUid() == Process.SYSTEM_UID; - } - } - if (sLockSettings != null && !sIsSystemProcess) { - // No context; use the ActivityThread's context as an approximation for - // determining the target API level. - Application application = ActivityThread.currentApplication(); - - boolean isPreMnc = application != null - && application.getApplicationInfo() != null - && application.getApplicationInfo().targetSdkVersion - <= VERSION_CODES.LOLLIPOP_MR1; - if (isPreMnc) { - try { - return sLockSettings.getString(name, "0", userHandle); - } catch (RemoteException re) { - // Fall through - } - } else { - throw new SecurityException("Settings.Secure." + name - + " is deprecated and no longer accessible." - + " See API documentation for potential replacements."); - } + if (MOVED_TO_LOCK_SETTINGS.contains(name) && Process.myUid() != Process.SYSTEM_UID) { + // No context; use the ActivityThread's context as an approximation for + // determining the target API level. + Application application = ActivityThread.currentApplication(); + + boolean isPreMnc = application != null + && application.getApplicationInfo() != null + && application.getApplicationInfo().targetSdkVersion + <= VERSION_CODES.LOLLIPOP_MR1; + if (isPreMnc) { + // Old apps used to get the three deprecated LOCK_PATTERN_* settings from + // ILockSettings.getString(). For security reasons, we now just return a + // stubbed-out value. Note: the only one of these three settings actually known + // to have been used was LOCK_PATTERN_ENABLED, and ILockSettings.getString() + // already always returned "0" for that starting in Android 11. + return "0"; } + throw new SecurityException("Settings.Secure." + name + " is deprecated and no" + + " longer accessible. See API documentation for potential replacements."); } return sNameValueCache.getStringForUser(resolver, name, userHandle); @@ -10784,6 +10778,15 @@ public final class Settings { "low_power_warning_acknowledged"; /** + * Whether the "first time extra battery saver warning" dialog needs to be shown + * (0: default) or not (1). + * + * @hide + */ + public static final String EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED = + "extra_low_power_warning_acknowledged"; + + /** * 0 (default) Auto battery saver suggestion has not been suppressed. 1) it has been * suppressed. * @hide @@ -15199,6 +15202,12 @@ public final class Settings { public static final String LOW_POWER_MODE = "low_power"; /** + * If 1 extra low power mode is enabled. + * @hide + */ + public static final String EXTRA_LOW_POWER_MODE = "extra_low_power"; + + /** * If 1, battery saver ({@link #LOW_POWER_MODE}) will be re-activated after the device * is unplugged from a charger or rebooted. * @hide diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 6896e0244791..d14abfd5d7ae 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -4937,4 +4937,66 @@ public final class Telephony { return ALL_COLUMNS; } } + + /** + * Stores incoming satellite datagrams. + * @hide + */ + public static final class SatelliteDatagrams { + /** + * Not instantiable. + * @hide + */ + private SatelliteDatagrams() {} + + /** + * Provider name for Satellite Datagrams table. + */ + public static final String PROVIDER_NAME = "satellite"; + + /** + * Table name for Satellite Datagrams table. + */ + public static final String TABLE_NAME = "incoming_datagrams"; + + /** + * URL for satellite incoming datagrams table. + */ + private static final String URL = "content://" + PROVIDER_NAME + "/" + TABLE_NAME; + + /** + * The {@code content://} style URI for this provider. + * @hide + */ + public static final Uri CONTENT_URI = Uri.parse(URL); + + /** + * SatelliteProvider unique key column name is the datagram id. + * <P>Type: INTEGER (int)</P> + * @hide + */ + public static final String COLUMN_UNIQUE_KEY_DATAGRAM_ID = "datagram_id"; + + /** + * SatelliteProvider column name for storing datagram. + * <p>TYPE: BLOB + * @hide + */ + public static final String COLUMN_DATAGRAM = "datagram"; + + /** All columns in {@link SatelliteDatagrams} table. */ + private static final List<String> ALL_COLUMNS = List.of( + COLUMN_UNIQUE_KEY_DATAGRAM_ID, + COLUMN_DATAGRAM + ); + + /** + * @return All columns in {@link SatelliteDatagrams} table. + * @hide + */ + @NonNull + public static List<String> getAllColumns() { + return ALL_COLUMNS; + } + } } diff --git a/core/java/android/service/voice/VisualQueryDetectionService.java b/core/java/android/service/voice/VisualQueryDetectionService.java index 2e455c2eeb29..1783186cd045 100644 --- a/core/java/android/service/voice/VisualQueryDetectionService.java +++ b/core/java/android/service/voice/VisualQueryDetectionService.java @@ -49,7 +49,7 @@ import java.util.function.IntConsumer; * {@link VoiceInteractionService#createVisualQueryDetector(PersistableBundle, SharedMemory, * Executor, VisualQueryDetector.Callback)} is called, the system will bind the application's * {@link VisualQueryDetectionService}. When requested from {@link VoiceInteractionService}, the - * system calls into the {@link VisualQueryDetectionService#onStartDetection(Callback)} to enable + * system calls into the {@link VisualQueryDetectionService#onStartDetection()} to enable * detection. This method MUST be implemented to support visual query detection service. * * Note: Methods in this class may be called concurrently. @@ -78,6 +78,8 @@ public abstract class VisualQueryDetectionService extends Service /** @hide */ public static final String KEY_INITIALIZATION_STATUS = "initialization_status"; + private IDetectorSessionVisualQueryDetectionCallback mRemoteCallback = null; + private final ISandboxedDetectionService mInterface = new ISandboxedDetectionService.Stub() { @@ -85,7 +87,8 @@ public abstract class VisualQueryDetectionService extends Service public void detectWithVisualSignals( IDetectorSessionVisualQueryDetectionCallback callback) { Log.v(TAG, "#detectWithVisualSignals"); - VisualQueryDetectionService.this.onStartDetection(new Callback(callback)); + mRemoteCallback = callback; + VisualQueryDetectionService.this.onStartDetection(); } @Override @@ -178,16 +181,41 @@ public abstract class VisualQueryDetectionService extends Service /** * This is called after the service is set up and the client should open the camera and the - * microphone to start recognition. - * - * Called when the {@link VoiceInteractionService} requests that this service - * {@link HotwordDetector#startRecognition()} start recognition on audio coming directly + * microphone to start recognition. When the {@link VoiceInteractionService} requests that this + * service {@link HotwordDetector#startRecognition()} start recognition on audio coming directly * from the device microphone. - * - * @param callback The callback to use for responding to the detection request. - * + * <p> + * Signal senders that return attention and query results are also expected to be called in this + * method according to the detection outcomes. + * <p> + * On successful user attention, developers should call + * {@link VisualQueryDetectionService#gainedAttention()} to enable the streaming of the query. + * <p> + * On user attention is lost, developers should call + * {@link VisualQueryDetectionService#lostAttention()} to disable the streaming of the query. + * <p> + * On query is detected and ready to stream, developers should call + * {@link VisualQueryDetectionService#streamQuery(String)} to return detected query to the + * {@link VisualQueryDetector}. + * <p> + * On streamed query should be rejected, clients should call + * {@link VisualQueryDetectionService#rejectQuery()} to abandon query streamed to the + * {@link VisualQueryDetector}. + * <p> + * On streamed query is finished, clients should call + * {@link VisualQueryDetectionService#finishQuery()} to complete query streamed to + * {@link VisualQueryDetector}. + * <p> + * Before a call for {@link VisualQueryDetectionService#streamQuery(String)} is triggered, + * {@link VisualQueryDetectionService#gainedAttention()} MUST be called to enable the streaming + * of query. A query streaming is also expected to be finished by calling either + * {@link VisualQueryDetectionService#finishQuery()} or + * {@link VisualQueryDetectionService#rejectQuery()} before a new query should start streaming. + * When the service enters the state where query streaming should be disabled, + * {@link VisualQueryDetectionService#lostAttention()} MUST be called to block unnecessary + * streaming. */ - public void onStartDetection(@NonNull Callback callback) { + public void onStartDetection() { throw new UnsupportedOperationException(); } @@ -199,118 +227,78 @@ public abstract class VisualQueryDetectionService extends Service } /** - * Callback for sending out signals and returning query results. - * - * On successful user attention, developers should call {@link Callback#onAttentionGained()} - * to enable the streaming of the query. - * <p> - * On user attention is lost, developers should call {@link Callback#onAttentionLost()} to - * disable the streaming of the query. - * <p> - * On query is detected and ready to stream, developers should call - * {@link Callback#onQueryDetected(String)} to return detected query to the - * {@link VisualQueryDetector}. - * <p> - * On streamed query should be rejected, clients should call {@link Callback#onQueryRejected()} - * to abandon query streamed to the {@link VisualQueryDetector}. - * <p> - * On streamed query is finished, clients should call {@link Callback#onQueryFinished()} to - * complete query streamed to {@link VisualQueryDetector}. - * <p> - * Before a call for {@link Callback#onQueryDetected(String)} is triggered, - * {@link Callback#onAttentionGained()} MUST be called to enable the streaming of query. A query - * streaming is also expected to be finished by calling either - * {@link Callback#onQueryFinished()} or {@link Callback#onQueryRejected()} before a new query - * should start streaming. When the service enters the state where query streaming should be - * disabled, {@link Callback#onAttentionLost()} MUST be called to block unnecessary streaming. + * Informs the system that the user attention is gained so queries can be streamed. */ - public static final class Callback { - - // TODO: consider making the constructor a test api for testing purpose - public Callback() { - mRemoteCallback = null; - } - - private final IDetectorSessionVisualQueryDetectionCallback mRemoteCallback; - - private Callback(IDetectorSessionVisualQueryDetectionCallback remoteCallback) { - mRemoteCallback = remoteCallback; - } - - /** - * Informs attention listener that the user attention is gained. - */ - public void onAttentionGained() { - try { - mRemoteCallback.onAttentionGained(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + public final void gainedAttention() { + try { + mRemoteCallback.onAttentionGained(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } + } - /** - * Informs attention listener that the user attention is lost. - */ - public void onAttentionLost() { - try { - mRemoteCallback.onAttentionLost(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + /** + * Informs the system that the user attention is lost to stop streaming. + */ + public final void lostAttention() { + try { + mRemoteCallback.onAttentionLost(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } + } - /** - * Informs the {@link VisualQueryDetector} with the text content being captured about the - * query from the audio source. {@code partialQuery} is provided to the - * {@link VisualQueryDetector}. This method is expected to be only triggered if - * {@link Callback#onAttentionGained()} is called to put the service into the attention - * gained state. - * - * @param partialQuery Partially detected query in string. - * @throws IllegalStateException if method called without attention gained. - */ - public void onQueryDetected(@NonNull String partialQuery) throws IllegalStateException { - Objects.requireNonNull(partialQuery); - try { - mRemoteCallback.onQueryDetected(partialQuery); - } catch (RemoteException e) { - throw new IllegalStateException("#onQueryDetected must be only be triggered after " - + "calling #onAttentionGained to be in the attention gained state."); - } + /** + * Informs the {@link VisualQueryDetector} with the text content being captured about the + * query from the audio source. {@code partialQuery} is provided to the + * {@link VisualQueryDetector}. This method is expected to be only triggered if + * {@link VisualQueryDetectionService#gainedAttention()} is called to put the service into the + * attention gained state. + * + * @param partialQuery Partially detected query in string. + * @throws IllegalStateException if method called without attention gained. + */ + public final void streamQuery(@NonNull String partialQuery) throws IllegalStateException { + Objects.requireNonNull(partialQuery); + try { + mRemoteCallback.onQueryDetected(partialQuery); + } catch (RemoteException e) { + throw new IllegalStateException("#streamQuery must be only be triggered after " + + "calling #gainedAttention to be in the attention gained state."); } + } - /** - * Informs the {@link VisualQueryDetector} to abandon the streamed partial query that has - * been sent to {@link VisualQueryDetector}.This method is expected to be only triggered if - * {@link Callback#onQueryDetected()} is called to put the service into the query streaming - * state. - * - * @throws IllegalStateException if method called without query streamed. - */ - public void onQueryRejected() throws IllegalStateException { - try { - mRemoteCallback.onQueryRejected(); - } catch (RemoteException e) { - throw new IllegalStateException("#onQueryRejected must be only be triggered after " - + "calling #onQueryDetected to be in the query streaming state."); - } + /** + * Informs the {@link VisualQueryDetector} to abandon the streamed partial query that has + * been sent to {@link VisualQueryDetector}.This method is expected to be only triggered if + * {@link VisualQueryDetectionService#streamQuery(String)} is called to put the service into + * the query streaming state. + * + * @throws IllegalStateException if method called without query streamed. + */ + public final void rejectQuery() throws IllegalStateException { + try { + mRemoteCallback.onQueryRejected(); + } catch (RemoteException e) { + throw new IllegalStateException("#rejectQuery must be only be triggered after " + + "calling #streamQuery to be in the query streaming state."); } + } - /** - * Informs {@link VisualQueryDetector} with the metadata to complete the streamed partial - * query that has been sent to {@link VisualQueryDetector}. This method is expected to be - * only triggered if {@link Callback#onQueryDetected()} is called to put the service into - * the query streaming state. - * - * @throws IllegalStateException if method called without query streamed. - */ - public void onQueryFinished() throws IllegalStateException { - try { - mRemoteCallback.onQueryFinished(); - } catch (RemoteException e) { - throw new IllegalStateException("#onQueryFinished must be only be triggered after " - + "calling #onQueryDetected to be in the query streaming state."); - } + /** + * Informs {@link VisualQueryDetector} with the metadata to complete the streamed partial + * query that has been sent to {@link VisualQueryDetector}. This method is expected to be + * only triggered if {@link VisualQueryDetectionService#streamQuery(String)} is called to put + * the service into the query streaming state. + * + * @throws IllegalStateException if method called without query streamed. + */ + public final void finishQuery() throws IllegalStateException { + try { + mRemoteCallback.onQueryFinished(); + } catch (RemoteException e) { + throw new IllegalStateException("#finishQuery must be only be triggered after " + + "calling #streamQuery to be in the query streaming state."); } } diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java index 0be3253fc570..f0f6a4f829cf 100644 --- a/core/java/android/service/voice/VisualQueryDetector.java +++ b/core/java/android/service/voice/VisualQueryDetector.java @@ -177,7 +177,8 @@ public class VisualQueryDetector { public interface Callback { /** - * Called when the {@link VisualQueryDetectionService} starts to stream partial queries. + * Called when the {@link VisualQueryDetectionService} starts to stream partial queries + * with {@link VisualQueryDetectionService#streamQuery(String)}. * * @param partialQuery The partial query in a text form being streamed. */ @@ -185,12 +186,13 @@ public class VisualQueryDetector { /** * Called when the {@link VisualQueryDetectionService} decides to abandon the streamed - * partial queries. + * partial queries with {@link VisualQueryDetectionService#rejectQuery()}. */ void onQueryRejected(); /** - * Called when the {@link VisualQueryDetectionService} finishes streaming partial queries. + * Called when the {@link VisualQueryDetectionService} finishes streaming partial queries + * with {@link VisualQueryDetectionService#finishQuery()}. */ void onQueryFinished(); diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index f648ad4fbac3..434b1c76113f 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -53,9 +53,7 @@ import com.android.internal.telephony.ITelephonyRegistry; import java.lang.ref.WeakReference; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.WeakHashMap; @@ -83,15 +81,16 @@ public class TelephonyRegistryManager { * A mapping between {@link SubscriptionManager.OnSubscriptionsChangedListener} and * its callback IOnSubscriptionsChangedListener. */ - private final Map<SubscriptionManager.OnSubscriptionsChangedListener, - IOnSubscriptionsChangedListener> mSubscriptionChangedListenerMap = new HashMap<>(); + private final ConcurrentHashMap<SubscriptionManager.OnSubscriptionsChangedListener, + IOnSubscriptionsChangedListener> + mSubscriptionChangedListenerMap = new ConcurrentHashMap<>(); /** * A mapping between {@link SubscriptionManager.OnOpportunisticSubscriptionsChangedListener} and * its callback IOnSubscriptionsChangedListener. */ - private final Map<SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, - IOnSubscriptionsChangedListener> mOpportunisticSubscriptionChangedListenerMap - = new HashMap<>(); + private final ConcurrentHashMap<SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, + IOnSubscriptionsChangedListener> + mOpportunisticSubscriptionChangedListenerMap = new ConcurrentHashMap<>(); /** * A mapping between {@link CarrierConfigManager.CarrierConfigChangeListener} and its callback diff --git a/core/java/android/util/SparseArrayMap.java b/core/java/android/util/SparseArrayMap.java index 1a2c4df96b36..b4e1f59874b0 100644 --- a/core/java/android/util/SparseArrayMap.java +++ b/core/java/android/util/SparseArrayMap.java @@ -90,6 +90,14 @@ public class SparseArrayMap<K, V> { } /** + * Removes the data for the keyIndex and mapIndex, if there was any. + * @hide + */ + public void deleteAt(int keyIndex, int mapIndex) { + mData.valueAt(keyIndex).removeAt(mapIndex); + } + + /** * Get the value associated with the int-K pair. */ @Nullable diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index e02e600f53c8..39477380e196 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -20,7 +20,6 @@ import static android.view.InsetsSourceProto.FRAME; import static android.view.InsetsSourceProto.TYPE; import static android.view.InsetsSourceProto.VISIBLE; import static android.view.InsetsSourceProto.VISIBLE_FRAME; -import static android.view.ViewRootImpl.CAPTION_ON_SHELL; import static android.view.WindowInsets.Type.ime; import android.annotation.IntRange; @@ -169,7 +168,7 @@ public class InsetsSource implements Parcelable { // During drag-move and drag-resizing, the caption insets position may not get updated // before the app frame get updated. To layout the app content correctly during drag events, // we always return the insets with the corresponding height covering the top. - if (!CAPTION_ON_SHELL && getType() == WindowInsets.Type.captionBar()) { + if (getType() == WindowInsets.Type.captionBar()) { return Insets.of(0, frame.height(), 0, 0); } // Checks for whether there is shared edge with insets for 0-width/height window. diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS index 9e1762065367..b574ecf32dd8 100644 --- a/core/java/android/view/OWNERS +++ b/core/java/android/view/OWNERS @@ -31,6 +31,7 @@ per-file Input*.java = file:/services/core/java/com/android/server/input/OWNERS per-file Input*.aidl = file:/services/core/java/com/android/server/input/OWNERS per-file KeyEvent.java = file:/services/core/java/com/android/server/input/OWNERS per-file MotionEvent.java = file:/services/core/java/com/android/server/input/OWNERS +per-file MotionPredictor.java = file:/services/core/java/com/android/server/input/OWNERS per-file PointerIcon.java = file:/services/core/java/com/android/server/input/OWNERS per-file SimulatedDpad.java = file:/services/core/java/com/android/server/input/OWNERS per-file BatchedInputEventReceiver.java = file:/services/core/java/com/android/server/input/OWNERS diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f430ec300b5b..71a3a7b7cc51 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -10078,9 +10078,12 @@ public final class ViewRootImpl implements ViewParent, } void checkThread() { - if (mThread != Thread.currentThread()) { + Thread current = Thread.currentThread(); + if (mThread != current) { throw new CalledFromWrongThreadException( - "Only the original thread that created a view hierarchy can touch its views."); + "Only the original thread that created a view hierarchy can touch its views." + + " Expected: " + mThread.getName() + + " Calling: " + current.getName()); } } @@ -11382,6 +11385,10 @@ public final class ViewRootImpl implements ViewParent, sendBackKeyEvent(KeyEvent.ACTION_DOWN); sendBackKeyEvent(KeyEvent.ACTION_UP); }; + if (mOnBackInvokedDispatcher.hasImeOnBackInvokedDispatcher()) { + Log.d(TAG, "Skip registering CompatOnBackInvokedCallback on IME dispatcher"); + return; + } mOnBackInvokedDispatcher.registerOnBackInvokedCallback( OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatOnBackInvokedCallback); } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index aa631cfa5980..9504852f6e98 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -2068,12 +2068,14 @@ public final class AccessibilityManager { * {@link android.view.Display#INVALID_DISPLAY}, or is already being proxy-ed. * * @throws SecurityException if the app does not hold the - * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission. + * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the + * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission. * * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) + @RequiresPermission(allOf = {Manifest.permission.MANAGE_ACCESSIBILITY, + Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean registerDisplayProxy(@NonNull AccessibilityDisplayProxy proxy) { final IAccessibilityManager service; synchronized (mLock) { @@ -2096,12 +2098,14 @@ public final class AccessibilityManager { * @return {@code true} if the proxy is successfully unregistered. * * @throws SecurityException if the app does not hold the - * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission. + * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the + * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission. * * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) + @RequiresPermission(allOf = {Manifest.permission.MANAGE_ACCESSIBILITY, + Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean unregisterDisplayProxy(@NonNull AccessibilityDisplayProxy proxy) { final IAccessibilityManager service; synchronized (mLock) { diff --git a/core/java/android/view/autofill/OWNERS b/core/java/android/view/autofill/OWNERS index 26c59a68fd53..622b0e208812 100644 --- a/core/java/android/view/autofill/OWNERS +++ b/core/java/android/view/autofill/OWNERS @@ -1,10 +1,6 @@ # Bug component: 351486 -augale@google.com -haoranzhang@google.com -joannechung@google.com -markpun@google.com -lpeter@google.com simranjit@google.com -tymtsai@google.com +haoranzhang@google.com +skxu@google.com yunicorn@google.com diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl index bf9d3c2eefaf..fd86769293a6 100644 --- a/core/java/android/window/ITaskOrganizer.aidl +++ b/core/java/android/window/ITaskOrganizer.aidl @@ -34,8 +34,9 @@ oneway interface ITaskOrganizer { * has create a starting window for the Task. * * @param info The information about the Task that's available + * @param appToken Token of the application being started. */ - void addStartingWindow(in StartingWindowInfo info); + void addStartingWindow(in StartingWindowInfo info, IBinder appToken); /** * Called when the Task want to remove the starting window. diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java index 071c20f25e5c..1a58fd556609 100644 --- a/core/java/android/window/SnapshotDrawerUtils.java +++ b/core/java/android/window/SnapshotDrawerUtils.java @@ -329,21 +329,6 @@ public class SnapshotDrawerUtils { } /** - * Get or create a TaskDescription from a RunningTaskInfo. - */ - public static ActivityManager.TaskDescription getOrCreateTaskDescription( - ActivityManager.RunningTaskInfo runningTaskInfo) { - final ActivityManager.TaskDescription taskDescription; - if (runningTaskInfo.taskDescription != null) { - taskDescription = runningTaskInfo.taskDescription; - } else { - taskDescription = new ActivityManager.TaskDescription(); - taskDescription.setBackgroundColor(WHITE); - } - return taskDescription; - } - - /** * Help method to draw the snapshot on a surface. */ public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp, @@ -359,8 +344,13 @@ public class SnapshotDrawerUtils { final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams; final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo; - final ActivityManager.TaskDescription taskDescription = - getOrCreateTaskDescription(runningTaskInfo); + final ActivityManager.TaskDescription taskDescription; + if (runningTaskInfo.taskDescription != null) { + taskDescription = runningTaskInfo.taskDescription; + } else { + taskDescription = new ActivityManager.TaskDescription(); + taskDescription.setBackgroundColor(WHITE); + } drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags, attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes); final Rect systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState); diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java index 451acbe84a60..1b64e613ed66 100644 --- a/core/java/android/window/StartingWindowInfo.java +++ b/core/java/android/window/StartingWindowInfo.java @@ -22,12 +22,9 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.TaskInfo; import android.content.pm.ActivityInfo; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; -import android.os.RemoteException; import android.view.InsetsState; -import android.view.SurfaceControl; import android.view.WindowInsets; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowManager; @@ -62,8 +59,6 @@ public final class StartingWindowInfo implements Parcelable { /** @hide **/ public static final int STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN = 4; - public static final int STARTING_WINDOW_TYPE_WINDOWLESS = 5; - /** * @hide */ @@ -72,8 +67,7 @@ public final class StartingWindowInfo implements Parcelable { STARTING_WINDOW_TYPE_SPLASH_SCREEN, STARTING_WINDOW_TYPE_SNAPSHOT, STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN, - STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN, - STARTING_WINDOW_TYPE_WINDOWLESS + STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN }) public @interface StartingWindowType {} @@ -124,7 +118,6 @@ public final class StartingWindowInfo implements Parcelable { TYPE_PARAMETER_ACTIVITY_CREATED, TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN, TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN, - TYPE_PARAMETER_WINDOWLESS, TYPE_PARAMETER_LEGACY_SPLASH_SCREEN }) public @interface StartingTypeParams {} @@ -158,12 +151,6 @@ public final class StartingWindowInfo implements Parcelable { * @hide */ public static final int TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN = 0x00000080; - - /** - * Windowless surface - */ - public static final int TYPE_PARAMETER_WINDOWLESS = 0x00000100; - /** * Application is allowed to use the legacy splash screen * @hide @@ -195,33 +182,7 @@ public final class StartingWindowInfo implements Parcelable { */ public TaskSnapshot taskSnapshot; - @InsetsType public int requestedVisibleTypes = WindowInsets.Type.defaultVisible(); - - /** - * App token where the starting window should add to. - */ - public IBinder appToken; - - public IWindowlessStartingSurfaceCallback windowlessStartingSurfaceCallback; - - /** - * The root surface where windowless surface should attach on. - */ - public SurfaceControl rootSurface; - - /** - * Notify windowless surface is created. - * @param addedSurface Created surface. - */ - public void notifyAddComplete(SurfaceControl addedSurface) { - if (windowlessStartingSurfaceCallback != null) { - try { - windowlessStartingSurfaceCallback.onSurfaceAdded(addedSurface); - } catch (RemoteException e) { - // - } - } - } + public @InsetsType int requestedVisibleTypes = WindowInsets.Type.defaultVisible(); public StartingWindowInfo() { @@ -255,9 +216,6 @@ public final class StartingWindowInfo implements Parcelable { dest.writeBoolean(isKeyguardOccluded); dest.writeTypedObject(taskSnapshot, flags); dest.writeInt(requestedVisibleTypes); - dest.writeStrongBinder(appToken); - dest.writeStrongInterface(windowlessStartingSurfaceCallback); - dest.writeTypedObject(rootSurface, flags); } void readFromParcel(@NonNull Parcel source) { @@ -272,10 +230,6 @@ public final class StartingWindowInfo implements Parcelable { isKeyguardOccluded = source.readBoolean(); taskSnapshot = source.readTypedObject(TaskSnapshot.CREATOR); requestedVisibleTypes = source.readInt(); - appToken = source.readStrongBinder(); - windowlessStartingSurfaceCallback = IWindowlessStartingSurfaceCallback.Stub - .asInterface(source.readStrongBinder()); - rootSurface = source.readTypedObject(SurfaceControl.CREATOR); } @Override diff --git a/core/java/android/window/StartingWindowRemovalInfo.java b/core/java/android/window/StartingWindowRemovalInfo.java index 518123600b9a..384dacfe89ed 100644 --- a/core/java/android/window/StartingWindowRemovalInfo.java +++ b/core/java/android/window/StartingWindowRemovalInfo.java @@ -67,16 +67,6 @@ public final class StartingWindowRemovalInfo implements Parcelable { */ public float roundedCornerRadius; - /** - * Remove windowless surface. - */ - public boolean windowlessSurface; - - /** - * Remove immediately. - */ - public boolean removeImmediately; - public StartingWindowRemovalInfo() { } @@ -97,8 +87,6 @@ public final class StartingWindowRemovalInfo implements Parcelable { playRevealAnimation = source.readBoolean(); deferRemoveForIme = source.readBoolean(); roundedCornerRadius = source.readFloat(); - windowlessSurface = source.readBoolean(); - removeImmediately = source.readBoolean(); } @Override @@ -109,8 +97,6 @@ public final class StartingWindowRemovalInfo implements Parcelable { dest.writeBoolean(playRevealAnimation); dest.writeBoolean(deferRemoveForIme); dest.writeFloat(roundedCornerRadius); - dest.writeBoolean(windowlessSurface); - dest.writeBoolean(removeImmediately); } @Override @@ -119,9 +105,7 @@ public final class StartingWindowRemovalInfo implements Parcelable { + " frame=" + mainFrame + " playRevealAnimation=" + playRevealAnimation + " roundedCornerRadius=" + roundedCornerRadius - + " deferRemoveForIme=" + deferRemoveForIme - + " windowlessSurface=" + windowlessSurface - + " removeImmediately=" + removeImmediately + "}"; + + " deferRemoveForIme=" + deferRemoveForIme + "}"; } public static final @android.annotation.NonNull Creator<StartingWindowRemovalInfo> CREATOR = diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index d4728c1187d7..02878f8ae72b 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -92,10 +92,13 @@ public class TaskOrganizer extends WindowOrganizer { * has create a starting window for the Task. * * @param info The information about the Task that's available + * @param appToken Token of the application being started. + * context to for resources * @hide */ @BinderThread - public void addStartingWindow(@NonNull StartingWindowInfo info) {} + public void addStartingWindow(@NonNull StartingWindowInfo info, + @NonNull IBinder appToken) {} /** * Called when the Task want to remove the starting window. @@ -294,8 +297,9 @@ public class TaskOrganizer extends WindowOrganizer { private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() { @Override - public void addStartingWindow(StartingWindowInfo windowInfo) { - mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo)); + public void addStartingWindow(StartingWindowInfo windowInfo, + IBinder appToken) { + mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo, appToken)); } @Override diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index a8c2b2f28df4..8066f5085a01 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -357,6 +357,11 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { mImeDispatcher = imeDispatcher; } + /** Returns true if a non-null {@link ImeOnBackInvokedDispatcher} has been set. **/ + public boolean hasImeOnBackInvokedDispatcher() { + return mImeDispatcher != null; + } + /** * Class used to check whether a callback can be registered or not. This is meant to be * shared with {@link ProxyOnBackInvokedDispatcher} which needs to do the same checks. diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 62f1599f0706..d93fff98254b 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -37,6 +37,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR; @@ -240,6 +241,7 @@ public class InteractionJankMonitor { public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE = 67; public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME = 68; public static final int CUJ_IME_INSETS_ANIMATION = 69; + public static final int CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION = 70; private static final int NO_STATSD_LOGGING = -1; @@ -318,6 +320,7 @@ public class InteractionJankMonitor { UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_SWIPE, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_TO_HOME, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_ANIMATION, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION, }; private static class InstanceHolder { @@ -412,6 +415,7 @@ public class InteractionJankMonitor { CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE, CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME, CUJ_IME_INSETS_ANIMATION, + CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION, }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { @@ -946,6 +950,8 @@ public class InteractionJankMonitor { return "LAUNCHER_CLOSE_ALL_APPS_TO_HOME"; case CUJ_IME_INSETS_ANIMATION: return "IME_INSETS_ANIMATION"; + case CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION: + return "CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION"; } return "UNKNOWN"; } diff --git a/core/java/com/android/internal/midi/EventScheduler.java b/core/java/com/android/internal/midi/EventScheduler.java index 506902f61fd6..426cd74074eb 100644 --- a/core/java/com/android/internal/midi/EventScheduler.java +++ b/core/java/com/android/internal/midi/EventScheduler.java @@ -16,7 +16,6 @@ package com.android.internal.midi; -import java.util.Iterator; import java.util.SortedMap; import java.util.TreeMap; @@ -26,11 +25,11 @@ import java.util.TreeMap; * And only one Thread can read from the buffer. */ public class EventScheduler { - private static final long NANOS_PER_MILLI = 1000000; + public static final long NANOS_PER_MILLI = 1000000; private final Object mLock = new Object(); - volatile private SortedMap<Long, FastEventQueue> mEventBuffer; - private FastEventQueue mEventPool = null; + protected volatile SortedMap<Long, FastEventQueue> mEventBuffer; + protected FastEventQueue mEventPool = null; private int mMaxPoolSize = 200; private boolean mClosed; @@ -38,9 +37,13 @@ public class EventScheduler { mEventBuffer = new TreeMap<Long, FastEventQueue>(); } - // If we keep at least one node in the list then it can be atomic - // and non-blocking. - private class FastEventQueue { + /** + * Class for a fast event queue. + * + * If we keep at least one node in the list then it can be atomic + * and non-blocking. + */ + public static class FastEventQueue { // One thread takes from the beginning of the list. volatile SchedulableEvent mFirst; // A second thread returns events to the end of the list. @@ -48,7 +51,7 @@ public class EventScheduler { volatile long mEventsAdded; volatile long mEventsRemoved; - FastEventQueue(SchedulableEvent event) { + public FastEventQueue(SchedulableEvent event) { mFirst = event; mLast = mFirst; mEventsAdded = 1; @@ -149,7 +152,8 @@ public class EventScheduler { * @param event */ public void add(SchedulableEvent event) { - synchronized (mLock) { + Object lock = getLock(); + synchronized (lock) { FastEventQueue list = mEventBuffer.get(event.getTimestamp()); if (list == null) { long lowestTime = mEventBuffer.isEmpty() ? Long.MAX_VALUE @@ -159,7 +163,7 @@ public class EventScheduler { // If the event we added is earlier than the previous earliest // event then notify any threads waiting for the next event. if (event.getTimestamp() < lowestTime) { - mLock.notify(); + lock.notify(); } } else { list.add(event); @@ -167,7 +171,7 @@ public class EventScheduler { } } - private SchedulableEvent removeNextEventLocked(long lowestTime) { + protected SchedulableEvent removeNextEventLocked(long lowestTime) { SchedulableEvent event; FastEventQueue list = mEventBuffer.get(lowestTime); // Remove list from tree if this is the last node. @@ -186,7 +190,8 @@ public class EventScheduler { */ public SchedulableEvent getNextEvent(long time) { SchedulableEvent event = null; - synchronized (mLock) { + Object lock = getLock(); + synchronized (lock) { if (!mEventBuffer.isEmpty()) { long lowestTime = mEventBuffer.firstKey(); // Is it time for this list to be processed? @@ -209,7 +214,8 @@ public class EventScheduler { */ public SchedulableEvent waitNextEvent() throws InterruptedException { SchedulableEvent event = null; - synchronized (mLock) { + Object lock = getLock(); + synchronized (lock) { while (!mClosed) { long millisToWait = Integer.MAX_VALUE; if (!mEventBuffer.isEmpty()) { @@ -231,7 +237,7 @@ public class EventScheduler { } } } - mLock.wait((int) millisToWait); + lock.wait((int) millisToWait); } } return event; @@ -242,10 +248,25 @@ public class EventScheduler { mEventBuffer = new TreeMap<Long, FastEventQueue>(); } + /** + * Stops the EventScheduler. + * The subscriber calling waitNextEvent() will get one final SchedulableEvent returning null. + */ public void close() { - synchronized (mLock) { + Object lock = getLock(); + synchronized (lock) { mClosed = true; - mLock.notify(); + lock.notify(); } } + + /** + * Gets the lock. This doesn't lock it in anyway. + * Subclasses can override this. + * + * @return Object + */ + protected Object getLock() { + return mLock; + } } diff --git a/core/java/com/android/internal/midi/MidiEventMultiScheduler.java b/core/java/com/android/internal/midi/MidiEventMultiScheduler.java new file mode 100644 index 000000000000..16e4abecebdd --- /dev/null +++ b/core/java/com/android/internal/midi/MidiEventMultiScheduler.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.midi; + +/** + * Uses multiple MidiEventSchedulers for waiting for events. + * + */ +public class MidiEventMultiScheduler { + private MultiLockMidiEventScheduler[] mMidiEventSchedulers; + private int mNumEventSchedulers; + private int mNumClosedSchedulers = 0; + private final Object mMultiLock = new Object(); + + private class MultiLockMidiEventScheduler extends MidiEventScheduler { + @Override + public void close() { + synchronized (mMultiLock) { + mNumClosedSchedulers++; + } + super.close(); + } + + @Override + protected Object getLock() { + return mMultiLock; + } + + public boolean isEventBufferEmptyLocked() { + return mEventBuffer.isEmpty(); + } + + public long getLowestTimeLocked() { + return mEventBuffer.firstKey(); + } + } + + /** + * MidiEventMultiScheduler constructor + * + * @param numSchedulers the number of schedulers to create + */ + public MidiEventMultiScheduler(int numSchedulers) { + mNumEventSchedulers = numSchedulers; + mMidiEventSchedulers = new MultiLockMidiEventScheduler[numSchedulers]; + for (int i = 0; i < numSchedulers; i++) { + mMidiEventSchedulers[i] = new MultiLockMidiEventScheduler(); + } + } + + /** + * Waits for the next MIDI event. This will return true when it receives it. + * If all MidiEventSchedulers have been closed, this will return false. + * + * @return true if a MIDI event is received and false if all schedulers are closed. + */ + public boolean waitNextEvent() throws InterruptedException { + synchronized (mMultiLock) { + while (true) { + if (mNumClosedSchedulers >= mNumEventSchedulers) { + return false; + } + long lowestTime = Long.MAX_VALUE; + long now = System.nanoTime(); + for (MultiLockMidiEventScheduler eventScheduler : mMidiEventSchedulers) { + if (!eventScheduler.isEventBufferEmptyLocked()) { + lowestTime = Math.min(lowestTime, + eventScheduler.getLowestTimeLocked()); + } + } + if (lowestTime <= now) { + return true; + } + long nanosToWait = lowestTime - now; + // Add 1 millisecond so we don't wake up before it is + // ready. + long millisToWait = 1 + (nanosToWait / EventScheduler.NANOS_PER_MILLI); + // Clip 64-bit value to 32-bit max. + if (millisToWait > Integer.MAX_VALUE) { + millisToWait = Integer.MAX_VALUE; + } + mMultiLock.wait(millisToWait); + } + } + } + + /** + * Gets the number of MidiEventSchedulers. + * + * @return the number of MidiEventSchedulers. + */ + public int getNumEventSchedulers() { + return mNumEventSchedulers; + } + + /** + * Gets a specific MidiEventScheduler based on the index. + * + * @param index the zero indexed index of a MIDI event scheduler + * @return a MidiEventScheduler + */ + public MidiEventScheduler getEventScheduler(int index) { + return mMidiEventSchedulers[index]; + } + + /** + * Closes all event schedulers. + */ + public void close() { + for (MidiEventScheduler eventScheduler : mMidiEventSchedulers) { + eventScheduler.close(); + } + } +} diff --git a/core/java/com/android/internal/midi/MidiEventScheduler.java b/core/java/com/android/internal/midi/MidiEventScheduler.java index 7b019044110f..1b2934d519fd 100644 --- a/core/java/com/android/internal/midi/MidiEventScheduler.java +++ b/core/java/com/android/internal/midi/MidiEventScheduler.java @@ -79,7 +79,7 @@ public class MidiEventScheduler extends EventScheduler { /** * Create an event that contains the message. */ - private MidiEvent createScheduledEvent(byte[] msg, int offset, int count, + public MidiEvent createScheduledEvent(byte[] msg, int offset, int count, long timestamp) { MidiEvent event; if (count > POOL_EVENT_SIZE) { diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 8a9445d8554a..28b98d6fab06 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -16,14 +16,10 @@ package com.android.internal.os; -import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL; - -import android.annotation.TestApi; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.ApplicationErrorReport; import android.app.IActivityManager; -import android.app.compat.CompatChanges; import android.compat.annotation.UnsupportedAppUsage; import android.content.type.DefaultMimeMapFactory; import android.net.TrafficStats; @@ -40,7 +36,6 @@ import com.android.internal.logging.AndroidConfig; import dalvik.system.RuntimeHooks; import dalvik.system.VMRuntime; -import dalvik.system.ZipPathValidator; import libcore.content.type.MimeMap; @@ -265,31 +260,10 @@ public class RuntimeInit { */ TrafficStats.attachSocketTagger(); - /* - * Initialize the zip path validator callback depending on the targetSdk. - */ - initZipPathValidatorCallback(); - initialized = true; } /** - * If targetSDK >= U: set the safe zip path validator callback which disallows dangerous zip - * entry names. - * Otherwise: clear the callback to the default validation. - * - * @hide - */ - @TestApi - public static void initZipPathValidatorCallback() { - if (CompatChanges.isChangeEnabled(VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL)) { - ZipPathValidator.setCallback(new SafeZipPathValidatorCallback()); - } else { - ZipPathValidator.clearCallback(); - } - } - - /** * Returns an HTTP user agent of the form * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MAIN)". */ diff --git a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java index cd55d3292217..f2b0544bcfad 100644 --- a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java @@ -259,6 +259,7 @@ public class BaseProtoLogImpl { if (writeToFile) { writeProtoLogToFileLocked(); logAndPrintln(pw, "Log written to " + mLogFile + "."); + mBuffer.resetBuffer(); } if (mProtoLogEnabled) { logAndPrintln(pw, "ERROR: logging was re-enabled while waiting for flush."); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index f72c4c36b6c3..59f6d2b29481 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -170,6 +170,8 @@ public class LockPatternUtils { private static final String LOCK_SCREEN_OWNER_INFO_ENABLED = Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED; + private static final String LOCK_PIN_ENHANCED_PRIVACY = "pin_enhanced_privacy"; + private static final String LOCK_SCREEN_DEVICE_OWNER_INFO = "lockscreen.device_owner_info"; private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents"; @@ -1037,6 +1039,27 @@ public class LockPatternUtils { } /** + * @return Whether enhanced pin privacy is enabled. + */ + public boolean isPinEnhancedPrivacyEnabled(int userId) { + return getBoolean(LOCK_PIN_ENHANCED_PRIVACY, false, userId); + } + + /** + * Set whether enhanced pin privacy is enabled. + */ + public void setPinEnhancedPrivacyEnabled(boolean enabled, int userId) { + setBoolean(LOCK_PIN_ENHANCED_PRIVACY, enabled, userId); + } + + /** + * @return Whether enhanced pin privacy was ever chosen. + */ + public boolean isPinEnhancedPrivacyEverChosen(int userId) { + return getString(LOCK_PIN_ENHANCED_PRIVACY, userId) != null; + } + + /** * Set whether the visible password is enabled for cryptkeeper screen. */ public void setVisiblePasswordEnabled(boolean enabled, int userId) { diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index 939a0e411913..9c6a534c3bbb 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -266,7 +266,8 @@ static jboolean nativeIsDataInjectionEnabled(JNIEnv *_env, jclass _this, jlong s } static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager, - jlong size, jint channelType, jint fd, jobject hardwareBufferObj) { + jint deviceId, jlong size, jint channelType, jint fd, + jobject hardwareBufferObj) { const native_handle_t *nativeHandle = nullptr; NATIVE_HANDLE_DECLARE_STORAGE(ashmemHandle, 1, 0); @@ -287,7 +288,7 @@ static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorMa } SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager); - return mgr->createDirectChannel(size, channelType, nativeHandle); + return mgr->createDirectChannel(deviceId, size, channelType, nativeHandle); } static void nativeDestroyDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager, @@ -532,7 +533,7 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = { {"nativeIsDataInjectionEnabled", "(J)Z", (void *)nativeIsDataInjectionEnabled}, - {"nativeCreateDirectChannel", "(JJIILandroid/hardware/HardwareBuffer;)I", + {"nativeCreateDirectChannel", "(JIJIILandroid/hardware/HardwareBuffer;)I", (void *)nativeCreateDirectChannel}, {"nativeDestroyDirectChannel", "(JI)V", (void *)nativeDestroyDirectChannel}, diff --git a/core/proto/android/nfc/Android.bp b/core/proto/android/nfc/Android.bp new file mode 100644 index 000000000000..6a62c917f240 --- /dev/null +++ b/core/proto/android/nfc/Android.bp @@ -0,0 +1,43 @@ +// +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +filegroup { + name: "srcs_nfc_proto", + srcs: [ + "*.proto", + ], +} + +// Will be statically linked by `framework-nfc`. +java_library { + name: "nfc-proto-java-gen", + installable: false, + proto: { + type: "stream", + include_dirs: [ + "external/protobuf/src", + ], + }, + srcs: [ + ":srcs_nfc_proto", + ], + sdk_version: "current", + min_sdk_version: "current", +} diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto index e029af4f9819..f87d9109e7f8 100644 --- a/core/proto/android/service/notification.proto +++ b/core/proto/android/service/notification.proto @@ -285,22 +285,24 @@ message NotificationRemoteViewsProto { repeated PackageRemoteViewInfoProto package_remote_view_info = 1; } +// Enum used in DNDModeProto to specify the zen mode setting. +enum LoggedZenMode { + ROOT_CONFIG = -1; // Used to distinguish config (one per user) from the rules. + OFF = 0; + IMPORTANT_INTERRUPTIONS = 1; + NO_INTERRUPTIONS = 2; + ALARMS = 3; +} + /** * Atom that represents an item in the list of Do Not Disturb rules, pulled from * NotificationManagerService.java. */ message DNDModeProto { - enum Mode { - ROOT_CONFIG = -1; // Used to distinguish the config (one per user) from the rules. - ZEN_MODE_OFF = 0; - ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1; - ZEN_MODE_NO_INTERRUPTIONS = 2; - ZEN_MODE_ALARMS = 3; - } optional int32 user = 1; // Android user ID (0, 1, 10, ...) optional bool enabled = 2; // true for ROOT_CONFIG if a manualRule is enabled optional bool channels_bypassing = 3; // only valid for ROOT_CONFIG - optional Mode zen_mode = 4; + optional LoggedZenMode zen_mode = 4; // id is one of the system default rule IDs, or empty // May also be "MANUAL_RULE" to indicate app-activation of the manual rule. optional string id = 5; @@ -308,15 +310,34 @@ message DNDModeProto { optional DNDPolicyProto policy = 7; } +// Enum used in DNDPolicyProto for a particular policy parameter's state. +enum State { + STATE_UNSET = 0; + STATE_ALLOW = 1; + STATE_DISALLOW = 2; +} + +// Enum used in DNDPolicyProto for which people are allowed to break through. +enum PeopleType { + PEOPLE_UNSET = 0; + PEOPLE_ANYONE = 1; + PEOPLE_CONTACTS = 2; + PEOPLE_STARRED = 3; + PEOPLE_NONE = 4; +} + +// Enum used in DNDPolicyProto for conversation types allowed to break through. +enum ConversationType { + CONV_UNSET = 0; + CONV_ANYONE = 1; + CONV_IMPORTANT = 2; + CONV_NONE = 3; +} + /** - * Atom that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto. + * Message that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto. */ message DNDPolicyProto { - enum State { - STATE_UNSET = 0; - STATE_ALLOW = 1; - STATE_DISALLOW = 2; - } optional State calls = 1; optional State repeat_callers = 2; optional State messages = 3; @@ -334,23 +355,8 @@ message DNDPolicyProto { optional State ambient = 15; optional State notification_list = 16; - enum PeopleType { - PEOPLE_UNSET = 0; - PEOPLE_ANYONE = 1; - PEOPLE_CONTACTS = 2; - PEOPLE_STARRED = 3; - PEOPLE_NONE = 4; - } - optional PeopleType allow_calls_from = 17; optional PeopleType allow_messages_from = 18; - enum ConversationType { - CONV_UNSET = 0; - CONV_ANYONE = 1; - CONV_IMPORTANT = 2; - CONV_NONE = 3; - } - optional ConversationType allow_conversations_from = 19; } diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 76b39bb5c180..70464d830c0b 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -207,12 +207,9 @@ <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Aplikazio pertsonalak blokeatuta egongo dira laneko profila aktibatzen duzun arte"</string> <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"Aplikazio pertsonalak egun eta ordu honetan blokeatuko dira: <xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>. IKT saileko administratzaileak ez dizu ematen baimenik laneko profila <xliff:g id="NUMBER">%3$d</xliff:g> egunez baino gehiagoz desaktibatuta edukitzeko."</string> <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"Aktibatu"</string> - <!-- no translation found for work_profile_telephony_paused_title (7690804479291839519) --> - <skip /> - <!-- no translation found for work_profile_telephony_paused_text (8065762301100978221) --> - <skip /> - <!-- no translation found for work_profile_telephony_paused_turn_on_button (7542632318337068821) --> - <skip /> + <string name="work_profile_telephony_paused_title" msgid="7690804479291839519">"Deiak eta mezuak desaktibatuta daude"</string> + <string name="work_profile_telephony_paused_text" msgid="8065762301100978221">"Laneko aplikazioak pausatu dituzu. Ez duzu jasoko telefono-deirik edo testu-mezurik."</string> + <string name="work_profile_telephony_paused_turn_on_button" msgid="7542632318337068821">"Berraktibatu laneko aplikazioak"</string> <string name="me" msgid="6207584824693813140">"Ni"</string> <string name="power_dialog" product="tablet" msgid="8333207765671417261">"Tabletaren aukerak"</string> <string name="power_dialog" product="tv" msgid="7792839006640933763">"Android TV gailuaren aukerak"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index dfb0b2e9f3a3..562560e00b11 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -296,7 +296,7 @@ <string name="safeMode" msgid="8974401416068943888">"حالت ایمن"</string> <string name="android_system_label" msgid="5974767339591067210">"سیستم Android"</string> <string name="user_owner_label" msgid="8628726904184471211">"جابهجا شدن به نمایه شخصی"</string> - <string name="managed_profile_label" msgid="7316778766973512382">"جابهجا شدن به نمایه کاری"</string> + <string name="managed_profile_label" msgid="7316778766973512382">"رفتن به نمایه کاری"</string> <string name="permgrouplab_contacts" msgid="4254143639307316920">"مخاطبین"</string> <string name="permgroupdesc_contacts" msgid="9163927941244182567">"دسترسی به مخاطبین شما"</string> <string name="permgrouplab_location" msgid="1858277002233964394">"مکان"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 0c4dfaec5c82..b8a364b1fbd4 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -208,12 +208,9 @@ <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Vos applications personnelles sont bloquées jusqu\'à ce que vous activiez votre profil professionnel"</string> <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"Les applications personnelles seront bloquées le <xliff:g id="DATE">%1$s</xliff:g> à <xliff:g id="TIME">%2$s</xliff:g>. Votre administrateur informatique ne vous autorise pas à laisser votre profil professionnel désactivé pendant plus de <xliff:g id="NUMBER">%3$d</xliff:g> jours."</string> <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"Activer"</string> - <!-- no translation found for work_profile_telephony_paused_title (7690804479291839519) --> - <skip /> - <!-- no translation found for work_profile_telephony_paused_text (8065762301100978221) --> - <skip /> - <!-- no translation found for work_profile_telephony_paused_turn_on_button (7542632318337068821) --> - <skip /> + <string name="work_profile_telephony_paused_title" msgid="7690804479291839519">"Les appels et messages sont désactivés"</string> + <string name="work_profile_telephony_paused_text" msgid="8065762301100978221">"Vous avez mis en pause les applications professionnelles. Vous ne recevrez aucun appel téléphonique ni message texte."</string> + <string name="work_profile_telephony_paused_turn_on_button" msgid="7542632318337068821">"Réact. applis prof."</string> <string name="me" msgid="6207584824693813140">"Moi"</string> <string name="power_dialog" product="tablet" msgid="8333207765671417261">"Options de la tablette"</string> <string name="power_dialog" product="tv" msgid="7792839006640933763">"Options d\'Android TV"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 706e92076ceb..7169464a9eac 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -1960,7 +1960,7 @@ <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g>՝ անհասանելի է"</string> <string name="app_streaming_blocked_title_for_permission_dialog" msgid="4483161748582966785">"Անհրաժեշտ է թույլտվություն"</string> <string name="app_streaming_blocked_title_for_camera_dialog" msgid="3935701653713853065">"Տեսախցիկն անհասանելի է"</string> - <string name="app_streaming_blocked_title_for_fingerprint_dialog" msgid="3516853717714141951">"Շարունակեք հեռախոսով"</string> + <string name="app_streaming_blocked_title_for_fingerprint_dialog" msgid="3516853717714141951">"Շարունակեք հեռախոսով"</string> <string name="app_streaming_blocked_title_for_microphone_dialog" msgid="544822455127171206">"Խոսափողն անհասանելի է"</string> <string name="app_streaming_blocked_title_for_playstore_dialog" msgid="8149823099822897538">"Play Խանութը հասանելի չէ"</string> <string name="app_streaming_blocked_title_for_settings_dialog" product="tv" msgid="196994247017450357">"Android TV-ի կարգավորումներն անհասանելի են"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index bd29ffb10129..a5878646264d 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1985,7 +1985,7 @@ <string name="profile_encrypted_message" msgid="1128512616293157802">"Tocca per sbloc. prof. di lav."</string> <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Connesso a <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string> <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Tocca per visualizzare i file"</string> - <string name="pin_target" msgid="8036028973110156895">"Blocca"</string> + <string name="pin_target" msgid="8036028973110156895">"Fissa"</string> <string name="pin_specific_target" msgid="7824671240625957415">"Blocca <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="unpin_target" msgid="3963318576590204447">"Sgancia"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"Sblocca <xliff:g id="LABEL">%1$s</xliff:g>"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 8064eb04949e..348dcd502a97 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -207,12 +207,9 @@ <string name="personal_apps_suspension_text" msgid="6115455688932935597">"직장 프로필을 사용 설정할 때까지 개인 앱이 차단됩니다."</string> <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"개인 앱이 <xliff:g id="DATE">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g>에 차단됩니다. IT 관리자가 <xliff:g id="NUMBER">%3$d</xliff:g>일 넘게 직장 프로필을 중지하도록 허용하지 않습니다."</string> <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"사용 설정"</string> - <!-- no translation found for work_profile_telephony_paused_title (7690804479291839519) --> - <skip /> - <!-- no translation found for work_profile_telephony_paused_text (8065762301100978221) --> - <skip /> - <!-- no translation found for work_profile_telephony_paused_turn_on_button (7542632318337068821) --> - <skip /> + <string name="work_profile_telephony_paused_title" msgid="7690804479291839519">"전화 및 메시지 사용 중지됨"</string> + <string name="work_profile_telephony_paused_text" msgid="8065762301100978221">"직장 앱을 일시중지했습니다. 전화나 문자 메시지를 수신하지 않습니다."</string> + <string name="work_profile_telephony_paused_turn_on_button" msgid="7542632318337068821">"직장 앱 일시중지 해제"</string> <string name="me" msgid="6207584824693813140">"나"</string> <string name="power_dialog" product="tablet" msgid="8333207765671417261">"태블릿 옵션"</string> <string name="power_dialog" product="tv" msgid="7792839006640933763">"Android TV 옵션"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 3888e0c527c0..047d126aaaf7 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1172,8 +1172,8 @@ <string name="no" msgid="5122037903299899715">"Отмена"</string> <string name="dialog_alert_title" msgid="651856561974090712">"Внимание!"</string> <string name="loading" msgid="3138021523725055037">"Загрузка…"</string> - <string name="capital_on" msgid="2770685323900821829">"I"</string> - <string name="capital_off" msgid="7443704171014626777">"O"</string> + <string name="capital_on" msgid="2770685323900821829">"Включено"</string> + <string name="capital_off" msgid="7443704171014626777">"Выключено"</string> <string name="checked" msgid="9179896827054513119">"отмечено"</string> <string name="not_checked" msgid="7972320087569023342">"не отмечено"</string> <string name="selected" msgid="6614607926197755875">"выбрано"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 209c78551947..733e0eacc258 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1158,14 +1158,25 @@ <integer name="config_triplePressOnStemPrimaryBehavior">0</integer> <!-- Control the behavior when the user short presses the stem primary button. - Stem primary button is only used on watch form factor. If a device is not - a watch, setting this config is no-op. - 0 - Nothing - 1 - Go to launch all apps + Stem primary button is only used on watch form factor. If a device is not + a watch, setting this config is no-op. + 0 - Nothing + 1 - Go to launch all apps --> <integer name="config_shortPressOnStemPrimaryBehavior">0</integer> + <!-- Control the behavior of the search key. + 0 - Launch default search activity + 1 - Launch target activity defined by config_searchKeyTargetActivity + --> + <integer name="config_searchKeyBehavior">0</integer> + + <!-- Component name for the default target activity to be launched when user + presses the global search key. [DO NOT TRANSLATE] + --> + <string name="config_searchKeyTargetActivity" translatable="false"></string> + <!-- Time to wait while a button is pressed before triggering a very long press. --> <integer name="config_veryLongPressTimeout">3500</integer> @@ -2787,8 +2798,10 @@ <integer name="config_userTypePackageWhitelistMode">13</integer> <!-- 1+4+8 --> <!-- Whether the main user is a permanent admin user. If the main user is a permanent admin user - it can't be deleted or downgraded to non-admin status. --> - <bool name="config_isMainUserPermanentAdmin">false</bool> + it can't be deleted or downgraded to non-admin status. + This is generally only relevant on headless system user mode devices; on other devices, the + main user is the system user which is always a permanent admin anyway. --> + <bool name="config_isMainUserPermanentAdmin">true</bool> <!-- Whether switch to headless system user is allowed. If allowed, headless system user can run in the foreground even though it is not a full user. --> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 24a7d8588d35..973b3a76c956 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -110,6 +110,10 @@ <string name="config_satellite_service_package" translatable="false"></string> <java-symbol type="string" name="config_satellite_service_package" /> + <!-- Telephony pointing UI package name to be launched. --> + <string name="config_pointing_ui_package" translatable="false"></string> + <java-symbol type="string" name="config_pointing_ui_package" /> + <!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks will not perform handover if the target transport is out of service, or VoPS not supported. The network will be torn down on the source transport, and will be diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 8d56e7a163a7..e2b46d017288 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -464,6 +464,8 @@ <java-symbol type="integer" name="config_doublePressOnStemPrimaryBehavior" /> <java-symbol type="integer" name="config_triplePressOnStemPrimaryBehavior" /> <java-symbol type="string" name="config_doublePressOnPowerTargetActivity" /> + <java-symbol type="integer" name="config_searchKeyBehavior" /> + <java-symbol type="string" name="config_searchKeyTargetActivity" /> <java-symbol type="integer" name="config_windowOutsetBottom" /> <java-symbol type="integer" name="db_connection_pool_size" /> <java-symbol type="integer" name="db_journal_size_limit" /> diff --git a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java index f97099d04572..16ed3ef42da3 100644 --- a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java +++ b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java @@ -17,9 +17,15 @@ package android.companion.virtual.sensor; import static android.hardware.Sensor.TYPE_ACCELEROMETER; +import static android.hardware.SensorDirectChannel.RATE_STOP; +import static android.hardware.SensorDirectChannel.RATE_VERY_FAST; +import static android.hardware.SensorDirectChannel.TYPE_HARDWARE_BUFFER; +import static android.hardware.SensorDirectChannel.TYPE_MEMORY_FILE; import static com.google.common.truth.Truth.assertThat; +import static org.testng.Assert.assertThrows; + import android.os.Parcel; import android.platform.test.annotations.Presubmit; @@ -40,6 +46,8 @@ public class VirtualSensorConfigTest { final VirtualSensorConfig originalConfig = new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME) .setVendor(SENSOR_VENDOR) + .setHighestDirectReportRateLevel(RATE_VERY_FAST) + .setDirectChannelTypesSupported(TYPE_MEMORY_FILE) .build(); final Parcel parcel = Parcel.obtain(); originalConfig.writeToParcel(parcel, /* flags= */ 0); @@ -49,6 +57,39 @@ public class VirtualSensorConfigTest { assertThat(recreatedConfig.getType()).isEqualTo(originalConfig.getType()); assertThat(recreatedConfig.getName()).isEqualTo(originalConfig.getName()); assertThat(recreatedConfig.getVendor()).isEqualTo(originalConfig.getVendor()); + assertThat(recreatedConfig.getHighestDirectReportRateLevel()).isEqualTo(RATE_VERY_FAST); + assertThat(recreatedConfig.getDirectChannelTypesSupported()).isEqualTo(TYPE_MEMORY_FILE); + // From hardware/libhardware/include/hardware/sensors-base.h: + // 0x400 is SENSOR_FLAG_DIRECT_CHANNEL_ASHMEM (i.e. TYPE_MEMORY_FILE) + // 0x800 is SENSOR_FLAG_DIRECT_CHANNEL_GRALLOC (i.e. TYPE_HARDWARE_BUFFER) + // 7 is SENSOR_FLAG_SHIFT_DIRECT_REPORT + assertThat(recreatedConfig.getFlags()).isEqualTo(0x400 | RATE_VERY_FAST << 7); + } + + @Test + public void hardwareBufferDirectChannelTypeSupported_throwsException() { + assertThrows( + IllegalArgumentException.class, + () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME) + .setDirectChannelTypesSupported(TYPE_HARDWARE_BUFFER | TYPE_MEMORY_FILE)); + } + + @Test + public void directChannelTypeSupported_missingHighestReportRateLevel_throwsException() { + assertThrows( + IllegalArgumentException.class, + () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME) + .setDirectChannelTypesSupported(TYPE_MEMORY_FILE) + .build()); + } + + @Test + public void directChannelTypeSupported_missingDirectChannelTypeSupported_throwsException() { + assertThrows( + IllegalArgumentException.class, + () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME) + .setHighestDirectReportRateLevel(RATE_VERY_FAST) + .build()); } @Test @@ -56,5 +97,8 @@ public class VirtualSensorConfigTest { final VirtualSensorConfig config = new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME).build(); assertThat(config.getVendor()).isNull(); + assertThat(config.getHighestDirectReportRateLevel()).isEqualTo(RATE_STOP); + assertThat(config.getDirectChannelTypesSupported()).isEqualTo(0); + assertThat(config.getFlags()).isEqualTo(0); } } diff --git a/core/tests/coretests/src/android/content/pm/UserPackageTest.java b/core/tests/coretests/src/android/content/pm/UserPackageTest.java new file mode 100644 index 000000000000..5114e2cf9327 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/UserPackageTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.platform.test.annotations.Presubmit; + +import junit.framework.TestCase; + +@Presubmit +public class UserPackageTest extends TestCase { + public void testCacheLimit() { + UserPackage.setValidUserIds(new int[]{0}); + for (int i = 0; i < UserPackage.MAX_NUM_CACHED_ENTRIES_PER_USER; ++i) { + UserPackage.of(0, "app" + i); + assertEquals(i + 1, UserPackage.numEntriesForUser(0)); + } + + for (int i = 0; i < UserPackage.MAX_NUM_CACHED_ENTRIES_PER_USER; ++i) { + UserPackage.of(0, "appOverLimit" + i); + final int numCached = UserPackage.numEntriesForUser(0); + assertTrue(numCached >= 1); + assertTrue(numCached <= UserPackage.MAX_NUM_CACHED_ENTRIES_PER_USER); + } + } +} diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt index ef106bc7cb73..ee1b2aa9a259 100644 --- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt +++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt @@ -43,14 +43,56 @@ class FontScaleConverterFactoryTest { assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) } - @SmallTest - fun missingLookupTableReturnsNull() { - assertThat(FontScaleConverterFactory.forScale(3F)).isNull() + @LargeTest + @Test + fun missingLookupTablePastEnd_returnsLinear() { + val table = FontScaleConverterFactory.forScale(3F)!! + generateSequenceOfFractions(-10000f..10000f, step = 0.01f) + .map { + assertThat(table.convertSpToDp(it)).isWithin(CONVERSION_TOLERANCE).of(it * 3f) + } + assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(3f) + assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(24f) + assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(30f) + assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(15f) + assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) + assertThat(table.convertSpToDp(50F)).isWithin(CONVERSION_TOLERANCE).of(150f) + assertThat(table.convertSpToDp(100F)).isWithin(CONVERSION_TOLERANCE).of(300f) } @SmallTest - fun missingLookupTable105ReturnsNull() { - assertThat(FontScaleConverterFactory.forScale(1.05F)).isNull() + fun missingLookupTable110_returnsInterpolated() { + val table = FontScaleConverterFactory.forScale(1.1F)!! + + assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(1.1f) + assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(8f * 1.1f) + assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(11f) + assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(5f * 1.1f) + assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) + assertThat(table.convertSpToDp(50F)).isLessThan(50f * 1.1f) + assertThat(table.convertSpToDp(100F)).isLessThan(100f * 1.1f) + } + + @Test + fun missingLookupTable199_returnsInterpolated() { + val table = FontScaleConverterFactory.forScale(1.9999F)!! + assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(2f) + assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(16f) + assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(20f) + assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(10f) + assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) + } + + @Test + fun missingLookupTable160_returnsInterpolated() { + val table = FontScaleConverterFactory.forScale(1.6F)!! + assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(1f * 1.6F) + assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(8f * 1.6F) + assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(10f * 1.6F) + assertThat(table.convertSpToDp(20F)).isLessThan(20f * 1.6F) + assertThat(table.convertSpToDp(100F)).isLessThan(100f * 1.6F) + assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(5f * 1.6F) + assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) } @SmallTest @@ -83,7 +125,7 @@ class FontScaleConverterFactoryTest { @Test fun allFeasibleScalesAndConversionsDoNotCrash() { generateSequenceOfFractions(-10f..10f, step = 0.01f) - .mapNotNull{ FontScaleConverterFactory.forScale(it) } + .mapNotNull{ FontScaleConverterFactory.forScale(it) }!! .flatMap{ table -> generateSequenceOfFractions(-2000f..2000f, step = 0.01f) .map{ Pair(table, it) } diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java index 2e31bb581fbc..b6fc137471a4 100644 --- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java +++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java @@ -32,16 +32,18 @@ import android.platform.test.annotations.Presubmit; import android.test.mock.MockContentResolver; import android.util.MemoryIntArray; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -59,42 +61,63 @@ public class NameValueCacheTest { private static final String NAMESPACE = "namespace"; private static final String NAMESPACE2 = "namespace2"; + private static final String SETTING = "test_setting"; + private static final String SETTING2 = "test_setting2"; + @Mock private IContentProvider mMockIContentProvider; @Mock private ContentProvider mMockContentProvider; private MockContentResolver mMockContentResolver; - private MemoryIntArray mCacheGenerationStore; - private int mCurrentGeneration = 123; + private MemoryIntArray mConfigsCacheGenerationStore; + private MemoryIntArray mSettingsCacheGenerationStore; + + private HashMap<String, HashMap<String, String>> mConfigsStorage; + private HashMap<String, String> mSettingsStorage; - private HashMap<String, HashMap<String, String>> mStorage; @Before public void setUp() throws Exception { Settings.Config.clearProviderForTest(); + Settings.Secure.clearProviderForTest(); MockitoAnnotations.initMocks(this); when(mMockContentProvider.getIContentProvider()).thenReturn(mMockIContentProvider); - mMockContentResolver = new MockContentResolver(InstrumentationRegistry - .getInstrumentation().getContext()); + mMockContentResolver = new MockContentResolver( + InstrumentationRegistry.getInstrumentation().getContext()); mMockContentResolver.addProvider(Settings.Config.CONTENT_URI.getAuthority(), mMockContentProvider); - mCacheGenerationStore = new MemoryIntArray(1); - mStorage = new HashMap<>(); + mMockContentResolver.addProvider(Settings.Secure.CONTENT_URI.getAuthority(), + mMockContentProvider); + mConfigsCacheGenerationStore = new MemoryIntArray(2); + mConfigsCacheGenerationStore.set(0, 123); + mConfigsCacheGenerationStore.set(1, 456); + mSettingsCacheGenerationStore = new MemoryIntArray(2); + mSettingsCacheGenerationStore.set(0, 234); + mSettingsCacheGenerationStore.set(1, 567); + mConfigsStorage = new HashMap<>(); + mSettingsStorage = new HashMap<>(); // Stores keyValues for a given prefix and increments the generation. (Note that this // increments the generation no matter what, it doesn't pay attention to if anything // actually changed). when(mMockIContentProvider.call(any(), eq(Settings.Config.CONTENT_URI.getAuthority()), - eq(Settings.CALL_METHOD_SET_ALL_CONFIG), - any(), any(Bundle.class))).thenAnswer(invocationOnMock -> { + eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class))).thenAnswer( + invocationOnMock -> { Bundle incomingBundle = invocationOnMock.getArgument(4); HashMap<String, String> keyValues = (HashMap<String, String>) incomingBundle.getSerializable( - Settings.CALL_METHOD_FLAGS_KEY); + Settings.CALL_METHOD_FLAGS_KEY, HashMap.class); String prefix = incomingBundle.getString(Settings.CALL_METHOD_PREFIX_KEY); - mStorage.put(prefix, keyValues); - mCacheGenerationStore.set(0, ++mCurrentGeneration); - + mConfigsStorage.put(prefix, keyValues); + int currentGeneration; + // Different prefixes have different generation codes + if (prefix.equals(NAMESPACE + "/")) { + currentGeneration = mConfigsCacheGenerationStore.get(0); + mConfigsCacheGenerationStore.set(0, ++currentGeneration); + } else if (prefix.equals(NAMESPACE2 + "/")) { + currentGeneration = mConfigsCacheGenerationStore.get(1); + mConfigsCacheGenerationStore.set(1, ++currentGeneration); + } Bundle result = new Bundle(); result.putInt(Settings.KEY_CONFIG_SET_ALL_RETURN, Settings.SET_ALL_RESULT_SUCCESS); @@ -102,49 +125,101 @@ public class NameValueCacheTest { }); // Returns the keyValues corresponding to a namespace, or an empty map if the namespace - // doesn't have anything stored for it. Returns the generation key if the caller asked - // for one. + // doesn't have anything stored for it. Returns the generation key if the map isn't empty + // and the caller asked for the generation key. when(mMockIContentProvider.call(any(), eq(Settings.Config.CONTENT_URI.getAuthority()), - eq(Settings.CALL_METHOD_LIST_CONFIG), - any(), any(Bundle.class))).thenAnswer(invocationOnMock -> { + eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class))).thenAnswer( + invocationOnMock -> { Bundle incomingBundle = invocationOnMock.getArgument(4); String prefix = incomingBundle.getString(Settings.CALL_METHOD_PREFIX_KEY); - if (!mStorage.containsKey(prefix)) { - mStorage.put(prefix, new HashMap<>()); + if (!mConfigsStorage.containsKey(prefix)) { + mConfigsStorage.put(prefix, new HashMap<>()); } - HashMap<String, String> keyValues = mStorage.get(prefix); + HashMap<String, String> keyValues = mConfigsStorage.get(prefix); Bundle bundle = new Bundle(); bundle.putSerializable(Settings.NameValueTable.VALUE, keyValues); - if (incomingBundle.containsKey(Settings.CALL_METHOD_TRACK_GENERATION_KEY)) { + if (!keyValues.isEmpty() && incomingBundle.containsKey( + Settings.CALL_METHOD_TRACK_GENERATION_KEY)) { + int index = prefix.equals(NAMESPACE + "/") ? 0 : 1; + bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, + mConfigsCacheGenerationStore); + bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index); + bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY, + mConfigsCacheGenerationStore.get(index)); + } + return bundle; + }); + + // Stores value for a given setting's name and increments the generation. (Note that this + // increments the generation no matter what, it doesn't pay attention to if anything + // actually changed). + when(mMockIContentProvider.call(any(), eq(Settings.Secure.CONTENT_URI.getAuthority()), + eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class))).thenAnswer( + invocationOnMock -> { + Bundle incomingBundle = invocationOnMock.getArgument(4); + String key = invocationOnMock.getArgument(3); + String value = incomingBundle.getString(Settings.NameValueTable.VALUE); + mSettingsStorage.put(key, value); + int currentGeneration; + // Different settings have different generation codes + if (key.equals(SETTING)) { + currentGeneration = mSettingsCacheGenerationStore.get(0); + mSettingsCacheGenerationStore.set(0, ++currentGeneration); + } else if (key.equals(SETTING2)) { + currentGeneration = mSettingsCacheGenerationStore.get(1); + mSettingsCacheGenerationStore.set(1, ++currentGeneration); + } + return null; + }); + + // Returns the value corresponding to a setting, or null if the setting + // doesn't have a value stored for it. Returns the generation key if the value isn't null + // and the caller asked for the generation key. + when(mMockIContentProvider.call(any(), eq(Settings.Secure.CONTENT_URI.getAuthority()), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class))).thenAnswer( + invocationOnMock -> { + Bundle incomingBundle = invocationOnMock.getArgument(4); + String key = invocationOnMock.getArgument(3); + String value = mSettingsStorage.get(key); + + Bundle bundle = new Bundle(); + bundle.putSerializable(Settings.NameValueTable.VALUE, value); + + if (value != null && incomingBundle.containsKey( + Settings.CALL_METHOD_TRACK_GENERATION_KEY)) { + int index = key.equals(SETTING) ? 0 : 1; bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, - mCacheGenerationStore); - bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, 0); + mSettingsCacheGenerationStore); + bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index); bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY, - mCacheGenerationStore.get(0)); + mSettingsCacheGenerationStore.get(index)); } return bundle; }); } + @After + public void cleanUp() throws IOException { + mConfigsStorage.clear(); + mSettingsStorage.clear(); + } + @Test public void testCaching_singleNamespace() throws Exception { HashMap<String, String> keyValues = new HashMap<>(); keyValues.put("a", "b"); Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues); - verify(mMockIContentProvider).call(any(), any(), - eq(Settings.CALL_METHOD_SET_ALL_CONFIG), - any(), any(Bundle.class)); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class)); Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver, - NAMESPACE, - Collections.emptyList()); - verify(mMockIContentProvider).call(any(), any(), - eq(Settings.CALL_METHOD_LIST_CONFIG), - any(), any(Bundle.class)); + NAMESPACE, Collections.emptyList()); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class)); assertThat(returnedValues).containsExactlyEntriesIn(keyValues); Map<String, String> cachedKeyValues = Settings.Config.getStrings(mMockContentResolver, @@ -156,14 +231,12 @@ public class NameValueCacheTest { keyValues.put("a", "c"); Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues); verify(mMockIContentProvider, times(2)).call(any(), any(), - eq(Settings.CALL_METHOD_SET_ALL_CONFIG), - any(), any(Bundle.class)); + eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class)); Map<String, String> returnedValues2 = Settings.Config.getStrings(mMockContentResolver, NAMESPACE, Collections.emptyList()); verify(mMockIContentProvider, times(2)).call(any(), any(), - eq(Settings.CALL_METHOD_LIST_CONFIG), - any(), any(Bundle.class)); + eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class)); assertThat(returnedValues2).containsExactlyEntriesIn(keyValues); Map<String, String> cachedKeyValues2 = Settings.Config.getStrings(mMockContentResolver, @@ -177,36 +250,31 @@ public class NameValueCacheTest { HashMap<String, String> keyValues = new HashMap<>(); keyValues.put("a", "b"); Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues); - verify(mMockIContentProvider).call(any(), any(), - eq(Settings.CALL_METHOD_SET_ALL_CONFIG), + verify(mMockIContentProvider).call(any(), any(), eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class)); + Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver, + NAMESPACE, Collections.emptyList()); + verify(mMockIContentProvider).call(any(), any(), eq(Settings.CALL_METHOD_LIST_CONFIG), + any(), any(Bundle.class)); + assertThat(returnedValues).containsExactlyEntriesIn(keyValues); + HashMap<String, String> keyValues2 = new HashMap<>(); keyValues2.put("c", "d"); keyValues2.put("e", "f"); Settings.Config.setStrings(mMockContentResolver, NAMESPACE2, keyValues2); verify(mMockIContentProvider, times(2)).call(any(), any(), - eq(Settings.CALL_METHOD_SET_ALL_CONFIG), - any(), any(Bundle.class)); - - Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver, - NAMESPACE, - Collections.emptyList()); - verify(mMockIContentProvider).call(any(), any(), - eq(Settings.CALL_METHOD_LIST_CONFIG), - any(), any(Bundle.class)); - assertThat(returnedValues).containsExactlyEntriesIn(keyValues); + eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class)); Map<String, String> returnedValues2 = Settings.Config.getStrings(mMockContentResolver, - NAMESPACE2, - Collections.emptyList()); + NAMESPACE2, Collections.emptyList()); verify(mMockIContentProvider, times(2)).call(any(), any(), - eq(Settings.CALL_METHOD_LIST_CONFIG), - any(), any(Bundle.class)); + eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class)); assertThat(returnedValues2).containsExactlyEntriesIn(keyValues2); Map<String, String> cachedKeyValues = Settings.Config.getStrings(mMockContentResolver, NAMESPACE, Collections.emptyList()); + // Modifying the second namespace doesn't affect the cache of the first namespace verifyNoMoreInteractions(mMockIContentProvider); assertThat(cachedKeyValues).containsExactlyEntriesIn(keyValues); @@ -219,17 +287,90 @@ public class NameValueCacheTest { @Test public void testCaching_emptyNamespace() throws Exception { Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver, - NAMESPACE, - Collections.emptyList()); - verify(mMockIContentProvider).call(any(), any(), - eq(Settings.CALL_METHOD_LIST_CONFIG), - any(), any(Bundle.class)); + NAMESPACE, Collections.emptyList()); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class)); assertThat(returnedValues).isEmpty(); Map<String, String> cachedKeyValues = Settings.Config.getStrings(mMockContentResolver, NAMESPACE, Collections.emptyList()); - verifyNoMoreInteractions(mMockIContentProvider); + // Empty list won't be cached + verify(mMockIContentProvider, times(2)).call(any(), any(), + eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class)); assertThat(cachedKeyValues).isEmpty(); } + @Test + public void testCaching_singleSetting() throws Exception { + Settings.Secure.putString(mMockContentResolver, SETTING, "a"); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class)); + + String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(returnedValue).isEqualTo("a"); + + String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + verifyNoMoreInteractions(mMockIContentProvider); + assertThat(cachedValue).isEqualTo("a"); + + // Modify the value to invalidate the cache. + Settings.Secure.putString(mMockContentResolver, SETTING, "b"); + verify(mMockIContentProvider, times(2)).call(any(), any(), + eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class)); + + String returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING); + verify(mMockIContentProvider, times(2)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(returnedValue2).isEqualTo("b"); + + String cachedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING); + verifyNoMoreInteractions(mMockIContentProvider); + assertThat(cachedValue2).isEqualTo("b"); + } + + @Test + public void testCaching_multipleSettings() throws Exception { + Settings.Secure.putString(mMockContentResolver, SETTING, "a"); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class)); + + String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(returnedValue).isEqualTo("a"); + + Settings.Secure.putString(mMockContentResolver, SETTING2, "b"); + verify(mMockIContentProvider, times(2)).call(any(), any(), + eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class)); + + String returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2); + verify(mMockIContentProvider, times(2)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(returnedValue2).isEqualTo("b"); + + String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + // Modifying the second setting doesn't affect the cache of the first setting + verifyNoMoreInteractions(mMockIContentProvider); + assertThat(cachedValue).isEqualTo("a"); + + String cachedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2); + verifyNoMoreInteractions(mMockIContentProvider); + assertThat(cachedValue2).isEqualTo("b"); + } + + @Test + public void testCaching_nullSetting() throws Exception { + String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(returnedValue).isNull(); + + String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + // Empty list won't be cached + verify(mMockIContentProvider, times(2)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(cachedValue).isNull(); + } } diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index ca1367a710ec..06920524acfc 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -248,17 +248,22 @@ public class InsetsControllerTest { @Test public void testSystemDrivenInsetsAnimationLoggingListener_onReady() { + var loggingListener = mock(WindowInsetsAnimationControlListener.class); + prepareControls(); // only the original thread that created view hierarchy can touch its views InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { - WindowInsetsAnimationControlListener loggingListener = - mock(WindowInsetsAnimationControlListener.class); mController.setSystemDrivenInsetsAnimationLoggingListener(loggingListener); mController.getSourceConsumer(mImeSource).onWindowFocusGained(true); // since there is no focused view, forcefully make IME visible. mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */); - verify(loggingListener).onReady(notNull(), anyInt()); + // When using the animation thread, this must not invoke onReady() + mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw(); }); + // Wait for onReady() being dispatched on the animation thread. + InsetsAnimationThread.get().getThreadHandler().runWithScissors(() -> {}, 500); + + verify(loggingListener).onReady(notNull(), anyInt()); } @Test diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java index 1db6587e1283..6fa8f1117343 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java @@ -19,6 +19,7 @@ package android.view; import static android.view.WindowInsets.Type.FIRST; import static android.view.WindowInsets.Type.LAST; import static android.view.WindowInsets.Type.SIZE; +import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.navigationBars; @@ -51,11 +52,13 @@ public class InsetsSourceTest { private final InsetsSource mSource = new InsetsSource(0 /* id */, navigationBars()); private final InsetsSource mImeSource = new InsetsSource(1 /* id */, ime()); + private final InsetsSource mCaptionSource = new InsetsSource(2 /* id */, captionBar()); @Before public void setUp() { mSource.setVisible(true); mImeSource.setVisible(true); + mCaptionSource.setVisible(true); } @Test @@ -107,6 +110,17 @@ public class InsetsSourceTest { } @Test + public void testCalculateInsets_caption_resizing() { + mCaptionSource.setFrame(new Rect(0, 0, 100, 100)); + Insets insets = mCaptionSource.calculateInsets(new Rect(0, 0, 200, 200), false); + assertEquals(Insets.of(0, 100, 0, 0), insets); + insets = mCaptionSource.calculateInsets(new Rect(0, 0, 50, 200), false); + assertEquals(Insets.of(0, 100, 0, 0), insets); + insets = mCaptionSource.calculateInsets(new Rect(100, 100, 200, 500), false); + assertEquals(Insets.of(0, 100, 0, 0), insets); + } + + @Test public void testCalculateInsets_invisible() { mSource.setFrame(new Rect(0, 0, 500, 100)); mSource.setVisible(false); diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index b035c23cb98b..fde1a6d7b04c 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -248,6 +248,18 @@ public class InsetsStateTest { } @Test + public void testCalculateInsets_captionBarOffset() { + mState.getOrCreateSource(ID_CAPTION_BAR, captionBar()) + .setFrame(new Rect(0, 0, 100, 300)) + .setVisible(true); + + Insets visibleInsets = mState.calculateVisibleInsets( + new Rect(0, 0, 150, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */); + assertEquals(Insets.of(0, 300, 0, 0), visibleInsets); + } + + @Test public void testCalculateInsets_extraNavRightStatusTop() { mState.getOrCreateSource(ID_STATUS_BAR, statusBars()) .setFrame(new Rect(0, 0, 100, 100)) diff --git a/core/tests/coretests/src/com/android/internal/os/SafeZipPathValidatorCallbackTest.java b/core/tests/coretests/src/com/android/internal/os/SafeZipPathValidatorCallbackTest.java deleted file mode 100644 index c540a150bf35..000000000000 --- a/core/tests/coretests/src/com/android/internal/os/SafeZipPathValidatorCallbackTest.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2022 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.internal.os; - -import static org.junit.Assert.assertThrows; - -import android.compat.testing.PlatformCompatChangeRule; - -import androidx.test.runner.AndroidJUnit4; - -import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; -import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestRule; -import org.junit.runner.RunWith; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipException; -import java.util.zip.ZipFile; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; - -/** - * Test SafeZipPathCallback. - */ -@RunWith(AndroidJUnit4.class) -public class SafeZipPathValidatorCallbackTest { - @Rule - public TestRule mCompatChangeRule = new PlatformCompatChangeRule(); - - @Before - public void setUp() { - RuntimeInit.initZipPathValidatorCallback(); - } - - @Test - @EnableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL}) - public void testNewZipFile_whenZipFileHasDangerousEntriesAndChangeEnabled_throws() - throws Exception { - final String[] dangerousEntryNames = { - "../foo.bar", - "foo/../bar.baz", - "foo/../../bar.baz", - "foo.bar/..", - "foo.bar/../", - "..", - "../", - "/foo", - }; - for (String entryName : dangerousEntryNames) { - final File tempFile = File.createTempFile("smdc", "zip"); - try { - writeZipFileOutputStreamWithEmptyEntry(tempFile, entryName); - - assertThrows( - "ZipException expected for entry: " + entryName, - ZipException.class, - () -> { - new ZipFile(tempFile); - }); - } finally { - tempFile.delete(); - } - } - } - - @Test - @EnableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL}) - public void - testZipInputStreamGetNextEntry_whenZipFileHasDangerousEntriesAndChangeEnabled_throws() - throws Exception { - final String[] dangerousEntryNames = { - "../foo.bar", - "foo/../bar.baz", - "foo/../../bar.baz", - "foo.bar/..", - "foo.bar/../", - "..", - "../", - "/foo", - }; - for (String entryName : dangerousEntryNames) { - byte[] badZipBytes = getZipBytesFromZipOutputStreamWithEmptyEntry(entryName); - try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(badZipBytes))) { - assertThrows( - "ZipException expected for entry: " + entryName, - ZipException.class, - () -> { - zis.getNextEntry(); - }); - } - } - } - - @Test - @EnableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL}) - public void testNewZipFile_whenZipFileHasNormalEntriesAndChangeEnabled_doesNotThrow() - throws Exception { - final String[] normalEntryNames = { - "foo", "foo.bar", "foo..bar", - }; - for (String entryName : normalEntryNames) { - final File tempFile = File.createTempFile("smdc", "zip"); - try { - writeZipFileOutputStreamWithEmptyEntry(tempFile, entryName); - try { - new ZipFile((tempFile)); - } catch (ZipException e) { - throw new AssertionError("ZipException not expected for entry: " + entryName); - } - } finally { - tempFile.delete(); - } - } - } - - @Test - @DisableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL}) - public void - testZipInputStreamGetNextEntry_whenZipFileHasNormalEntriesAndChangeEnabled_doesNotThrow() - throws Exception { - final String[] normalEntryNames = { - "foo", "foo.bar", "foo..bar", - }; - for (String entryName : normalEntryNames) { - byte[] zipBytes = getZipBytesFromZipOutputStreamWithEmptyEntry(entryName); - try { - ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes)); - zis.getNextEntry(); - } catch (ZipException e) { - throw new AssertionError("ZipException not expected for entry: " + entryName); - } - } - } - - @Test - @DisableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL}) - public void - testNewZipFile_whenZipFileHasNormalAndDangerousEntriesAndChangeDisabled_doesNotThrow() - throws Exception { - final String[] entryNames = { - "../foo.bar", - "foo/../bar.baz", - "foo/../../bar.baz", - "foo.bar/..", - "foo.bar/../", - "..", - "../", - "/foo", - "foo", - "foo.bar", - "foo..bar", - }; - for (String entryName : entryNames) { - final File tempFile = File.createTempFile("smdc", "zip"); - try { - writeZipFileOutputStreamWithEmptyEntry(tempFile, entryName); - try { - new ZipFile((tempFile)); - } catch (ZipException e) { - throw new AssertionError("ZipException not expected for entry: " + entryName); - } - } finally { - tempFile.delete(); - } - } - } - - @Test - @DisableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL}) - public void - testZipInputStreamGetNextEntry_whenZipFileHasNormalAndDangerousEntriesAndChangeDisabled_doesNotThrow() - throws Exception { - final String[] entryNames = { - "../foo.bar", - "foo/../bar.baz", - "foo/../../bar.baz", - "foo.bar/..", - "foo.bar/../", - "..", - "../", - "/foo", - "foo", - "foo.bar", - "foo..bar", - }; - for (String entryName : entryNames) { - byte[] zipBytes = getZipBytesFromZipOutputStreamWithEmptyEntry(entryName); - try { - ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes)); - zis.getNextEntry(); - } catch (ZipException e) { - throw new AssertionError("ZipException not expected for entry: " + entryName); - } - } - } - - private void writeZipFileOutputStreamWithEmptyEntry(File tempFile, String entryName) - throws IOException { - FileOutputStream tempFileStream = new FileOutputStream(tempFile); - writeZipOutputStreamWithEmptyEntry(tempFileStream, entryName); - tempFileStream.close(); - } - - private byte[] getZipBytesFromZipOutputStreamWithEmptyEntry(String entryName) - throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - writeZipOutputStreamWithEmptyEntry(bos, entryName); - return bos.toByteArray(); - } - - private void writeZipOutputStreamWithEmptyEntry(OutputStream os, String entryName) - throws IOException { - ZipOutputStream zos = new ZipOutputStream(os); - ZipEntry entry = new ZipEntry(entryName); - zos.putNextEntry(entry); - zos.write(new byte[2]); - zos.closeEntry(); - zos.close(); - } -} diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java index 539eb6253f4d..be2c27de637c 100644 --- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -51,6 +51,7 @@ import android.app.ActivityThread.ActivityClientRecord; import android.app.LoadedApk; import android.app.servertransaction.PendingTransactionActions; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -293,7 +294,7 @@ public class ActivityThreadClientTest { private Activity launchActivity(ActivityClientRecord r) { return mThread.handleLaunchActivity(r, null /* pendingActions */, - null /* customIntent */); + Context.DEVICE_ID_DEFAULT, null /* customIntent */); } private void startActivity(ActivityClientRecord r) { @@ -347,7 +348,7 @@ public class ActivityThreadClientTest { doNothing().when(packageInfo).updateApplicationInfo(any(), any()); return new ActivityClientRecord(mock(IBinder.class), Intent.makeMainActivity(component), - 0 /* ident */, info, new Configuration(), 0 /*deviceId */, null /* referrer */, + 0 /* ident */, info, new Configuration(), null /* referrer */, null /* voiceInteractor */, null /* state */, null /* persistentState */, null /* pendingResults */, null /* pendingNewIntents */, null /* activityOptions */, true /* isForward */, null /* profilerInfo */, diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java index 87a805379b56..0f2f8797b896 100644 --- a/graphics/java/android/graphics/Color.java +++ b/graphics/java/android/graphics/Color.java @@ -767,7 +767,7 @@ public class Color { * Returns the alpha component encoded in the specified color long. * The returned value is always in the range \([0..1]\). * - * @param color The color long whose blue channel to extract + * @param color The color long whose alpha channel to extract * @return A float value in the range \([0..1]\) * * @see #colorSpace(long) diff --git a/graphics/java/android/graphics/Mesh.java b/graphics/java/android/graphics/Mesh.java index 6a1313e16ce5..66fabec91924 100644 --- a/graphics/java/android/graphics/Mesh.java +++ b/graphics/java/android/graphics/Mesh.java @@ -341,7 +341,6 @@ public class Mesh { * @hide so only calls from module can utilize it */ long getNativeWrapperInstance() { - nativeUpdateMesh(mNativeMeshWrapper, mIsIndexed); return mNativeMeshWrapper; } @@ -383,5 +382,4 @@ public class Mesh { private static native void nativeUpdateUniforms(long builder, String uniformName, int[] values); - private static native void nativeUpdateMesh(long nativeMeshWrapper, boolean mIsIndexed); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index b6fd0bbafc71..585f81c81a36 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -428,9 +428,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } @Override - public void addStartingWindow(StartingWindowInfo info) { + public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { if (mStartingWindow != null) { - mStartingWindow.addStartingWindow(info); + mStartingWindow.addStartingWindow(info, appToken); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 713dd39201a4..69f0bad4fb45 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -371,7 +371,14 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mViewHost.relayout(lp); } - void setInteractive(boolean interactive, String from) { + /** + * Set divider should interactive to user or not. + * + * @param interactive divider interactive. + * @param hideHandle divider handle hidden or not, only work when interactive is false. + * @param from caller from where. + */ + void setInteractive(boolean interactive, boolean hideHandle, String from) { if (interactive == mInteractive) return; ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Set divider bar %s from %s", interactive ? "interactive" : "non-interactive", @@ -387,7 +394,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mMoving = false; } releaseTouching(); - mHandle.setVisibility(mInteractive ? View.VISIBLE : View.INVISIBLE); + mHandle.setVisibility(!mInteractive && hideHandle ? View.INVISIBLE : View.VISIBLE); } private boolean isLandscape() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 8a18271b029a..7ac4d51c1502 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -486,6 +486,17 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } /** + * Set divider should interactive to user or not. + * + * @param interactive divider interactive. + * @param hideHandle divider handle hidden or not, only work when interactive is false. + * @param from caller from where. + */ + public void setDividerInteractive(boolean interactive, boolean hideHandle, String from) { + mSplitWindowManager.setInteractive(interactive, hideHandle, from); + } + + /** * Sets new divide position and updates bounds correspondingly. Notifies listener if the new * target indicates dismissing split. */ @@ -735,21 +746,28 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } } - /** Apply recorded task layout to the {@link WindowContainerTransaction}. */ - public void applyTaskChanges(WindowContainerTransaction wct, + /** Apply recorded task layout to the {@link WindowContainerTransaction}. + * + * @return true if stage bounds actually update. + */ + public boolean applyTaskChanges(WindowContainerTransaction wct, ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) { + boolean boundsChanged = false; if (!mBounds1.equals(mWinBounds1) || !task1.token.equals(mWinToken1)) { wct.setBounds(task1.token, mBounds1); wct.setSmallestScreenWidthDp(task1.token, getSmallestWidthDp(mBounds1)); mWinBounds1.set(mBounds1); mWinToken1 = task1.token; + boundsChanged = true; } if (!mBounds2.equals(mWinBounds2) || !task2.token.equals(mWinToken2)) { wct.setBounds(task2.token, mBounds2); wct.setSmallestScreenWidthDp(task2.token, getSmallestWidthDp(mBounds2)); mWinBounds2.set(mBounds2); mWinToken2 = task2.token; + boundsChanged = true; } + return boundsChanged; } private int getSmallestWidthDp(Rect bounds) { @@ -1091,7 +1109,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough // because DividerView won't receive onImeVisibilityChanged callback after it being // re-inflated. - mSplitWindowManager.setInteractive(!mImeShown || !mHasImeFocus || isFloating, + setDividerInteractive(!mImeShown || !mHasImeFocus || isFloating, true, "onImeStartPositioning"); return needOffset ? IME_ANIMATION_NO_ALPHA : 0; @@ -1118,7 +1136,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange // Restore the split layout when wm-shell is not controlling IME insets anymore. if (!controlling && mImeShown) { reset(); - mSplitWindowManager.setInteractive(true, "onImeControlTargetChanged"); + setDividerInteractive(true, true, "onImeControlTargetChanged"); mSplitLayoutHandler.setLayoutOffsetTarget(0, 0, SplitLayout.this); mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index eb3c1df0ae73..00361d9dd9cf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -168,9 +168,16 @@ public final class SplitWindowManager extends WindowlessWindowManager { } } - void setInteractive(boolean interactive, String from) { + /** + * Set divider should interactive to user or not. + * + * @param interactive divider interactive. + * @param hideHandle divider handle hidden or not, only work when interactive is false. + * @param from caller from where. + */ + void setInteractive(boolean interactive, boolean hideHandle, String from) { if (mDividerView == null) return; - mDividerView.setInteractive(interactive, from); + mDividerView.setInteractive(interactive, hideHandle, from); } View getDividerView() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index 3b2db5127316..76d9152fdfbc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -126,14 +126,12 @@ public class CompatUIController implements OnDisplaysChangedListener, private final Lazy<Transitions> mTransitionsLazy; private final DockStateReader mDockStateReader; private final CompatUIConfiguration mCompatUIConfiguration; - - private CompatUICallback mCallback; - // Only show each hint once automatically in the process life. private final CompatUIHintsState mCompatUIHintsState; - private final CompatUIShellCommandHandler mCompatUIShellCommandHandler; + private CompatUICallback mCallback; + // Indicates if the keyguard is currently showing, in which case compat UIs shouldn't // be shown. private boolean mKeyguardShowing; @@ -372,19 +370,20 @@ public class CompatUIController implements OnDisplaysChangedListener, RestartDialogWindowManager layout = mTaskIdToRestartDialogWindowManagerMap.get(taskInfo.taskId); if (layout != null) { - // TODO(b/266262111) Handle theme change when taskListener changes - if (layout.getTaskListener() != taskListener) { - mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId); - } - layout.setRequestRestartDialog( - mSetOfTaskIdsShowingRestartDialog.contains(taskInfo.taskId)); - // UI already exists, update the UI layout. - if (!layout.updateCompatInfo(taskInfo, taskListener, - showOnDisplay(layout.getDisplayId()))) { - // The layout is no longer eligible to be shown, remove from active layouts. + if (layout.needsToBeRecreated(taskInfo, taskListener)) { mTaskIdToRestartDialogWindowManagerMap.remove(taskInfo.taskId); + layout.release(); + } else { + layout.setRequestRestartDialog( + mSetOfTaskIdsShowingRestartDialog.contains(taskInfo.taskId)); + // UI already exists, update the UI layout. + if (!layout.updateCompatInfo(taskInfo, taskListener, + showOnDisplay(layout.getDisplayId()))) { + // The layout is no longer eligible to be shown, remove from active layouts. + mTaskIdToRestartDialogWindowManagerMap.remove(taskInfo.taskId); + } + return; } - return; } // Create a new UI layout. final Context context = getOrCreateDisplayContext(taskInfo.displayId); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java index efd459498adb..b22c9c7e7529 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java @@ -151,7 +151,6 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana @Override public void setConfiguration(Configuration configuration) { super.setConfiguration(configuration); - // TODO(b/266262111): Investigate loss of theme configuration when switching TaskListener mContext = mContext.createConfigurationContext(configuration); } @@ -210,7 +209,8 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana } View layout = getLayout(); - if (layout == null || prevTaskListener != taskListener) { + if (layout == null || prevTaskListener != taskListener + || mTaskConfig.uiMode != prevTaskConfig.uiMode) { // Layout wasn't created yet or TaskListener changed, recreate the layout for new // surface parent. release(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java index 10f25d0eef11..2440838844c4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java @@ -155,6 +155,11 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract { return super.updateCompatInfo(taskInfo, taskListener, canShow); } + boolean needsToBeRecreated(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) { + return taskInfo.configuration.uiMode != mTaskInfo.configuration.uiMode + || !getTaskListener().equals(taskListener); + } + private void updateDialogMargins() { if (mLayout == null) { return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index d1f439894c47..948bf2d100f9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -196,7 +196,8 @@ public abstract class WMShellModule { DisplayController displayController, SyncTransactionQueue syncQueue, Optional<DesktopModeController> desktopModeController, - Optional<DesktopTasksController> desktopTasksController) { + Optional<DesktopTasksController> desktopTasksController, + Optional<SplitScreenController> splitScreenController) { if (DesktopModeStatus.isAnyEnabled()) { return new DesktopModeWindowDecorViewModel( context, @@ -206,7 +207,8 @@ public abstract class WMShellModule { displayController, syncQueue, desktopModeController, - desktopTasksController); + desktopTasksController, + splitScreenController); } return new CaptionWindowDecorViewModel( context, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index b59fe1818780..4cfaae6e51c7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -36,6 +36,7 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_U import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import android.content.ClipDescription; +import android.content.ComponentCallbacks2; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; @@ -58,9 +59,9 @@ import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.annotations.ExternalMainThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.SplitScreenController; -import com.android.wm.shell.sysui.ConfigurationChangeListener; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; @@ -70,7 +71,7 @@ import java.util.ArrayList; * Handles the global drag and drop handling for the Shell. */ public class DragAndDropController implements DisplayController.OnDisplaysChangedListener, - View.OnDragListener, ConfigurationChangeListener { + View.OnDragListener, ComponentCallbacks2 { private static final String TAG = DragAndDropController.class.getSimpleName(); @@ -119,7 +120,6 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange mMainExecutor.executeDelayed(() -> { mDisplayController.addDisplayWindowListener(this); }, 0); - mShellController.addConfigurationChangeListener(this); } /** @@ -180,6 +180,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange try { wm.addView(rootView, layoutParams); addDisplayDropTarget(displayId, context, wm, rootView, dragLayout); + context.registerComponentCallbacks(this); } catch (WindowManager.InvalidDisplayException e) { Slog.w(TAG, "Unable to add view for display id: " + displayId); } @@ -209,6 +210,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange if (pd == null) { return; } + pd.context.unregisterComponentCallbacks(this); pd.wm.removeViewImmediate(pd.rootView); mDisplayDropTargets.remove(displayId); } @@ -328,18 +330,29 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange return mimeTypes; } + // Note: Component callbacks are always called on the main thread of the process + @ExternalMainThread @Override - public void onThemeChanged() { - for (int i = 0; i < mDisplayDropTargets.size(); i++) { - mDisplayDropTargets.get(i).dragLayout.onThemeChange(); - } + public void onConfigurationChanged(Configuration newConfig) { + mMainExecutor.execute(() -> { + for (int i = 0; i < mDisplayDropTargets.size(); i++) { + mDisplayDropTargets.get(i).dragLayout.onConfigChanged(newConfig); + } + }); } + // Note: Component callbacks are always called on the main thread of the process + @ExternalMainThread @Override - public void onConfigurationChanged(Configuration newConfig) { - for (int i = 0; i < mDisplayDropTargets.size(); i++) { - mDisplayDropTargets.get(i).dragLayout.onConfigChanged(newConfig); - } + public void onTrimMemory(int level) { + // Do nothing + } + + // Note: Component callbacks are always called on the main thread of the process + @ExternalMainThread + @Override + public void onLowMemory() { + // Do nothing } private static class PerDisplay { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index 44fd8eec4d06..fe42822ab6a1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -18,6 +18,8 @@ package com.android.wm.shell.draganddrop; import static android.app.StatusBarManager.DISABLE_NONE; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS; +import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -72,6 +74,7 @@ public class DragLayout extends LinearLayout { private final SplitScreenController mSplitScreenController; private final IconProvider mIconProvider; private final StatusBarManager mStatusBarManager; + private final Configuration mLastConfiguration = new Configuration(); private DragAndDropPolicy.Target mCurrentTarget = null; private DropZoneView mDropZoneView1; @@ -92,6 +95,7 @@ public class DragLayout extends LinearLayout { mIconProvider = iconProvider; mPolicy = new DragAndDropPolicy(context, splitScreenController); mStatusBarManager = context.getSystemService(StatusBarManager.class); + mLastConfiguration.setTo(context.getResources().getConfiguration()); mDisplayMargin = context.getResources().getDimensionPixelSize( R.dimen.drop_layout_display_margin); @@ -132,11 +136,6 @@ public class DragLayout extends LinearLayout { return super.onApplyWindowInsets(insets); } - public void onThemeChange() { - mDropZoneView1.onThemeChange(); - mDropZoneView2.onThemeChange(); - } - public void onConfigChanged(Configuration newConfig) { if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE && getOrientation() != HORIZONTAL) { @@ -147,6 +146,15 @@ public class DragLayout extends LinearLayout { setOrientation(LinearLayout.VERTICAL); updateContainerMargins(newConfig.orientation); } + + final int diff = newConfig.diff(mLastConfiguration); + final boolean themeChanged = (diff & CONFIG_ASSETS_PATHS) != 0 + || (diff & CONFIG_UI_MODE) != 0; + if (themeChanged) { + mDropZoneView1.onThemeChange(); + mDropZoneView2.onThemeChange(); + } + mLastConfiguration.setTo(newConfig); } private void updateContainerMarginsForSingleTask() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index c7ad4fdcacf0..94b9e907fa76 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -422,6 +422,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mStageCoordinator.goToFullscreenFromSplit(); } + /** Move the specified task to fullscreen, regardless of focus state. */ + public void moveTaskToFullscreen(int taskId) { + mStageCoordinator.moveTaskToFullscreen(taskId); + } + public boolean isLaunchToSplit(TaskInfo taskInfo) { return mStageCoordinator.isLaunchToSplit(taskInfo); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index c96323a340b4..e1c089550c2d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -340,6 +340,12 @@ class SplitScreenTransitions { IBinder startResizeTransition(WindowContainerTransaction wct, Transitions.TransitionHandler handler, @Nullable TransitionFinishedCallback finishCallback) { + if (mPendingResize != null) { + mPendingResize.cancel(null); + mAnimations.clear(); + onFinish(null /* wct */, null /* wctCB */); + } + IBinder transition = mTransitions.startTransition(TRANSIT_CHANGE, wct, handler); setResizeTransition(transition, finishCallback); return transition; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 8b24d86a568f..427d79e3a1b9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -937,7 +937,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, new IRemoteAnimationFinishedCallback.Stub() { @Override public void onAnimationFinished() throws RemoteException { - onRemoteAnimationFinishedOrCancelled(false /* cancel */, evictWct); + onRemoteAnimationFinishedOrCancelled(evictWct); finishedCallback.onAnimationFinished(); } }; @@ -953,7 +953,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onAnimationCancelled(boolean isKeyguardOccluded) { - onRemoteAnimationFinishedOrCancelled(true /* cancel */, evictWct); + onRemoteAnimationFinishedOrCancelled(evictWct); try { adapter.getRunner().onAnimationCancelled(isKeyguardOccluded); } catch (RemoteException e) { @@ -974,15 +974,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - private void onRemoteAnimationFinishedOrCancelled(boolean cancel, - WindowContainerTransaction evictWct) { + private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) { mIsSplitEntering = false; mShouldUpdateRecents = true; mSplitRequest = null; // If any stage has no child after animation finished, it means that split will display // nothing, such status will happen if task and intent is same app but not support // multi-instance, we should exit split and expand that app as full screen. - if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) { + if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) { mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0 ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN)); @@ -1237,8 +1236,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Notify recents if we are exiting in a way that breaks the pair, and disable further // updates to splits in the recents until we enter split again if (shouldBreakPairedTaskInRecents(exitReason) && mShouldUpdateRecents) { - recentTasks.removeSplitPair(mMainStage.getLastVisibleTaskId()); - recentTasks.removeSplitPair(mSideStage.getLastVisibleTaskId()); + recentTasks.removeSplitPair(mMainStage.getTopVisibleChildTaskId()); + recentTasks.removeSplitPair(mSideStage.getTopVisibleChildTaskId()); } }); mShouldUpdateRecents = false; @@ -1925,10 +1924,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } final WindowContainerTransaction wct = new WindowContainerTransaction(); - updateWindowBounds(layout, wct); + boolean sizeChanged = updateWindowBounds(layout, wct); + if (!sizeChanged) return; + sendOnBoundsChanged(); if (ENABLE_SHELL_TRANSITIONS) { - mSplitTransitions.startResizeTransition(wct, this, null /* callback */); + mSplitLayout.setDividerInteractive(false, false, "onSplitResizeStart"); + mSplitTransitions.startResizeTransition(wct, this, (finishWct, t) -> + mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish")); } else { mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { @@ -1947,13 +1950,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** * Populates `wct` with operations that match the split windows to the current layout. * To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied + * + * @return true if stage bounds actually . */ - private void updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) { + private boolean updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) { final StageTaskListener topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; final StageTaskListener bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); + return layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, + bottomRightStage.mRootTaskInfo); } void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t, @@ -2400,6 +2406,20 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT); } + /** Move the specified task to fullscreen, regardless of focus state. */ + public void moveTaskToFullscreen(int taskId) { + boolean leftOrTop; + if (mMainStage.containsTask(taskId)) { + leftOrTop = (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); + } else if (mSideStage.containsTask(taskId)) { + leftOrTop = (mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT); + } else { + return; + } + mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT); + + } + boolean isLaunchToSplit(TaskInfo taskInfo) { return getActivateSplitPosition(taskInfo) != SPLIT_POSITION_UNDEFINED; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 0359761388dc..a841b7f96d3c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -92,7 +92,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { protected SurfaceControl mDimLayer; protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>(); private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>(); - private int mLastVisibleTaskId = INVALID_TASK_ID; // TODO(b/204308910): Extracts SplitDecorManager related code to common package. private SplitDecorManager mSplitDecorManager; @@ -124,13 +123,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } /** - * Returns the last visible task's id. - */ - int getLastVisibleTaskId() { - return mLastVisibleTaskId; - } - - /** * Returns the top visible child task's id. */ int getTopVisibleChildTaskId() { @@ -229,9 +221,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { return; } mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); - if (taskInfo.isVisible && taskInfo.taskId != mLastVisibleTaskId) { - mLastVisibleTaskId = taskInfo.taskId; - } mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */, taskInfo.isVisible); if (!ENABLE_SHELL_TRANSITIONS) { @@ -264,9 +253,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } else if (mChildrenTaskInfo.contains(taskId)) { mChildrenTaskInfo.remove(taskId); mChildrenLeashes.remove(taskId); - if (taskId == mLastVisibleTaskId) { - mLastVisibleTaskId = INVALID_TASK_ID; - } mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible); if (ENABLE_SHELL_TRANSITIONS) { // Status is managed/synchronized by the transition lifecycle. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java deleted file mode 100644 index 1ddd8f9a3a14..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.startingsurface; - -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.hardware.display.DisplayManager; -import android.view.Display; - -import com.android.wm.shell.common.ShellExecutor; - -// abstract class to create splash screen window(or windowless window) -abstract class AbsSplashWindowCreator { - protected static final String TAG = StartingWindowController.TAG; - protected final SplashscreenContentDrawer mSplashscreenContentDrawer; - protected final Context mContext; - protected final DisplayManager mDisplayManager; - protected final ShellExecutor mSplashScreenExecutor; - protected final StartingSurfaceDrawer.StartingWindowRecordManager mStartingWindowRecordManager; - - private StartingSurface.SysuiProxy mSysuiProxy; - - AbsSplashWindowCreator(SplashscreenContentDrawer contentDrawer, Context context, - ShellExecutor splashScreenExecutor, DisplayManager displayManager, - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) { - mSplashscreenContentDrawer = contentDrawer; - mContext = context; - mSplashScreenExecutor = splashScreenExecutor; - mDisplayManager = displayManager; - mStartingWindowRecordManager = startingWindowRecordManager; - } - - int getSplashScreenTheme(int splashScreenThemeResId, ActivityInfo activityInfo) { - return splashScreenThemeResId != 0 - ? splashScreenThemeResId - : activityInfo.getThemeResource() != 0 ? activityInfo.getThemeResource() - : com.android.internal.R.style.Theme_DeviceDefault_DayNight; - } - - protected Display getDisplay(int displayId) { - return mDisplayManager.getDisplay(displayId); - } - - void setSysuiProxy(StartingSurface.SysuiProxy sysuiProxy) { - mSysuiProxy = sysuiProxy; - } - - protected void requestTopUi(boolean requestTopUi) { - if (mSysuiProxy != null) { - mSysuiProxy.requestTopUi(requestTopUi, TAG); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java deleted file mode 100644 index 20c4d5ae5f58..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.startingsurface; - -import android.window.StartingWindowInfo; -import android.window.TaskSnapshot; - -import com.android.wm.shell.common.ShellExecutor; - -class SnapshotWindowCreator { - private final ShellExecutor mMainExecutor; - private final StartingSurfaceDrawer.StartingWindowRecordManager - mStartingWindowRecordManager; - - SnapshotWindowCreator(ShellExecutor mainExecutor, - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) { - mMainExecutor = mainExecutor; - mStartingWindowRecordManager = startingWindowRecordManager; - } - - void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, TaskSnapshot snapshot) { - final int taskId = startingWindowInfo.taskInfo.taskId; - // Remove any existing starting window for this task before adding. - mStartingWindowRecordManager.removeWindow(taskId, true); - final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, - startingWindowInfo.appToken, snapshot, mMainExecutor, - () -> mStartingWindowRecordManager.removeWindow(taskId, true)); - if (surface != null) { - final SnapshotWindowRecord tView = new SnapshotWindowRecord(surface, - startingWindowInfo.taskInfo.topActivityType, mMainExecutor); - mStartingWindowRecordManager.addRecord(taskId, tView); - } - } - - private static class SnapshotWindowRecord extends StartingSurfaceDrawer.SnapshotRecord { - private final TaskSnapshotWindow mTaskSnapshotWindow; - - SnapshotWindowRecord(TaskSnapshotWindow taskSnapshotWindow, - int activityType, ShellExecutor removeExecutor) { - super(activityType, removeExecutor); - mTaskSnapshotWindow = taskSnapshotWindow; - mBGColor = mTaskSnapshotWindow.getBackgroundColor(); - } - - @Override - protected void removeImmediately() { - super.removeImmediately(); - mTaskSnapshotWindow.removeImmediately(); - } - - @Override - protected boolean hasImeSurface() { - return mTaskSnapshotWindow.hasImeSurface(); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index 79cd891741d6..ebb957b2201b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -24,6 +24,9 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLA import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; +import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MAX_ANIMATION_DURATION; +import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MINIMAL_ANIMATION_DURATION; + import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; @@ -93,25 +96,6 @@ import java.util.function.UnaryOperator; public class SplashscreenContentDrawer { private static final String TAG = StartingWindowController.TAG; - /** - * The minimum duration during which the splash screen is shown when the splash screen icon is - * animated. - */ - static final long MINIMAL_ANIMATION_DURATION = 400L; - - /** - * Allow the icon style splash screen to be displayed for longer to give time for the animation - * to finish, i.e. the extra buffer time to keep the splash screen if the animation is slightly - * longer than the {@link #MINIMAL_ANIMATION_DURATION} duration. - */ - static final long TIME_WINDOW_DURATION = 100L; - - /** - * The maximum duration during which the splash screen will be shown if the application is ready - * to show before the icon animation finishes. - */ - static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION; - // The acceptable area ratio of foreground_icon_area/background_icon_area, if there is an // icon which it's non-transparent foreground area is similar to it's background area, then // do not enlarge the foreground drawable. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java deleted file mode 100644 index 4db81e232f20..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.startingsurface; - -import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; -import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; - -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.app.ActivityThread; -import android.app.TaskInfo; -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.PixelFormat; -import android.hardware.display.DisplayManager; -import android.os.IBinder; -import android.os.RemoteCallback; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.Trace; -import android.os.UserHandle; -import android.util.Slog; -import android.util.SparseArray; -import android.view.Choreographer; -import android.view.Display; -import android.view.SurfaceControlViewHost; -import android.view.View; -import android.view.WindowInsetsController; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; -import android.widget.FrameLayout; -import android.window.SplashScreenView; -import android.window.StartingWindowInfo; -import android.window.StartingWindowRemovalInfo; - -import com.android.internal.R; -import com.android.internal.protolog.common.ProtoLog; -import com.android.internal.util.ContrastColorUtil; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.protolog.ShellProtoLogGroup; - -import java.util.function.Supplier; - -/** - * A class which able to draw splash screen as the starting window for a task. - * - * In order to speed up, there will use two threads to creating a splash screen in parallel. - * Right now we are still using PhoneWindow to create splash screen window, so the view is added to - * the ViewRootImpl, and those view won't be draw immediately because the ViewRootImpl will call - * scheduleTraversal to register a callback from Choreographer, so the drawing result of the view - * can synchronize on each frame. - * - * The bad thing is that we cannot decide when would Choreographer#doFrame happen, and drawing - * the AdaptiveIconDrawable object can be time consuming, so we use the splash-screen background - * thread to draw the AdaptiveIconDrawable object to a Bitmap and cache it to a BitmapShader after - * the SplashScreenView just created, once we get the BitmapShader then the #draw call can be very - * quickly. - * - * So basically we are using the spare time to prepare the SplashScreenView while splash screen - * thread is waiting for - * 1. WindowManager#addView(binder call to WM), - * 2. Choreographer#doFrame happen(uncertain time for next frame, depends on device), - * 3. Session#relayout(another binder call to WM which under Choreographer#doFrame, but will - * always happen before #draw). - * Because above steps are running on splash-screen thread, so pre-draw the BitmapShader on - * splash-screen background tread can make they execute in parallel, which ensure it is faster then - * to draw the AdaptiveIconDrawable when receive callback from Choreographer#doFrame. - * - * Here is the sequence to compare the difference between using single and two thread. - * - * Single thread: - * => makeSplashScreenContentView -> WM#addView .. waiting for Choreographer#doFrame -> relayout - * -> draw -> AdaptiveIconDrawable#draw - * - * Two threads: - * => makeSplashScreenContentView -> cachePaint(=AdaptiveIconDrawable#draw) - * => WM#addView -> .. waiting for Choreographer#doFrame -> relayout -> draw -> (draw the Paint - * directly). - */ -class SplashscreenWindowCreator extends AbsSplashWindowCreator { - private static final int LIGHT_BARS_MASK = - WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS - | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; - - private final WindowManagerGlobal mWindowManagerGlobal; - private Choreographer mChoreographer; - - /** - * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is - * rendered and that have not yet been removed by their client. - */ - private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts = - new SparseArray<>(1); - - SplashscreenWindowCreator(SplashscreenContentDrawer contentDrawer, Context context, - ShellExecutor splashScreenExecutor, DisplayManager displayManager, - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) { - super(contentDrawer, context, splashScreenExecutor, displayManager, - startingWindowRecordManager); - mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance()); - mWindowManagerGlobal = WindowManagerGlobal.getInstance(); - } - - void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, - @StartingWindowInfo.StartingWindowType int suggestType) { - final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo; - final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null - ? windowInfo.targetActivityInfo - : taskInfo.topActivityInfo; - if (activityInfo == null || activityInfo.packageName == null) { - return; - } - // replace with the default theme if the application didn't set - final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo); - final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme, - suggestType, mDisplayManager); - if (context == null) { - return; - } - final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters( - context, windowInfo, suggestType, activityInfo.packageName, - suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN - ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, windowInfo.appToken); - - final int displayId = taskInfo.displayId; - final int taskId = taskInfo.taskId; - final Display display = getDisplay(displayId); - - // TODO(b/173975965) tracking performance - // Prepare the splash screen content view on splash screen worker thread in parallel, so the - // content view won't be blocked by binder call like addWindow and relayout. - // 1. Trigger splash screen worker thread to create SplashScreenView before/while - // Session#addWindow. - // 2. Synchronize the SplashscreenView to splash screen thread before Choreographer start - // traversal, which will call Session#relayout on splash screen thread. - // 3. Pre-draw the BitmapShader if the icon is immobile on splash screen worker thread, at - // the same time the splash screen thread should be executing Session#relayout. Blocking the - // traversal -> draw on splash screen thread until the BitmapShader of the icon is ready. - - // Record whether create splash screen view success, notify to current thread after - // create splash screen view finished. - final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier(); - final FrameLayout rootLayout = new FrameLayout( - mSplashscreenContentDrawer.createViewContextWrapper(context)); - rootLayout.setPadding(0, 0, 0, 0); - rootLayout.setFitsSystemWindows(false); - final Runnable setViewSynchronized = () -> { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addSplashScreenView"); - // waiting for setContentView before relayoutWindow - SplashScreenView contentView = viewSupplier.get(); - final StartingSurfaceDrawer.StartingWindowRecord sRecord = - mStartingWindowRecordManager.getRecord(taskId); - final SplashWindowRecord record = sRecord instanceof SplashWindowRecord - ? (SplashWindowRecord) sRecord : null; - // If record == null, either the starting window added fail or removed already. - // Do not add this view if the token is mismatch. - if (record != null && windowInfo.appToken == record.mAppToken) { - // if view == null then creation of content view was failed. - if (contentView != null) { - try { - rootLayout.addView(contentView); - } catch (RuntimeException e) { - Slog.w(TAG, "failed set content view to starting window " - + "at taskId: " + taskId, e); - contentView = null; - } - } - record.setSplashScreenView(contentView); - } - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - }; - requestTopUi(true); - mSplashscreenContentDrawer.createContentView(context, suggestType, windowInfo, - viewSupplier::setView, viewSupplier::setUiThreadInitTask); - try { - if (addWindow(taskId, windowInfo.appToken, rootLayout, display, params, suggestType)) { - // We use the splash screen worker thread to create SplashScreenView while adding - // the window, as otherwise Choreographer#doFrame might be delayed on this thread. - // And since Choreographer#doFrame won't happen immediately after adding the window, - // if the view is not added to the PhoneWindow on the first #doFrame, the view will - // not be rendered on the first frame. So here we need to synchronize the view on - // the window before first round relayoutWindow, which will happen after insets - // animation. - mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null); - final SplashWindowRecord record = - (SplashWindowRecord) mStartingWindowRecordManager.getRecord(taskId); - if (record != null) { - record.parseAppSystemBarColor(context); - // Block until we get the background color. - final SplashScreenView contentView = viewSupplier.get(); - if (suggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { - contentView.addOnAttachStateChangeListener( - new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - final int lightBarAppearance = - ContrastColorUtil.isColorLight( - contentView.getInitBackgroundColor()) - ? LIGHT_BARS_MASK : 0; - contentView.getWindowInsetsController() - .setSystemBarsAppearance( - lightBarAppearance, LIGHT_BARS_MASK); - } - - @Override - public void onViewDetachedFromWindow(View v) { - } - }); - } - } - } else { - // release the icon view host - final SplashScreenView contentView = viewSupplier.get(); - if (contentView.getSurfaceHost() != null) { - SplashScreenView.releaseIconHost(contentView.getSurfaceHost()); - } - } - } catch (RuntimeException e) { - // don't crash if something else bad happens, for example a - // failure loading resources because we are loading from an app - // on external storage that has been unmounted. - Slog.w(TAG, "failed creating starting window at taskId: " + taskId, e); - } - } - - int estimateTaskBackgroundColor(TaskInfo taskInfo) { - if (taskInfo.topActivityInfo == null) { - return Color.TRANSPARENT; - } - final ActivityInfo activityInfo = taskInfo.topActivityInfo; - final String packageName = activityInfo.packageName; - final int userId = taskInfo.userId; - final Context windowContext; - try { - windowContext = mContext.createPackageContextAsUser( - packageName, Context.CONTEXT_RESTRICTED, UserHandle.of(userId)); - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Failed creating package context with package name " - + packageName + " for user " + taskInfo.userId, e); - return Color.TRANSPARENT; - } - try { - final IPackageManager packageManager = ActivityThread.getPackageManager(); - final String splashScreenThemeName = packageManager.getSplashScreenTheme(packageName, - userId); - final int splashScreenThemeId = splashScreenThemeName != null - ? windowContext.getResources().getIdentifier(splashScreenThemeName, null, null) - : 0; - - final int theme = getSplashScreenTheme(splashScreenThemeId, activityInfo); - - if (theme != windowContext.getThemeResId()) { - windowContext.setTheme(theme); - } - return mSplashscreenContentDrawer.estimateTaskBackgroundColor(windowContext); - } catch (RuntimeException | RemoteException e) { - Slog.w(TAG, "failed get starting window background color at taskId: " - + taskInfo.taskId, e); - } - return Color.TRANSPARENT; - } - - /** - * Called when the Task wants to copy the splash screen. - */ - public void copySplashScreenView(int taskId) { - final StartingSurfaceDrawer.StartingWindowRecord record = - mStartingWindowRecordManager.getRecord(taskId); - final SplashWindowRecord preView = record instanceof SplashWindowRecord - ? (SplashWindowRecord) record : null; - SplashScreenView.SplashScreenViewParcelable parcelable; - SplashScreenView splashScreenView = preView != null ? preView.mSplashView : null; - if (splashScreenView != null && splashScreenView.isCopyable()) { - parcelable = new SplashScreenView.SplashScreenViewParcelable(splashScreenView); - parcelable.setClientCallback( - new RemoteCallback((bundle) -> mSplashScreenExecutor.execute( - () -> onAppSplashScreenViewRemoved(taskId, false)))); - splashScreenView.onCopied(); - mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost()); - } else { - parcelable = null; - } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "Copying splash screen window view for task: %d with parcelable %b", - taskId, parcelable != null); - ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable); - } - - /** - * Called when the {@link SplashScreenView} is removed from the client Activity view's hierarchy - * or when the Activity is clean up. - * - * @param taskId The Task id on which the splash screen was attached - */ - public void onAppSplashScreenViewRemoved(int taskId) { - onAppSplashScreenViewRemoved(taskId, true /* fromServer */); - } - - /** - * @param fromServer If true, this means the removal was notified by the server. This is only - * used for debugging purposes. - * @see #onAppSplashScreenViewRemoved(int) - */ - private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) { - SurfaceControlViewHost viewHost = - mAnimatedSplashScreenSurfaceHosts.get(taskId); - if (viewHost == null) { - return; - } - mAnimatedSplashScreenSurfaceHosts.remove(taskId); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "%s the splash screen. Releasing SurfaceControlViewHost for task: %d", - fromServer ? "Server cleaned up" : "App removed", taskId); - SplashScreenView.releaseIconHost(viewHost); - } - - protected boolean addWindow(int taskId, IBinder appToken, View view, Display display, - WindowManager.LayoutParams params, - @StartingWindowInfo.StartingWindowType int suggestType) { - boolean shouldSaveView = true; - final Context context = view.getContext(); - try { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView"); - mWindowManagerGlobal.addView(view, params, display, - null /* parentWindow */, context.getUserId()); - } catch (WindowManager.BadTokenException e) { - // ignore - Slog.w(TAG, appToken + " already running, starting window not displayed. " - + e.getMessage()); - shouldSaveView = false; - } finally { - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - if (view.getParent() == null) { - Slog.w(TAG, "view not successfully added to wm, removing view"); - mWindowManagerGlobal.removeView(view, true /* immediate */); - shouldSaveView = false; - } - } - if (shouldSaveView) { - mStartingWindowRecordManager.removeWindow(taskId, true); - saveSplashScreenRecord(appToken, taskId, view, suggestType); - } - return shouldSaveView; - } - - private void saveSplashScreenRecord(IBinder appToken, int taskId, View view, - @StartingWindowInfo.StartingWindowType int suggestType) { - final SplashWindowRecord tView = - new SplashWindowRecord(appToken, view, suggestType); - mStartingWindowRecordManager.addRecord(taskId, tView); - } - - private void removeWindowInner(View decorView, boolean hideView) { - requestTopUi(false); - if (hideView) { - decorView.setVisibility(View.GONE); - } - mWindowManagerGlobal.removeView(decorView, false /* immediate */); - } - - private static class SplashScreenViewSupplier implements Supplier<SplashScreenView> { - private SplashScreenView mView; - private boolean mIsViewSet; - private Runnable mUiThreadInitTask; - void setView(SplashScreenView view) { - synchronized (this) { - mView = view; - mIsViewSet = true; - notify(); - } - } - - void setUiThreadInitTask(Runnable initTask) { - synchronized (this) { - mUiThreadInitTask = initTask; - } - } - - @Override - @Nullable - public SplashScreenView get() { - synchronized (this) { - while (!mIsViewSet) { - try { - wait(); - } catch (InterruptedException ignored) { - } - } - if (mUiThreadInitTask != null) { - mUiThreadInitTask.run(); - mUiThreadInitTask = null; - } - return mView; - } - } - } - - private class SplashWindowRecord extends StartingSurfaceDrawer.StartingWindowRecord { - private final IBinder mAppToken; - private final View mRootView; - @StartingWindowInfo.StartingWindowType private final int mSuggestType; - private final long mCreateTime; - - private boolean mSetSplashScreen; - private SplashScreenView mSplashView; - private int mSystemBarAppearance; - private boolean mDrawsSystemBarBackgrounds; - - SplashWindowRecord(IBinder appToken, View decorView, - @StartingWindowInfo.StartingWindowType int suggestType) { - mAppToken = appToken; - mRootView = decorView; - mSuggestType = suggestType; - mCreateTime = SystemClock.uptimeMillis(); - } - - void setSplashScreenView(SplashScreenView splashScreenView) { - if (mSetSplashScreen) { - return; - } - mSplashView = splashScreenView; - mBGColor = mSplashView.getInitBackgroundColor(); - mSetSplashScreen = true; - } - - void parseAppSystemBarColor(Context context) { - final TypedArray a = context.obtainStyledAttributes(R.styleable.Window); - mDrawsSystemBarBackgrounds = a.getBoolean( - R.styleable.Window_windowDrawsSystemBarBackgrounds, false); - if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) { - mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; - } - if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) { - mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; - } - a.recycle(); - } - - // Reset the system bar color which set by splash screen, make it align to the app. - void clearSystemBarColor() { - if (mRootView == null || !mRootView.isAttachedToWindow()) { - return; - } - if (mRootView.getLayoutParams() instanceof WindowManager.LayoutParams) { - final WindowManager.LayoutParams lp = - (WindowManager.LayoutParams) mRootView.getLayoutParams(); - if (mDrawsSystemBarBackgrounds) { - lp.flags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - } else { - lp.flags &= ~WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - } - mRootView.setLayoutParams(lp); - } - mRootView.getWindowInsetsController().setSystemBarsAppearance( - mSystemBarAppearance, LIGHT_BARS_MASK); - } - - @Override - public void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) { - if (mRootView != null) { - if (mSplashView != null) { - clearSystemBarColor(); - if (immediately - || mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { - removeWindowInner(mRootView, false); - } else { - if (info.playRevealAnimation) { - mSplashscreenContentDrawer.applyExitAnimation(mSplashView, - info.windowAnimationLeash, info.mainFrame, - () -> removeWindowInner(mRootView, true), - mCreateTime, info.roundedCornerRadius); - } else { - // the SplashScreenView has been copied to client, hide the view to skip - // default exit animation - removeWindowInner(mRootView, true); - } - } - } else { - // shouldn't happen - Slog.e(TAG, "Found empty splash screen, remove!"); - removeWindowInner(mRootView, false); - } - } - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java index ff06db370d1a..4f07bfeacce5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -16,80 +16,169 @@ package com.android.wm.shell.startingsurface; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION; import static android.view.Display.DEFAULT_DISPLAY; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; -import android.annotation.CallSuper; +import android.annotation.Nullable; +import android.app.ActivityManager.RunningTaskInfo; +import android.app.ActivityTaskManager; +import android.app.ActivityThread; import android.app.TaskInfo; -import android.app.WindowConfiguration; import android.content.Context; -import android.content.res.Configuration; +import android.content.pm.ActivityInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.content.res.TypedArray; import android.graphics.Color; +import android.graphics.PixelFormat; import android.hardware.display.DisplayManager; +import android.os.IBinder; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.Trace; +import android.os.UserHandle; +import android.util.Slog; import android.util.SparseArray; -import android.view.IWindow; -import android.view.SurfaceControl; -import android.view.SurfaceSession; +import android.view.Choreographer; +import android.view.Display; +import android.view.SurfaceControlViewHost; +import android.view.View; +import android.view.WindowInsetsController; import android.view.WindowManager; -import android.view.WindowlessWindowManager; +import android.view.WindowManagerGlobal; +import android.widget.FrameLayout; import android.window.SplashScreenView; +import android.window.SplashScreenView.SplashScreenViewParcelable; import android.window.StartingWindowInfo; import android.window.StartingWindowInfo.StartingWindowType; import android.window.StartingWindowRemovalInfo; import android.window.TaskSnapshot; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.ContrastColorUtil; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import java.util.function.Supplier; + /** * A class which able to draw splash screen or snapshot as the starting window for a task. + * + * In order to speed up, there will use two threads to creating a splash screen in parallel. + * Right now we are still using PhoneWindow to create splash screen window, so the view is added to + * the ViewRootImpl, and those view won't be draw immediately because the ViewRootImpl will call + * scheduleTraversal to register a callback from Choreographer, so the drawing result of the view + * can synchronize on each frame. + * + * The bad thing is that we cannot decide when would Choreographer#doFrame happen, and drawing + * the AdaptiveIconDrawable object can be time consuming, so we use the splash-screen background + * thread to draw the AdaptiveIconDrawable object to a Bitmap and cache it to a BitmapShader after + * the SplashScreenView just created, once we get the BitmapShader then the #draw call can be very + * quickly. + * + * So basically we are using the spare time to prepare the SplashScreenView while splash screen + * thread is waiting for + * 1. WindowManager#addView(binder call to WM), + * 2. Choreographer#doFrame happen(uncertain time for next frame, depends on device), + * 3. Session#relayout(another binder call to WM which under Choreographer#doFrame, but will + * always happen before #draw). + * Because above steps are running on splash-screen thread, so pre-draw the BitmapShader on + * splash-screen background tread can make they execute in parallel, which ensure it is faster then + * to draw the AdaptiveIconDrawable when receive callback from Choreographer#doFrame. + * + * Here is the sequence to compare the difference between using single and two thread. + * + * Single thread: + * => makeSplashScreenContentView -> WM#addView .. waiting for Choreographer#doFrame -> relayout + * -> draw -> AdaptiveIconDrawable#draw + * + * Two threads: + * => makeSplashScreenContentView -> cachePaint(=AdaptiveIconDrawable#draw) + * => WM#addView -> .. waiting for Choreographer#doFrame -> relayout -> draw -> (draw the Paint + * directly). */ @ShellSplashscreenThread public class StartingSurfaceDrawer { + private static final String TAG = StartingWindowController.TAG; + private final Context mContext; + private final DisplayManager mDisplayManager; private final ShellExecutor mSplashScreenExecutor; @VisibleForTesting final SplashscreenContentDrawer mSplashscreenContentDrawer; - @VisibleForTesting - final SplashscreenWindowCreator mSplashscreenWindowCreator; - private final SnapshotWindowCreator mSnapshotWindowCreator; - private final WindowlessSplashWindowCreator mWindowlessSplashWindowCreator; - private final WindowlessSnapshotWindowCreator mWindowlessSnapshotWindowCreator; + private Choreographer mChoreographer; + private final WindowManagerGlobal mWindowManagerGlobal; + private StartingSurface.SysuiProxy mSysuiProxy; + private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo(); + + private static final int LIGHT_BARS_MASK = + WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS + | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; + /** + * The minimum duration during which the splash screen is shown when the splash screen icon is + * animated. + */ + static final long MINIMAL_ANIMATION_DURATION = 400L; + + /** + * Allow the icon style splash screen to be displayed for longer to give time for the animation + * to finish, i.e. the extra buffer time to keep the splash screen if the animation is slightly + * longer than the {@link #MINIMAL_ANIMATION_DURATION} duration. + */ + static final long TIME_WINDOW_DURATION = 100L; + + /** + * The maximum duration during which the splash screen will be shown if the application is ready + * to show before the icon animation finishes. + */ + static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION; - @VisibleForTesting - final StartingWindowRecordManager mWindowRecords = new StartingWindowRecordManager(); - // Windowless surface could co-exist with starting window in a task. - @VisibleForTesting - final StartingWindowRecordManager mWindowlessRecords = new StartingWindowRecordManager(); /** * @param splashScreenExecutor The thread used to control add and remove starting window. */ public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor, IconProvider iconProvider, TransactionPool pool) { + mContext = context; + mDisplayManager = mContext.getSystemService(DisplayManager.class); mSplashScreenExecutor = splashScreenExecutor; - final DisplayManager displayManager = context.getSystemService(DisplayManager.class); - mSplashscreenContentDrawer = new SplashscreenContentDrawer(context, iconProvider, pool); - displayManager.getDisplay(DEFAULT_DISPLAY); - - mSplashscreenWindowCreator = new SplashscreenWindowCreator(mSplashscreenContentDrawer, - context, splashScreenExecutor, displayManager, mWindowRecords); - mSnapshotWindowCreator = new SnapshotWindowCreator(splashScreenExecutor, - mWindowRecords); - mWindowlessSplashWindowCreator = new WindowlessSplashWindowCreator( - mSplashscreenContentDrawer, context, splashScreenExecutor, displayManager, - mWindowlessRecords, pool); - mWindowlessSnapshotWindowCreator = new WindowlessSnapshotWindowCreator( - mWindowlessRecords, context, displayManager, mSplashscreenContentDrawer, pool); + mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconProvider, pool); + mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance()); + mWindowManagerGlobal = WindowManagerGlobal.getInstance(); + mDisplayManager.getDisplay(DEFAULT_DISPLAY); + } + + @VisibleForTesting + final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>(); + + /** + * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is + * rendered and that have not yet been removed by their client. + */ + private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts = + new SparseArray<>(1); + + private Display getDisplay(int displayId) { + return mDisplayManager.getDisplay(displayId); + } + + int getSplashScreenTheme(int splashScreenThemeResId, ActivityInfo activityInfo) { + return splashScreenThemeResId != 0 + ? splashScreenThemeResId + : activityInfo.getThemeResource() != 0 ? activityInfo.getThemeResource() + : com.android.internal.R.style.Theme_DeviceDefault_DayNight; } void setSysuiProxy(StartingSurface.SysuiProxy sysuiProxy) { - mSplashscreenWindowCreator.setSysuiProxy(sysuiProxy); - mWindowlessSplashWindowCreator.setSysuiProxy(sysuiProxy); + mSysuiProxy = sysuiProxy; } /** @@ -97,55 +186,231 @@ public class StartingSurfaceDrawer { * * @param suggestType The suggestion type to draw the splash screen. */ - void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, + void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken, @StartingWindowType int suggestType) { - mSplashscreenWindowCreator.addSplashScreenStartingWindow(windowInfo, suggestType); + final RunningTaskInfo taskInfo = windowInfo.taskInfo; + final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null + ? windowInfo.targetActivityInfo + : taskInfo.topActivityInfo; + if (activityInfo == null || activityInfo.packageName == null) { + return; + } + // replace with the default theme if the application didn't set + final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo); + final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme, + suggestType, mDisplayManager); + if (context == null) { + return; + } + final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters( + context, windowInfo, suggestType, activityInfo.packageName, + suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN + ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, appToken); + + final int displayId = taskInfo.displayId; + final int taskId = taskInfo.taskId; + final Display display = getDisplay(displayId); + + // TODO(b/173975965) tracking performance + // Prepare the splash screen content view on splash screen worker thread in parallel, so the + // content view won't be blocked by binder call like addWindow and relayout. + // 1. Trigger splash screen worker thread to create SplashScreenView before/while + // Session#addWindow. + // 2. Synchronize the SplashscreenView to splash screen thread before Choreographer start + // traversal, which will call Session#relayout on splash screen thread. + // 3. Pre-draw the BitmapShader if the icon is immobile on splash screen worker thread, at + // the same time the splash screen thread should be executing Session#relayout. Blocking the + // traversal -> draw on splash screen thread until the BitmapShader of the icon is ready. + + // Record whether create splash screen view success, notify to current thread after + // create splash screen view finished. + final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier(); + final FrameLayout rootLayout = new FrameLayout( + mSplashscreenContentDrawer.createViewContextWrapper(context)); + rootLayout.setPadding(0, 0, 0, 0); + rootLayout.setFitsSystemWindows(false); + final Runnable setViewSynchronized = () -> { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addSplashScreenView"); + // waiting for setContentView before relayoutWindow + SplashScreenView contentView = viewSupplier.get(); + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + // If record == null, either the starting window added fail or removed already. + // Do not add this view if the token is mismatch. + if (record != null && appToken == record.mAppToken) { + // if view == null then creation of content view was failed. + if (contentView != null) { + try { + rootLayout.addView(contentView); + } catch (RuntimeException e) { + Slog.w(TAG, "failed set content view to starting window " + + "at taskId: " + taskId, e); + contentView = null; + } + } + record.setSplashScreenView(contentView); + } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + }; + if (mSysuiProxy != null) { + mSysuiProxy.requestTopUi(true, TAG); + } + mSplashscreenContentDrawer.createContentView(context, suggestType, windowInfo, + viewSupplier::setView, viewSupplier::setUiThreadInitTask); + try { + if (addWindow(taskId, appToken, rootLayout, display, params, suggestType)) { + // We use the splash screen worker thread to create SplashScreenView while adding + // the window, as otherwise Choreographer#doFrame might be delayed on this thread. + // And since Choreographer#doFrame won't happen immediately after adding the window, + // if the view is not added to the PhoneWindow on the first #doFrame, the view will + // not be rendered on the first frame. So here we need to synchronize the view on + // the window before first round relayoutWindow, which will happen after insets + // animation. + mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null); + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + record.parseAppSystemBarColor(context); + // Block until we get the background color. + final SplashScreenView contentView = viewSupplier.get(); + if (suggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + contentView.addOnAttachStateChangeListener( + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + final int lightBarAppearance = ContrastColorUtil.isColorLight( + contentView.getInitBackgroundColor()) + ? LIGHT_BARS_MASK : 0; + contentView.getWindowInsetsController().setSystemBarsAppearance( + lightBarAppearance, LIGHT_BARS_MASK); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); + } + record.mBGColor = contentView.getInitBackgroundColor(); + } else { + // release the icon view host + final SplashScreenView contentView = viewSupplier.get(); + if (contentView.getSurfaceHost() != null) { + SplashScreenView.releaseIconHost(contentView.getSurfaceHost()); + } + } + } catch (RuntimeException e) { + // don't crash if something else bad happens, for example a + // failure loading resources because we are loading from an app + // on external storage that has been unmounted. + Slog.w(TAG, "failed creating starting window at taskId: " + taskId, e); + } } int getStartingWindowBackgroundColorForTask(int taskId) { - final StartingWindowRecord startingWindowRecord = mWindowRecords.getRecord(taskId); + final StartingWindowRecord startingWindowRecord = mStartingWindowRecords.get(taskId); if (startingWindowRecord == null) { return Color.TRANSPARENT; } - return startingWindowRecord.getBGColor(); + return startingWindowRecord.mBGColor; + } + + private static class SplashScreenViewSupplier implements Supplier<SplashScreenView> { + private SplashScreenView mView; + private boolean mIsViewSet; + private Runnable mUiThreadInitTask; + void setView(SplashScreenView view) { + synchronized (this) { + mView = view; + mIsViewSet = true; + notify(); + } + } + + void setUiThreadInitTask(Runnable initTask) { + synchronized (this) { + mUiThreadInitTask = initTask; + } + } + + @Override + @Nullable + public SplashScreenView get() { + synchronized (this) { + while (!mIsViewSet) { + try { + wait(); + } catch (InterruptedException ignored) { + } + } + if (mUiThreadInitTask != null) { + mUiThreadInitTask.run(); + mUiThreadInitTask = null; + } + return mView; + } + } } int estimateTaskBackgroundColor(TaskInfo taskInfo) { - return mSplashscreenWindowCreator.estimateTaskBackgroundColor(taskInfo); + if (taskInfo.topActivityInfo == null) { + return Color.TRANSPARENT; + } + final ActivityInfo activityInfo = taskInfo.topActivityInfo; + final String packageName = activityInfo.packageName; + final int userId = taskInfo.userId; + final Context windowContext; + try { + windowContext = mContext.createPackageContextAsUser( + packageName, Context.CONTEXT_RESTRICTED, UserHandle.of(userId)); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Failed creating package context with package name " + + packageName + " for user " + taskInfo.userId, e); + return Color.TRANSPARENT; + } + try { + final IPackageManager packageManager = ActivityThread.getPackageManager(); + final String splashScreenThemeName = packageManager.getSplashScreenTheme(packageName, + userId); + final int splashScreenThemeId = splashScreenThemeName != null + ? windowContext.getResources().getIdentifier(splashScreenThemeName, null, null) + : 0; + + final int theme = getSplashScreenTheme(splashScreenThemeId, activityInfo); + + if (theme != windowContext.getThemeResId()) { + windowContext.setTheme(theme); + } + return mSplashscreenContentDrawer.estimateTaskBackgroundColor(windowContext); + } catch (RuntimeException | RemoteException e) { + Slog.w(TAG, "failed get starting window background color at taskId: " + + taskInfo.taskId, e); + } + return Color.TRANSPARENT; } /** * Called when a task need a snapshot starting window. */ - void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, TaskSnapshot snapshot) { - mSnapshotWindowCreator.makeTaskSnapshotWindow(startingWindowInfo, snapshot); + void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, IBinder appToken, + TaskSnapshot snapshot) { + final int taskId = startingWindowInfo.taskInfo.taskId; + // Remove any existing starting window for this task before adding. + removeWindowNoAnimate(taskId); + final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken, + snapshot, mSplashScreenExecutor, () -> removeWindowNoAnimate(taskId)); + if (surface == null) { + return; + } + final StartingWindowRecord tView = new StartingWindowRecord(appToken, + null/* decorView */, surface, STARTING_WINDOW_TYPE_SNAPSHOT); + mStartingWindowRecords.put(taskId, tView); } /** * Called when the content of a task is ready to show, starting window can be removed. */ public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { - if (removalInfo.windowlessSurface) { - mWindowlessRecords.removeWindow(removalInfo, removalInfo.removeImmediately); - } else { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "Task start finish, remove starting surface for task: %d", - removalInfo.taskId); - mWindowRecords.removeWindow(removalInfo, removalInfo.removeImmediately); - } - } - - /** - * Create a windowless starting surface and attach to the root surface. - */ - void addWindowlessStartingSurface(StartingWindowInfo windowInfo) { - if (windowInfo.taskSnapshot != null) { - mWindowlessSnapshotWindowCreator.makeTaskSnapshotWindow(windowInfo, - windowInfo.rootSurface, windowInfo.taskSnapshot, mSplashScreenExecutor); - } else { - mWindowlessSplashWindowCreator.addSplashScreenStartingWindow( - windowInfo, windowInfo.rootSurface); - } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Task start finish, remove starting surface for task: %d", + removalInfo.taskId); + removeWindowSynced(removalInfo, false /* immediately */); } /** @@ -154,15 +419,37 @@ public class StartingSurfaceDrawer { public void clearAllWindows() { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, "Clear all starting windows immediately"); - mWindowRecords.clearAllWindows(); - mWindowlessRecords.clearAllWindows(); + final int taskSize = mStartingWindowRecords.size(); + final int[] taskIds = new int[taskSize]; + for (int i = taskSize - 1; i >= 0; --i) { + taskIds[i] = mStartingWindowRecords.keyAt(i); + } + for (int i = taskSize - 1; i >= 0; --i) { + removeWindowNoAnimate(taskIds[i]); + } } /** * Called when the Task wants to copy the splash screen. */ public void copySplashScreenView(int taskId) { - mSplashscreenWindowCreator.copySplashScreenView(taskId); + final StartingWindowRecord preView = mStartingWindowRecords.get(taskId); + SplashScreenViewParcelable parcelable; + SplashScreenView splashScreenView = preView != null ? preView.mContentView : null; + if (splashScreenView != null && splashScreenView.isCopyable()) { + parcelable = new SplashScreenViewParcelable(splashScreenView); + parcelable.setClientCallback( + new RemoteCallback((bundle) -> mSplashScreenExecutor.execute( + () -> onAppSplashScreenViewRemoved(taskId, false)))); + splashScreenView.onCopied(); + mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost()); + } else { + parcelable = null; + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Copying splash screen window view for task: %d with parcelable %b", + taskId, parcelable != null); + ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable); } /** @@ -172,148 +459,195 @@ public class StartingSurfaceDrawer { * @param taskId The Task id on which the splash screen was attached */ public void onAppSplashScreenViewRemoved(int taskId) { - mSplashscreenWindowCreator.onAppSplashScreenViewRemoved(taskId); + onAppSplashScreenViewRemoved(taskId, true /* fromServer */); } - void onImeDrawnOnTask(int taskId) { - onImeDrawnOnTask(mWindowRecords, taskId); - onImeDrawnOnTask(mWindowlessRecords, taskId); - } - - private void onImeDrawnOnTask(StartingWindowRecordManager records, int taskId) { - final StartingSurfaceDrawer.StartingWindowRecord sRecord = - records.getRecord(taskId); - final SnapshotRecord record = sRecord instanceof SnapshotRecord - ? (SnapshotRecord) sRecord : null; - if (record != null && record.hasImeSurface()) { - records.removeWindow(taskId, true); + /** + * @param fromServer If true, this means the removal was notified by the server. This is only + * used for debugging purposes. + * @see #onAppSplashScreenViewRemoved(int) + */ + private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) { + SurfaceControlViewHost viewHost = + mAnimatedSplashScreenSurfaceHosts.get(taskId); + if (viewHost == null) { + return; } + mAnimatedSplashScreenSurfaceHosts.remove(taskId); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "%s the splash screen. Releasing SurfaceControlViewHost for task: %d", + fromServer ? "Server cleaned up" : "App removed", taskId); + SplashScreenView.releaseIconHost(viewHost); } - static class WindowlessStartingWindow extends WindowlessWindowManager { - SurfaceControl mChildSurface; - - WindowlessStartingWindow(Configuration c, SurfaceControl rootSurface) { - super(c, rootSurface, null /* hostInputToken */); - } - - @Override - protected SurfaceControl getParentSurface(IWindow window, - WindowManager.LayoutParams attrs) { - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) - .setContainerLayer() - .setName("Windowless window") - .setHidden(false) - .setParent(mRootSurface) - .setCallsite("WindowlessStartingWindow#attachToParentSurface"); - mChildSurface = builder.build(); - try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) { - t.setLayer(mChildSurface, Integer.MAX_VALUE); - t.apply(); + protected boolean addWindow(int taskId, IBinder appToken, View view, Display display, + WindowManager.LayoutParams params, @StartingWindowType int suggestType) { + boolean shouldSaveView = true; + final Context context = view.getContext(); + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView"); + mWindowManagerGlobal.addView(view, params, display, + null /* parentWindow */, context.getUserId()); + } catch (WindowManager.BadTokenException e) { + // ignore + Slog.w(TAG, appToken + " already running, starting window not displayed. " + + e.getMessage()); + shouldSaveView = false; + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + if (view.getParent() == null) { + Slog.w(TAG, "view not successfully added to wm, removing view"); + mWindowManagerGlobal.removeView(view, true /* immediate */); + shouldSaveView = false; } - return mChildSurface; } - } - abstract static class StartingWindowRecord { - protected int mBGColor; - abstract void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately); - int getBGColor() { - return mBGColor; + if (shouldSaveView) { + removeWindowNoAnimate(taskId); + saveSplashScreenRecord(appToken, taskId, view, suggestType); } + return shouldSaveView; } - abstract static class SnapshotRecord extends StartingWindowRecord { - private static final long DELAY_REMOVAL_TIME_GENERAL = 100; - /** - * The max delay time in milliseconds for removing the task snapshot window with IME - * visible. - * Ideally the delay time will be shorter when receiving - * {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}. - */ - private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600; - private final Runnable mScheduledRunnable = this::removeImmediately; - - @WindowConfiguration.ActivityType protected final int mActivityType; - protected final ShellExecutor mRemoveExecutor; - - SnapshotRecord(int activityType, ShellExecutor removeExecutor) { - mActivityType = activityType; - mRemoveExecutor = removeExecutor; - } - - @Override - public final void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) { - if (immediately) { - removeImmediately(); - } else { - scheduleRemove(info.deferRemoveForIme); - } - } - - void scheduleRemove(boolean deferRemoveForIme) { - // Show the latest content as soon as possible for unlocking to home. - if (mActivityType == ACTIVITY_TYPE_HOME) { - removeImmediately(); - return; - } - mRemoveExecutor.removeCallbacks(mScheduledRunnable); - final long delayRemovalTime = hasImeSurface() && deferRemoveForIme - ? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE - : DELAY_REMOVAL_TIME_GENERAL; - mRemoveExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "Defer removing snapshot surface in %d", delayRemovalTime); - } + @VisibleForTesting + void saveSplashScreenRecord(IBinder appToken, int taskId, View view, + @StartingWindowType int suggestType) { + final StartingWindowRecord tView = new StartingWindowRecord(appToken, view, + null/* TaskSnapshotWindow */, suggestType); + mStartingWindowRecords.put(taskId, tView); + } - protected abstract boolean hasImeSurface(); + private void removeWindowNoAnimate(int taskId) { + mTmpRemovalInfo.taskId = taskId; + removeWindowSynced(mTmpRemovalInfo, true /* immediately */); + } - @CallSuper - protected void removeImmediately() { - mRemoveExecutor.removeCallbacks(mScheduledRunnable); + void onImeDrawnOnTask(int taskId) { + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + if (record != null && record.mTaskSnapshotWindow != null + && record.mTaskSnapshotWindow.hasImeSurface()) { + removeWindowNoAnimate(taskId); } } - static class StartingWindowRecordManager { - private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo(); - private final SparseArray<StartingWindowRecord> mStartingWindowRecords = - new SparseArray<>(); + protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo, boolean immediately) { + final int taskId = removalInfo.taskId; + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + if (record != null) { + if (record.mDecorView != null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Removing splash screen window for task: %d", taskId); + if (record.mContentView != null) { + record.clearSystemBarColor(); + if (immediately + || record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + removeWindowInner(record.mDecorView, false); + } else { + if (removalInfo.playRevealAnimation) { + mSplashscreenContentDrawer.applyExitAnimation(record.mContentView, + removalInfo.windowAnimationLeash, removalInfo.mainFrame, + () -> removeWindowInner(record.mDecorView, true), + record.mCreateTime, removalInfo.roundedCornerRadius); + } else { + // the SplashScreenView has been copied to client, hide the view to skip + // default exit animation + removeWindowInner(record.mDecorView, true); + } + } + } else { + // shouldn't happen + Slog.e(TAG, "Found empty splash screen, remove!"); + removeWindowInner(record.mDecorView, false); + } - void clearAllWindows() { - final int taskSize = mStartingWindowRecords.size(); - final int[] taskIds = new int[taskSize]; - for (int i = taskSize - 1; i >= 0; --i) { - taskIds[i] = mStartingWindowRecords.keyAt(i); } - for (int i = taskSize - 1; i >= 0; --i) { - removeWindow(taskIds[i], true); + if (record.mTaskSnapshotWindow != null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Removing task snapshot window for %d", taskId); + if (immediately) { + record.mTaskSnapshotWindow.removeImmediately(); + } else { + record.mTaskSnapshotWindow.scheduleRemove(removalInfo.deferRemoveForIme); + } } + mStartingWindowRecords.remove(taskId); } + } - void addRecord(int taskId, StartingWindowRecord record) { - mStartingWindowRecords.put(taskId, record); + private void removeWindowInner(View decorView, boolean hideView) { + if (mSysuiProxy != null) { + mSysuiProxy.requestTopUi(false, TAG); + } + if (hideView) { + decorView.setVisibility(View.GONE); } + mWindowManagerGlobal.removeView(decorView, false /* immediate */); + } - void removeWindow(StartingWindowRemovalInfo removeInfo, boolean immediately) { - final int taskId = removeInfo.taskId; - final StartingWindowRecord record = mStartingWindowRecords.get(taskId); - if (record != null) { - record.removeIfPossible(removeInfo, immediately); - mStartingWindowRecords.remove(taskId); + /** + * Record the view or surface for a starting window. + */ + private static class StartingWindowRecord { + private final IBinder mAppToken; + private final View mDecorView; + private final TaskSnapshotWindow mTaskSnapshotWindow; + private SplashScreenView mContentView; + private boolean mSetSplashScreen; + @StartingWindowType private int mSuggestType; + private int mBGColor; + private final long mCreateTime; + private int mSystemBarAppearance; + private boolean mDrawsSystemBarBackgrounds; + + StartingWindowRecord(IBinder appToken, View decorView, + TaskSnapshotWindow taskSnapshotWindow, @StartingWindowType int suggestType) { + mAppToken = appToken; + mDecorView = decorView; + mTaskSnapshotWindow = taskSnapshotWindow; + if (mTaskSnapshotWindow != null) { + mBGColor = mTaskSnapshotWindow.getBackgroundColor(); } + mSuggestType = suggestType; + mCreateTime = SystemClock.uptimeMillis(); } - void removeWindow(int taskId, boolean immediately) { - mTmpRemovalInfo.taskId = taskId; - removeWindow(mTmpRemovalInfo, immediately); + private void setSplashScreenView(SplashScreenView splashScreenView) { + if (mSetSplashScreen) { + return; + } + mContentView = splashScreenView; + mSetSplashScreen = true; } - StartingWindowRecord getRecord(int taskId) { - return mStartingWindowRecords.get(taskId); + private void parseAppSystemBarColor(Context context) { + final TypedArray a = context.obtainStyledAttributes(R.styleable.Window); + mDrawsSystemBarBackgrounds = a.getBoolean( + R.styleable.Window_windowDrawsSystemBarBackgrounds, false); + if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) { + mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; + } + if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) { + mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; + } + a.recycle(); } - @VisibleForTesting - int recordSize() { - return mStartingWindowRecords.size(); + // Reset the system bar color which set by splash screen, make it align to the app. + private void clearSystemBarColor() { + if (mDecorView == null || !mDecorView.isAttachedToWindow()) { + return; + } + if (mDecorView.getLayoutParams() instanceof WindowManager.LayoutParams) { + final WindowManager.LayoutParams lp = + (WindowManager.LayoutParams) mDecorView.getLayoutParams(); + if (mDrawsSystemBarBackgrounds) { + lp.flags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } else { + lp.flags &= ~WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } + mDecorView.setLayoutParams(lp); + } + mDecorView.getWindowInsetsController().setSystemBarsAppearance( + mSystemBarAppearance, LIGHT_BARS_MASK); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java index bec4ba3bf0d1..be2e79342d07 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -21,7 +21,6 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW; @@ -30,6 +29,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.TaskInfo; import android.content.Context; import android.graphics.Color; +import android.os.IBinder; import android.os.Trace; import android.util.SparseIntArray; import android.window.StartingWindowInfo; @@ -152,23 +152,22 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo /** * Called when a task need a starting window. */ - public void addStartingWindow(StartingWindowInfo windowInfo) { + public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { mSplashScreenExecutor.execute(() -> { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow"); final int suggestionType = mStartingWindowTypeAlgorithm.getSuggestedWindowType( windowInfo); final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo; - if (suggestionType == STARTING_WINDOW_TYPE_WINDOWLESS) { - mStartingSurfaceDrawer.addWindowlessStartingSurface(windowInfo); - } else if (isSplashScreenType(suggestionType)) { - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, suggestionType); + if (isSplashScreenType(suggestionType)) { + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken, + suggestionType); } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) { final TaskSnapshot snapshot = windowInfo.taskSnapshot; - mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, snapshot); + mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, + snapshot); } - if (suggestionType != STARTING_WINDOW_TYPE_NONE - && suggestionType != STARTING_WINDOW_TYPE_WINDOWLESS) { + if (suggestionType != STARTING_WINDOW_TYPE_NONE) { int taskId = runningTaskInfo.taskId; int color = mStartingSurfaceDrawer .getStartingWindowBackgroundColorForTask(taskId); @@ -219,13 +218,11 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { mSplashScreenExecutor.execute(() -> mStartingSurfaceDrawer.removeStartingWindow( removalInfo)); - if (!removalInfo.windowlessSurface) { - mSplashScreenExecutor.executeDelayed(() -> { - synchronized (mTaskBackgroundColors) { - mTaskBackgroundColors.delete(removalInfo.taskId); - } - }, TASK_BG_COLOR_RETAIN_TIME_MS); - } + mSplashScreenExecutor.executeDelayed(() -> { + synchronized (mTaskBackgroundColors) { + mTaskBackgroundColors.delete(removalInfo.taskId); + } + }, TASK_BG_COLOR_RETAIN_TIME_MS); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java index c964df1452e0..a05ed4f24a08 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java @@ -16,6 +16,7 @@ package com.android.wm.shell.startingsurface; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.graphics.Color.WHITE; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; @@ -62,14 +63,24 @@ public class TaskSnapshotWindow { private static final String TAG = StartingWindowController.TAG; private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId="; + private static final long DELAY_REMOVAL_TIME_GENERAL = 100; + /** + * The max delay time in milliseconds for removing the task snapshot window with IME visible. + * Ideally the delay time will be shorter when receiving + * {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}. + */ + private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600; + private final Window mWindow; private final Runnable mClearWindowHandler; private final ShellExecutor mSplashScreenExecutor; private final IWindowSession mSession; private boolean mHasDrawn; private final Paint mBackgroundPaint = new Paint(); + private final int mActivityType; private final int mOrientationOnCreation; + private final Runnable mScheduledRunnable = this::removeImmediately; private final boolean mHasImeSurface; static TaskSnapshotWindow create(StartingWindowInfo info, IBinder appToken, @@ -93,6 +104,7 @@ public class TaskSnapshotWindow { final Point taskSize = snapshot.getTaskSize(); final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y); final int orientation = snapshot.getOrientation(); + final int activityType = runningTaskInfo.topActivityType; final int displayId = runningTaskInfo.displayId; final IWindowSession session = WindowManagerGlobal.getWindowSession(); @@ -102,11 +114,16 @@ public class TaskSnapshotWindow { final InsetsSourceControl.Array tmpControls = new InsetsSourceControl.Array(); final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration(); - final TaskDescription taskDescription = - SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo); + final TaskDescription taskDescription; + if (runningTaskInfo.taskDescription != null) { + taskDescription = runningTaskInfo.taskDescription; + } else { + taskDescription = new TaskDescription(); + taskDescription.setBackgroundColor(WHITE); + } final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow( - snapshot, taskDescription, orientation, + snapshot, taskDescription, orientation, activityType, clearWindowHandler, splashScreenExecutor); final Window window = snapshotSurface.mWindow; @@ -136,8 +153,6 @@ public class TaskSnapshotWindow { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } catch (RemoteException e) { snapshotSurface.clearWindowSynced(); - Slog.w(TAG, "Failed to relayout snapshot starting window"); - return null; } SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot, @@ -149,7 +164,7 @@ public class TaskSnapshotWindow { } public TaskSnapshotWindow(TaskSnapshot snapshot, TaskDescription taskDescription, - int currentOrientation, Runnable clearWindowHandler, + int currentOrientation, int activityType, Runnable clearWindowHandler, ShellExecutor splashScreenExecutor) { mSplashScreenExecutor = splashScreenExecutor; mSession = WindowManagerGlobal.getWindowSession(); @@ -158,6 +173,7 @@ public class TaskSnapshotWindow { int backgroundColor = taskDescription.getBackgroundColor(); mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); mOrientationOnCreation = currentOrientation; + mActivityType = activityType; mClearWindowHandler = clearWindowHandler; mHasImeSurface = snapshot.hasImeSurface(); } @@ -170,7 +186,23 @@ public class TaskSnapshotWindow { return mHasImeSurface; } + void scheduleRemove(boolean deferRemoveForIme) { + // Show the latest content as soon as possible for unlocking to home. + if (mActivityType == ACTIVITY_TYPE_HOME) { + removeImmediately(); + return; + } + mSplashScreenExecutor.removeCallbacks(mScheduledRunnable); + final long delayRemovalTime = mHasImeSurface && deferRemoveForIme + ? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE + : DELAY_REMOVAL_TIME_GENERAL; + mSplashScreenExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Defer removing snapshot surface in %d", delayRemovalTime); + } + void removeImmediately() { + mSplashScreenExecutor.removeCallbacks(mScheduledRunnable); try { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, "Removing taskSnapshot surface, mHasDrawn=%b", mHasDrawn); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java deleted file mode 100644 index 144547885501..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.startingsurface; - -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.app.ActivityManager; -import android.content.Context; -import android.graphics.Point; -import android.graphics.Rect; -import android.hardware.display.DisplayManager; -import android.view.Display; -import android.view.InsetsState; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.WindowManager; -import android.widget.FrameLayout; -import android.window.SnapshotDrawerUtils; -import android.window.StartingWindowInfo; -import android.window.TaskSnapshot; - -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.TransactionPool; - -class WindowlessSnapshotWindowCreator { - private static final int DEFAULT_FADEOUT_DURATION = 233; - private final StartingSurfaceDrawer.StartingWindowRecordManager - mStartingWindowRecordManager; - private final DisplayManager mDisplayManager; - private final Context mContext; - private final SplashscreenContentDrawer mSplashscreenContentDrawer; - private final TransactionPool mTransactionPool; - - WindowlessSnapshotWindowCreator( - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager, - Context context, - DisplayManager displayManager, SplashscreenContentDrawer splashscreenContentDrawer, - TransactionPool transactionPool) { - mStartingWindowRecordManager = startingWindowRecordManager; - mContext = context; - mDisplayManager = displayManager; - mSplashscreenContentDrawer = splashscreenContentDrawer; - mTransactionPool = transactionPool; - } - - void makeTaskSnapshotWindow(StartingWindowInfo info, SurfaceControl rootSurface, - TaskSnapshot snapshot, ShellExecutor removeExecutor) { - final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo; - final int taskId = runningTaskInfo.taskId; - final String title = "Windowless Snapshot " + taskId; - final WindowManager.LayoutParams lp = SnapshotDrawerUtils.createLayoutParameters( - info, title, TYPE_APPLICATION_OVERLAY, snapshot.getHardwareBuffer().getFormat(), - null /* token */); - if (lp == null) { - return; - } - final Display display = mDisplayManager.getDisplay(runningTaskInfo.displayId); - final StartingSurfaceDrawer.WindowlessStartingWindow wlw = - new StartingSurfaceDrawer.WindowlessStartingWindow( - runningTaskInfo.configuration, rootSurface); - final SurfaceControlViewHost mViewHost = new SurfaceControlViewHost( - mContext, display, wlw, "WindowlessSnapshotWindowCreator"); - final Point taskSize = snapshot.getTaskSize(); - final Rect snapshotBounds = new Rect(0, 0, taskSize.x, taskSize.y); - final Rect windowBounds = runningTaskInfo.configuration.windowConfiguration.getBounds(); - final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState; - final FrameLayout rootLayout = new FrameLayout( - mSplashscreenContentDrawer.createViewContextWrapper(mContext)); - mViewHost.setView(rootLayout, lp); - SnapshotDrawerUtils.drawSnapshotOnSurface(info, lp, wlw.mChildSurface, snapshot, - snapshotBounds, windowBounds, topWindowInsetsState, false /* releaseAfterDraw */); - - final ActivityManager.TaskDescription taskDescription = - SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo); - - final SnapshotWindowRecord record = new SnapshotWindowRecord(mViewHost, wlw.mChildSurface, - taskDescription.getBackgroundColor(), snapshot.hasImeSurface(), - runningTaskInfo.topActivityType, removeExecutor); - mStartingWindowRecordManager.addRecord(taskId, record); - info.notifyAddComplete(wlw.mChildSurface); - } - - private class SnapshotWindowRecord extends StartingSurfaceDrawer.SnapshotRecord { - private SurfaceControlViewHost mViewHost; - private SurfaceControl mChildSurface; - private final boolean mHasImeSurface; - - SnapshotWindowRecord(SurfaceControlViewHost viewHost, SurfaceControl childSurface, - int bgColor, boolean hasImeSurface, int activityType, - ShellExecutor removeExecutor) { - super(activityType, removeExecutor); - mViewHost = viewHost; - mChildSurface = childSurface; - mBGColor = bgColor; - mHasImeSurface = hasImeSurface; - } - - @Override - protected void removeImmediately() { - super.removeImmediately(); - fadeoutThenRelease(); - } - - void fadeoutThenRelease() { - final ValueAnimator fadeOutAnimator = ValueAnimator.ofFloat(1f, 0f); - fadeOutAnimator.setDuration(DEFAULT_FADEOUT_DURATION); - final SurfaceControl.Transaction t = mTransactionPool.acquire(); - fadeOutAnimator.addUpdateListener(animation -> { - if (mChildSurface == null || !mChildSurface.isValid()) { - fadeOutAnimator.cancel(); - return; - } - t.setAlpha(mChildSurface, (float) animation.getAnimatedValue()); - t.apply(); - }); - - fadeOutAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - if (mChildSurface == null || !mChildSurface.isValid()) { - fadeOutAnimator.cancel(); - } - } - - @Override - public void onAnimationEnd(Animator animation) { - mTransactionPool.release(t); - if (mChildSurface != null) { - final SurfaceControl.Transaction t = mTransactionPool.acquire(); - t.remove(mChildSurface).apply(); - mTransactionPool.release(t); - mChildSurface = null; - } - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - } - }); - fadeOutAnimator.start(); - } - - @Override - protected boolean hasImeSurface() { - return mHasImeSurface; - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java deleted file mode 100644 index 12a0d4054b4d..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.startingsurface; - -import static android.graphics.Color.WHITE; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; - -import android.app.ActivityManager; -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.hardware.display.DisplayManager; -import android.os.Binder; -import android.os.SystemClock; -import android.view.Display; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.WindowManager; -import android.widget.FrameLayout; -import android.window.SplashScreenView; -import android.window.StartingWindowInfo; -import android.window.StartingWindowRemovalInfo; - -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.TransactionPool; - -class WindowlessSplashWindowCreator extends AbsSplashWindowCreator { - - private final TransactionPool mTransactionPool; - - WindowlessSplashWindowCreator(SplashscreenContentDrawer contentDrawer, - Context context, - ShellExecutor splashScreenExecutor, - DisplayManager displayManager, - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager, - TransactionPool pool) { - super(contentDrawer, context, splashScreenExecutor, displayManager, - startingWindowRecordManager); - mTransactionPool = pool; - } - - void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, SurfaceControl rootSurface) { - final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo; - final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null - ? windowInfo.targetActivityInfo - : taskInfo.topActivityInfo; - if (activityInfo == null || activityInfo.packageName == null) { - return; - } - - final int displayId = taskInfo.displayId; - final Display display = mDisplayManager.getDisplay(displayId); - if (display == null) { - // Can't show splash screen on requested display, so skip showing at all. - return; - } - final Context myContext = SplashscreenContentDrawer.createContext(mContext, windowInfo, - 0 /* theme */, STARTING_WINDOW_TYPE_SPLASH_SCREEN, mDisplayManager); - if (myContext == null) { - return; - } - final StartingSurfaceDrawer.WindowlessStartingWindow wlw = - new StartingSurfaceDrawer.WindowlessStartingWindow( - taskInfo.configuration, rootSurface); - final SurfaceControlViewHost viewHost = new SurfaceControlViewHost( - myContext, display, wlw, "WindowlessSplashWindowCreator"); - final String title = "Windowless Splash " + taskInfo.taskId; - final WindowManager.LayoutParams lp = SplashscreenContentDrawer.createLayoutParameters( - myContext, windowInfo, STARTING_WINDOW_TYPE_SPLASH_SCREEN, title, - PixelFormat.TRANSLUCENT, new Binder()); - final Rect windowBounds = taskInfo.configuration.windowConfiguration.getBounds(); - lp.width = windowBounds.width(); - lp.height = windowBounds.height(); - final ActivityManager.TaskDescription taskDescription; - if (taskInfo.taskDescription != null) { - taskDescription = taskInfo.taskDescription; - } else { - taskDescription = new ActivityManager.TaskDescription(); - taskDescription.setBackgroundColor(WHITE); - } - - final FrameLayout rootLayout = new FrameLayout( - mSplashscreenContentDrawer.createViewContextWrapper(mContext)); - viewHost.setView(rootLayout, lp); - - final int bgColor = taskDescription.getBackgroundColor(); - final SplashScreenView splashScreenView = mSplashscreenContentDrawer - .makeSimpleSplashScreenContentView(myContext, windowInfo, bgColor); - rootLayout.addView(splashScreenView); - final SplashWindowRecord record = new SplashWindowRecord(viewHost, splashScreenView, - wlw.mChildSurface, bgColor); - mStartingWindowRecordManager.addRecord(taskInfo.taskId, record); - windowInfo.notifyAddComplete(wlw.mChildSurface); - } - - private class SplashWindowRecord extends StartingSurfaceDrawer.StartingWindowRecord { - private SurfaceControlViewHost mViewHost; - private final long mCreateTime; - private SurfaceControl mChildSurface; - private final SplashScreenView mSplashView; - - SplashWindowRecord(SurfaceControlViewHost viewHost, SplashScreenView splashView, - SurfaceControl childSurface, int bgColor) { - mViewHost = viewHost; - mSplashView = splashView; - mChildSurface = childSurface; - mBGColor = bgColor; - mCreateTime = SystemClock.uptimeMillis(); - } - - @Override - public void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) { - if (!immediately) { - mSplashscreenContentDrawer.applyExitAnimation(mSplashView, - info.windowAnimationLeash, info.mainFrame, - this::release, mCreateTime, 0 /* roundedCornerRadius */); - } else { - release(); - } - } - - void release() { - if (mChildSurface != null) { - final SurfaceControl.Transaction t = mTransactionPool.acquire(); - t.remove(mChildSurface).apply(); - mTransactionPool.release(t); - mChildSurface = null; - } - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java index 72fc8686f648..bb43d7c1a090 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java @@ -22,7 +22,6 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT; @@ -31,7 +30,6 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS; import android.window.StartingWindowInfo; @@ -57,7 +55,6 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor final boolean legacySplashScreen = ((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0); final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0; - final boolean windowlessSurface = (parameter & TYPE_PARAMETER_WINDOWLESS) != 0; final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, @@ -70,15 +67,10 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor + "isSolidColorSplashScreen=%b, " + "legacySplashScreen=%b, " + "activityDrawn=%b, " - + "windowless=%b, " + "topIsHome=%b", newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated, - isSolidColorSplashScreen, legacySplashScreen, activityDrawn, windowlessSurface, - topIsHome); + isSolidColorSplashScreen, legacySplashScreen, activityDrawn, topIsHome); - if (windowlessSurface) { - return STARTING_WINDOW_TYPE_WINDOWLESS; - } if (!topIsHome) { if (!processRunning || newTask || (taskSwitch && !activityCreated)) { return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 766c4cd1cdf7..2aa6d1217fa6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -20,10 +20,14 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; + import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.content.Context; +import android.graphics.Rect; import android.hardware.input.InputManager; import android.os.Handler; import android.os.Looper; @@ -37,7 +41,6 @@ import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.View; import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; import androidx.annotation.Nullable; @@ -50,6 +53,7 @@ import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; +import com.android.wm.shell.splitscreen.SplitScreenController; import java.util.Optional; @@ -80,6 +84,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final InputMonitorFactory mInputMonitorFactory; private TaskOperations mTaskOperations; + private Optional<SplitScreenController> mSplitScreenController; + public DesktopModeWindowDecorViewModel( Context context, Handler mainHandler, @@ -88,7 +94,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { DisplayController displayController, SyncTransactionQueue syncQueue, Optional<DesktopModeController> desktopModeController, - Optional<DesktopTasksController> desktopTasksController) { + Optional<DesktopTasksController> desktopTasksController, + Optional<SplitScreenController> splitScreenController) { this( context, mainHandler, @@ -98,6 +105,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { syncQueue, desktopModeController, desktopTasksController, + splitScreenController, new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory()); } @@ -112,6 +120,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { SyncTransactionQueue syncQueue, Optional<DesktopModeController> desktopModeController, Optional<DesktopTasksController> desktopTasksController, + Optional<SplitScreenController> splitScreenController, DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory) { mContext = context; @@ -120,6 +129,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class); mTaskOrganizer = taskOrganizer; mDisplayController = displayController; + mSplitScreenController = splitScreenController; mSyncQueue = syncQueue; mDesktopModeController = desktopModeController; mDesktopTasksController = desktopTasksController; @@ -230,6 +240,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final int id = v.getId(); if (id == R.id.close_window || id == R.id.close_button) { mTaskOperations.closeTask(mTaskToken); + if (mSplitScreenController.isPresent() + && mSplitScreenController.get().isSplitScreenVisible()) { + int remainingTaskPosition = mTaskId == mSplitScreenController.get() + .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT).taskId + ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; + ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController.get() + .getTaskInfo(remainingTaskPosition); + mSplitScreenController.get().moveTaskToFullscreen(remainingTask.taskId); + } } else if (id == R.id.back_button) { mTaskOperations.injectBackKey(); } else if (id == R.id.caption_handle) { @@ -261,9 +280,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { if (taskInfo.isFocused) { return mDragDetector.isDragEvent(); } - final WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.reorder(mTaskToken, true /* onTop */); - mSyncQueue.queue(wct); return false; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: @@ -401,14 +417,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { * @param ev the {@link MotionEvent} received by {@link EventReceiver} */ private void handleReceivedMotionEvent(MotionEvent ev, InputMonitor inputMonitor) { + final DesktopModeWindowDecoration relevantDecor = getRelevantWindowDecor(ev); if (DesktopModeStatus.isProto2Enabled()) { - final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); - if (focusedDecor == null - || focusedDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) { - handleCaptionThroughStatusBar(ev); + if (relevantDecor == null + || relevantDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) { + handleCaptionThroughStatusBar(ev, relevantDecor); } } - handleEventOutsideFocusedCaption(ev); + handleEventOutsideFocusedCaption(ev, relevantDecor); // Prevent status bar from reacting to a caption drag. if (DesktopModeStatus.isProto2Enabled()) { if (mTransitionDragActive) { @@ -422,16 +438,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } // If an UP/CANCEL action is received outside of caption bounds, turn off handle menu - private void handleEventOutsideFocusedCaption(MotionEvent ev) { + private void handleEventOutsideFocusedCaption(MotionEvent ev, + DesktopModeWindowDecoration relevantDecor) { final int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); - if (focusedDecor == null) { + if (relevantDecor == null) { return; } if (!mTransitionDragActive) { - focusedDecor.closeHandleMenuIfNeeded(ev); + relevantDecor.closeHandleMenuIfNeeded(ev); } } } @@ -441,39 +457,38 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { * Perform caption actions if not able to through normal means. * Turn on desktop mode if handle is dragged below status bar. */ - private void handleCaptionThroughStatusBar(MotionEvent ev) { + private void handleCaptionThroughStatusBar(MotionEvent ev, + DesktopModeWindowDecoration relevantDecor) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: { // Begin drag through status bar if applicable. - final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); - if (focusedDecor != null) { + if (relevantDecor != null) { boolean dragFromStatusBarAllowed = false; if (DesktopModeStatus.isProto2Enabled()) { // In proto2 any full screen task can be dragged to freeform - dragFromStatusBarAllowed = focusedDecor.mTaskInfo.getWindowingMode() + dragFromStatusBarAllowed = relevantDecor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN; } - if (dragFromStatusBarAllowed && focusedDecor.checkTouchEventInHandle(ev)) { + if (dragFromStatusBarAllowed && relevantDecor.checkTouchEventInHandle(ev)) { mTransitionDragActive = true; } } break; } case MotionEvent.ACTION_UP: { - final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); - if (focusedDecor == null) { + if (relevantDecor == null) { mTransitionDragActive = false; return; } if (mTransitionDragActive) { mTransitionDragActive = false; final int statusBarHeight = mDisplayController - .getDisplayLayout(focusedDecor.mTaskInfo.displayId).stableInsets().top; + .getDisplayLayout(relevantDecor.mTaskInfo.displayId).stableInsets().top; if (ev.getY() > statusBarHeight) { if (DesktopModeStatus.isProto2Enabled()) { mDesktopTasksController.ifPresent( - c -> c.moveToDesktop(focusedDecor.mTaskInfo)); + c -> c.moveToDesktop(relevantDecor.mTaskInfo)); } else if (DesktopModeStatus.isProto1Enabled()) { mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); } @@ -481,7 +496,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return; } } - focusedDecor.checkClickEvent(ev); + relevantDecor.checkClickEvent(ev); break; } case MotionEvent.ACTION_CANCEL: { @@ -491,6 +506,38 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } @Nullable + private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) { + if (mSplitScreenController.isPresent() + && mSplitScreenController.get().isSplitScreenVisible()) { + // We can't look at focused task here as only one task will have focus. + return getSplitScreenDecor(ev); + } else { + return getFocusedDecor(); + } + } + + @Nullable + private DesktopModeWindowDecoration getSplitScreenDecor(MotionEvent ev) { + ActivityManager.RunningTaskInfo topOrLeftTask = + mSplitScreenController.get().getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT); + ActivityManager.RunningTaskInfo bottomOrRightTask = + mSplitScreenController.get().getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT); + if (topOrLeftTask != null && topOrLeftTask.getConfiguration() + .windowConfiguration.getBounds().contains((int) ev.getX(), (int) ev.getY())) { + return mWindowDecorByTaskId.get(topOrLeftTask.taskId); + } else if (bottomOrRightTask != null && bottomOrRightTask.getConfiguration() + .windowConfiguration.getBounds().contains((int) ev.getX(), (int) ev.getY())) { + Rect bottomOrRightBounds = bottomOrRightTask.getConfiguration().windowConfiguration + .getBounds(); + ev.offsetLocation(-bottomOrRightBounds.left, -bottomOrRightBounds.top); + return mWindowDecorByTaskId.get(bottomOrRightTask.taskId); + } else { + return null; + } + + } + + @Nullable private DesktopModeWindowDecoration getFocusedDecor() { final int size = mWindowDecorByTaskId.size(); DesktopModeWindowDecoration focusedDecor = null; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java index e8784d77be1b..bc0d93a6810a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java @@ -475,6 +475,36 @@ public class CompatUIControllerTest extends ShellTestCase { verify(mMockRestartDialogLayout).updateVisibility(true); } + @Test + public void testRestartLayoutRecreatedIfNeeded() { + final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, + /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN); + doReturn(true).when(mMockRestartDialogLayout) + .needsToBeRecreated(any(TaskInfo.class), + any(ShellTaskOrganizer.TaskListener.class)); + + mController.onCompatInfoChanged(taskInfo, mMockTaskListener); + mController.onCompatInfoChanged(taskInfo, mMockTaskListener); + + verify(mMockRestartDialogLayout, times(2)) + .createLayout(anyBoolean()); + } + + @Test + public void testRestartLayoutNotRecreatedIfNotNeeded() { + final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, + /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN); + doReturn(false).when(mMockRestartDialogLayout) + .needsToBeRecreated(any(TaskInfo.class), + any(ShellTaskOrganizer.TaskListener.class)); + + mController.onCompatInfoChanged(taskInfo, mMockTaskListener); + mController.onCompatInfoChanged(taskInfo, mMockTaskListener); + + verify(mMockRestartDialogLayout, times(1)) + .createLayout(anyBoolean()); + } + private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat, @CameraCompatControlState int cameraCompatControlState) { RunningTaskInfo taskInfo = new RunningTaskInfo(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java index b6dbcf204364..523cb6629d9a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java @@ -48,7 +48,6 @@ import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; @@ -82,7 +81,7 @@ public class DragAndDropControllerTest extends ShellTestCase { @Mock private ShellExecutor mMainExecutor; @Mock - private SplitScreenController mSplitScreenController; + private WindowManager mWindowManager; private DragAndDropController mController; @@ -100,11 +99,6 @@ public class DragAndDropControllerTest extends ShellTestCase { } @Test - public void instantiateController_registerConfigChangeListener() { - verify(mShellController, times(1)).addConfigurationChangeListener(any()); - } - - @Test public void testIgnoreNonDefaultDisplays() { final int nonDefaultDisplayId = 12345; final View dragLayout = mock(View.class); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index 0bb809d354dc..2e2e49e40030 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -134,6 +134,7 @@ public class StageCoordinatorTests extends ShellTestCase { when(mSplitLayout.getBounds2()).thenReturn(mBounds2); when(mSplitLayout.getRootBounds()).thenReturn(mRootBounds); when(mSplitLayout.isLandscape()).thenReturn(false); + when(mSplitLayout.applyTaskChanges(any(), any(), any())).thenReturn(true); mRootTask = new TestRunningTaskInfoBuilder().build(); mRootLeash = new SurfaceControl.Builder(mSurfaceSession).setName("test").build(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java index bf62acfc47a1..11fda8bf7bbc 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java @@ -24,8 +24,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.wm.shell.startingsurface.SplashscreenContentDrawer.MAX_ANIMATION_DURATION; -import static com.android.wm.shell.startingsurface.SplashscreenContentDrawer.MINIMAL_ANIMATION_DURATION; +import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MAX_ANIMATION_DURATION; +import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MINIMAL_ANIMATION_DURATION; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -56,9 +56,11 @@ import android.os.IBinder; import android.os.Looper; import android.os.UserHandle; import android.testing.TestableContext; +import android.view.Display; import android.view.IWindowSession; import android.view.InsetsState; import android.view.Surface; +import android.view.View; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowMetrics; @@ -104,7 +106,36 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { private ShellExecutor mTestExecutor; private final TestableContext mTestContext = new TestContext( InstrumentationRegistry.getInstrumentation().getTargetContext()); - StartingSurfaceDrawer mStartingSurfaceDrawer; + TestStartingSurfaceDrawer mStartingSurfaceDrawer; + + static final class TestStartingSurfaceDrawer extends StartingSurfaceDrawer{ + int mAddWindowForTask = 0; + + TestStartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor, + IconProvider iconProvider, TransactionPool pool) { + super(context, splashScreenExecutor, iconProvider, pool); + } + + @Override + protected boolean addWindow(int taskId, IBinder appToken, View view, Display display, + WindowManager.LayoutParams params, int suggestType) { + // listen for addView + mAddWindowForTask = taskId; + saveSplashScreenRecord(appToken, taskId, view, suggestType); + // Do not wait for background color + return false; + } + + @Override + protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo, + boolean immediately) { + // listen for removeView + if (mAddWindowForTask == removalInfo.taskId) { + mAddWindowForTask = 0; + } + mStartingWindowRecords.remove(removalInfo.taskId); + } + } private static class TestContext extends TestableContext { TestContext(Context context) { @@ -134,51 +165,44 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { doReturn(metrics).when(mMockWindowManager).getMaximumWindowMetrics(); doNothing().when(mMockWindowManager).addView(any(), any()); mTestExecutor = new HandlerExecutor(mTestHandler); - mStartingSurfaceDrawer = new StartingSurfaceDrawer(mTestContext, mTestExecutor, - mIconProvider, mTransactionPool); mStartingSurfaceDrawer = spy( - new StartingSurfaceDrawer(mTestContext, mTestExecutor, mIconProvider, + new TestStartingSurfaceDrawer(mTestContext, mTestExecutor, mIconProvider, mTransactionPool)); - spyOn(mStartingSurfaceDrawer.mSplashscreenWindowCreator); - spyOn(mStartingSurfaceDrawer.mWindowRecords); - spyOn(mStartingSurfaceDrawer.mWindowlessRecords); } @Test public void testAddSplashScreenSurface() { final int taskId = 1; final StartingWindowInfo windowInfo = - createWindowInfo(taskId, android.R.style.Theme, mBinder); - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, + createWindowInfo(taskId, android.R.style.Theme); + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, STARTING_WINDOW_TYPE_SPLASH_SCREEN); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer.mSplashscreenWindowCreator).addWindow( - eq(taskId), eq(mBinder), any(), any(), any(), + verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(), eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN)); + assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId); StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo(); removalInfo.taskId = windowInfo.taskInfo.taskId; mStartingSurfaceDrawer.removeStartingWindow(removalInfo); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer.mWindowRecords).removeWindow(any(), eq(false)); - assertEquals(mStartingSurfaceDrawer.mWindowRecords.recordSize(), 0); + verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(false)); + assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0); } @Test public void testFallbackDefaultTheme() { final int taskId = 1; final StartingWindowInfo windowInfo = - createWindowInfo(taskId, 0, mBinder); + createWindowInfo(taskId, 0); final int[] theme = new int[1]; doAnswer(invocation -> theme[0] = (Integer) invocation.callRealMethod()) - .when(mStartingSurfaceDrawer.mSplashscreenWindowCreator) - .getSplashScreenTheme(eq(0), any()); + .when(mStartingSurfaceDrawer).getSplashScreenTheme(eq(0), any()); - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, STARTING_WINDOW_TYPE_SPLASH_SCREEN); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer.mSplashscreenWindowCreator) - .getSplashScreenTheme(eq(0), any()); + verify(mStartingSurfaceDrawer).getSplashScreenTheme(eq(0), any()); assertNotEquals(theme[0], 0); } @@ -217,7 +241,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { public void testRemoveTaskSnapshotWithImeSurfaceWhenOnImeDrawn() throws Exception { final int taskId = 1; final StartingWindowInfo windowInfo = - createWindowInfo(taskId, android.R.style.Theme, mBinder); + createWindowInfo(taskId, android.R.style.Theme); TaskSnapshot snapshot = createTaskSnapshot(100, 100, new Point(100, 100), new Rect(0, 0, 0, 50), true /* hasImeSurface */); final IWindowSession session = WindowManagerGlobal.getWindowSession(); @@ -246,7 +270,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { when(TaskSnapshotWindow.create(eq(windowInfo), eq(mBinder), eq(snapshot), any(), any())).thenReturn(mockSnapshotWindow); // Simulate a task snapshot window created with IME snapshot shown. - mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, snapshot); + mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, mBinder, snapshot); waitHandlerIdle(mTestHandler); // Verify the task snapshot with IME snapshot will be removed when received the real IME @@ -254,36 +278,27 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { // makeTaskSnapshotWindow shall call removeWindowSynced before there add a new // StartingWindowRecord for the task. mStartingSurfaceDrawer.onImeDrawnOnTask(1); - verify(mStartingSurfaceDrawer.mWindowRecords, times(2)) - .removeWindow(any(), eq(true)); + verify(mStartingSurfaceDrawer, times(2)) + .removeWindowSynced(any(), eq(true)); } } @Test public void testClearAllWindows() { final int taskId = 1; - mStartingSurfaceDrawer.mWindowRecords.addRecord(taskId, - new StartingSurfaceDrawer.StartingWindowRecord() { - @Override - public void removeIfPossible(StartingWindowRemovalInfo info, - boolean immediately) { - - } - }); - mStartingSurfaceDrawer.mWindowlessRecords.addRecord(taskId, - new StartingSurfaceDrawer.StartingWindowRecord() { - @Override - public void removeIfPossible(StartingWindowRemovalInfo info, - boolean immediately) { + final StartingWindowInfo windowInfo = + createWindowInfo(taskId, android.R.style.Theme); + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, + STARTING_WINDOW_TYPE_SPLASH_SCREEN); + waitHandlerIdle(mTestHandler); + verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(), + eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN)); + assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId); - } - }); mStartingSurfaceDrawer.clearAllWindows(); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer.mWindowRecords).removeWindow(any(), eq(true)); - assertEquals(mStartingSurfaceDrawer.mWindowRecords.recordSize(), 0); - verify(mStartingSurfaceDrawer.mWindowlessRecords).removeWindow(any(), eq(true)); - assertEquals(mStartingSurfaceDrawer.mWindowlessRecords.recordSize(), 0); + verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(true)); + assertEquals(mStartingSurfaceDrawer.mStartingWindowRecords.size(), 0); } @Test @@ -336,7 +351,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { longAppDuration, longAppDuration)); } - private StartingWindowInfo createWindowInfo(int taskId, int themeResId, IBinder appToken) { + private StartingWindowInfo createWindowInfo(int taskId, int themeResId) { StartingWindowInfo windowInfo = new StartingWindowInfo(); final ActivityInfo info = new ActivityInfo(); info.applicationInfo = new ApplicationInfo(); @@ -345,7 +360,6 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); taskInfo.topActivityInfo = info; taskInfo.taskId = taskId; - windowInfo.appToken = appToken; windowInfo.targetActivityInfo = info; windowInfo.taskInfo = taskInfo; windowInfo.topOpaqueWindowInsetsState = new InsetsState(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java index 355072116cb1..1d1aa795173c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java @@ -49,6 +49,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopTasksController; +import com.android.wm.shell.splitscreen.SplitScreenController; import org.junit.Before; import org.junit.Test; @@ -73,6 +74,7 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { @Mock private Choreographer mMainChoreographer; @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private DisplayController mDisplayController; + @Mock private SplitScreenController mSplitScreenController; @Mock private SyncTransactionQueue mSyncQueue; @Mock private DesktopModeController mDesktopModeController; @Mock private DesktopTasksController mDesktopTasksController; @@ -98,6 +100,7 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { mSyncQueue, Optional.of(mDesktopModeController), Optional.of(mDesktopTasksController), + Optional.of(mSplitScreenController), mDesktopModeWindowDecorFactory, mMockInputMonitorFactory ); diff --git a/libs/dream/OWNERS b/libs/dream/OWNERS new file mode 100644 index 000000000000..a4b0127d23b1 --- /dev/null +++ b/libs/dream/OWNERS @@ -0,0 +1 @@ +include /core/java/android/service/dreams/OWNERS
\ No newline at end of file diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index bcbe706d71a3..536bb49675f1 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -332,6 +332,7 @@ cc_defaults { "jni/android_graphics_Matrix.cpp", "jni/android_graphics_Picture.cpp", "jni/android_graphics_DisplayListCanvas.cpp", + "jni/android_graphics_Mesh.cpp", "jni/android_graphics_RenderNode.cpp", "jni/android_nio_utils.cpp", "jni/android_util_PathParser.cpp", @@ -351,7 +352,6 @@ cc_defaults { "jni/ImageDecoder.cpp", "jni/Interpolator.cpp", "jni/MeshSpecification.cpp", - "jni/Mesh.cpp", "jni/MaskFilter.cpp", "jni/NinePatch.cpp", "jni/NinePatchPeeker.cpp", @@ -538,6 +538,7 @@ cc_defaults { "Interpolator.cpp", "LightingInfo.cpp", "Matrix.cpp", + "Mesh.cpp", "MemoryPolicy.cpp", "PathParser.cpp", "Properties.cpp", diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index e2127efca716..a18ba1c633b9 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -52,4 +52,5 @@ X(DrawShadowRec) X(DrawVectorDrawable) X(DrawRippleDrawable) X(DrawWebView) -X(DrawMesh) +X(DrawSkMesh) +X(DrawMesh)
\ No newline at end of file diff --git a/libs/hwui/MemoryPolicy.h b/libs/hwui/MemoryPolicy.h index 2f0f7f506447..41ced8cebf83 100644 --- a/libs/hwui/MemoryPolicy.h +++ b/libs/hwui/MemoryPolicy.h @@ -53,8 +53,8 @@ struct MemoryPolicy { // Whether or not to only purge scratch resources when triggering UI Hidden or background // collection bool purgeScratchOnly = true; - // Whether or not to trigger releasing GPU context when all contexts are stopped - bool releaseContextOnStoppedOnly = true; + // EXPERIMENTAL: Whether or not to trigger releasing GPU context when all contexts are stopped + bool releaseContextOnStoppedOnly = false; }; const MemoryPolicy& loadMemoryPolicy(); diff --git a/libs/hwui/Mesh.cpp b/libs/hwui/Mesh.cpp new file mode 100644 index 000000000000..e59bc9565a59 --- /dev/null +++ b/libs/hwui/Mesh.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2022 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. + */ + +#include "Mesh.h" + +#include <GLES/gl.h> +#include <SkMesh.h> + +#include "SafeMath.h" + +static size_t min_vcount_for_mode(SkMesh::Mode mode) { + switch (mode) { + case SkMesh::Mode::kTriangles: + return 3; + case SkMesh::Mode::kTriangleStrip: + return 3; + } +} + +// Re-implementation of SkMesh::validate to validate user side that their mesh is valid. +std::tuple<bool, SkString> Mesh::validate() { +#define FAIL_MESH_VALIDATE(...) return std::make_tuple(false, SkStringPrintf(__VA_ARGS__)) + if (!mMeshSpec) { + FAIL_MESH_VALIDATE("MeshSpecification is required."); + } + if (mVertexBufferData.empty()) { + FAIL_MESH_VALIDATE("VertexBuffer is required."); + } + + auto meshStride = mMeshSpec->stride(); + auto meshMode = SkMesh::Mode(mMode); + SafeMath sm; + size_t vsize = sm.mul(meshStride, mVertexCount); + if (sm.add(vsize, mVertexOffset) > mVertexBufferData.size()) { + FAIL_MESH_VALIDATE( + "The vertex buffer offset and vertex count reads beyond the end of the" + " vertex buffer."); + } + + if (mVertexOffset % meshStride != 0) { + FAIL_MESH_VALIDATE("The vertex offset (%zu) must be a multiple of the vertex stride (%zu).", + mVertexOffset, meshStride); + } + + if (size_t uniformSize = mMeshSpec->uniformSize()) { + if (!mBuilder->fUniforms || mBuilder->fUniforms->size() < uniformSize) { + FAIL_MESH_VALIDATE("The uniform data is %zu bytes but must be at least %zu.", + mBuilder->fUniforms->size(), uniformSize); + } + } + + auto modeToStr = [](SkMesh::Mode m) { + switch (m) { + case SkMesh::Mode::kTriangles: + return "triangles"; + case SkMesh::Mode::kTriangleStrip: + return "triangle-strip"; + } + }; + if (!mIndexBufferData.empty()) { + if (mIndexCount < min_vcount_for_mode(meshMode)) { + FAIL_MESH_VALIDATE("%s mode requires at least %zu indices but index count is %zu.", + modeToStr(meshMode), min_vcount_for_mode(meshMode), mIndexCount); + } + size_t isize = sm.mul(sizeof(uint16_t), mIndexCount); + if (sm.add(isize, mIndexOffset) > mIndexBufferData.size()) { + FAIL_MESH_VALIDATE( + "The index buffer offset and index count reads beyond the end of the" + " index buffer."); + } + // If we allow 32 bit indices then this should enforce 4 byte alignment in that case. + if (!SkIsAlign2(mIndexOffset)) { + FAIL_MESH_VALIDATE("The index offset must be a multiple of 2."); + } + } else { + if (mVertexCount < min_vcount_for_mode(meshMode)) { + FAIL_MESH_VALIDATE("%s mode requires at least %zu vertices but vertex count is %zu.", + modeToStr(meshMode), min_vcount_for_mode(meshMode), mVertexCount); + } + SkASSERT(!fICount); + SkASSERT(!fIOffset); + } + + if (!sm.ok()) { + FAIL_MESH_VALIDATE("Overflow"); + } +#undef FAIL_MESH_VALIDATE + return {true, {}}; +} diff --git a/libs/hwui/Mesh.h b/libs/hwui/Mesh.h new file mode 100644 index 000000000000..983681707415 --- /dev/null +++ b/libs/hwui/Mesh.h @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2022 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. + */ + +#ifndef MESH_H_ +#define MESH_H_ + +#include <GrDirectContext.h> +#include <SkMesh.h> +#include <jni.h> +#include <log/log.h> + +#include <utility> + +class MeshUniformBuilder { +public: + struct MeshUniform { + template <typename T> + std::enable_if_t<std::is_trivially_copyable<T>::value, MeshUniform> operator=( + const T& val) { + if (!fVar) { + LOG_FATAL("Assigning to missing variable"); + } else if (sizeof(val) != fVar->sizeInBytes()) { + LOG_FATAL("Incorrect value size"); + } else { + void* dst = reinterpret_cast<void*>( + reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset); + memcpy(dst, &val, sizeof(val)); + } + } + + MeshUniform& operator=(const SkMatrix& val) { + if (!fVar) { + LOG_FATAL("Assigning to missing variable"); + } else if (fVar->sizeInBytes() != 9 * sizeof(float)) { + LOG_FATAL("Incorrect value size"); + } else { + float* data = reinterpret_cast<float*>( + reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset); + data[0] = val.get(0); + data[1] = val.get(3); + data[2] = val.get(6); + data[3] = val.get(1); + data[4] = val.get(4); + data[5] = val.get(7); + data[6] = val.get(2); + data[7] = val.get(5); + data[8] = val.get(8); + } + return *this; + } + + template <typename T> + bool set(const T val[], const int count) { + static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable"); + if (!fVar) { + LOG_FATAL("Assigning to missing variable"); + return false; + } else if (sizeof(T) * count != fVar->sizeInBytes()) { + LOG_FATAL("Incorrect value size"); + return false; + } else { + void* dst = reinterpret_cast<void*>( + reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset); + memcpy(dst, val, sizeof(T) * count); + } + return true; + } + + MeshUniformBuilder* fOwner; + const SkRuntimeEffect::Uniform* fVar; + }; + MeshUniform uniform(std::string_view name) { return {this, fMeshSpec->findUniform(name)}; } + + explicit MeshUniformBuilder(sk_sp<SkMeshSpecification> meshSpec) { + fMeshSpec = sk_sp(meshSpec); + fUniforms = (SkData::MakeZeroInitialized(meshSpec->uniformSize())); + } + + sk_sp<SkData> fUniforms; + +private: + void* writableUniformData() { + if (!fUniforms->unique()) { + fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size()); + } + return fUniforms->writable_data(); + } + + sk_sp<SkMeshSpecification> fMeshSpec; +}; + +class Mesh { +public: + Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode, const void* vertexBuffer, + size_t vertexBufferSize, jint vertexCount, jint vertexOffset, + std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds) + : mMeshSpec(meshSpec) + , mMode(mode) + , mVertexCount(vertexCount) + , mVertexOffset(vertexOffset) + , mBuilder(std::move(builder)) + , mBounds(bounds) { + copyToVector(mVertexBufferData, vertexBuffer, vertexBufferSize); + } + + Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode, const void* vertexBuffer, + size_t vertexBufferSize, jint vertexCount, jint vertexOffset, const void* indexBuffer, + size_t indexBufferSize, jint indexCount, jint indexOffset, + std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds) + : mMeshSpec(meshSpec) + , mMode(mode) + , mVertexCount(vertexCount) + , mVertexOffset(vertexOffset) + , mIndexCount(indexCount) + , mIndexOffset(indexOffset) + , mBuilder(std::move(builder)) + , mBounds(bounds) { + copyToVector(mVertexBufferData, vertexBuffer, vertexBufferSize); + copyToVector(mIndexBufferData, indexBuffer, indexBufferSize); + } + + Mesh(Mesh&&) = default; + + Mesh& operator=(Mesh&&) = default; + + [[nodiscard]] std::tuple<bool, SkString> validate(); + + void updateSkMesh(GrDirectContext* context) const { + GrDirectContext::DirectContextID genId = GrDirectContext::DirectContextID(); + if (context) { + genId = context->directContextID(); + } + + if (mIsDirty || genId != mGenerationId) { + auto vb = SkMesh::MakeVertexBuffer( + context, reinterpret_cast<const void*>(mVertexBufferData.data()), + mVertexBufferData.size()); + auto meshMode = SkMesh::Mode(mMode); + if (!mIndexBufferData.empty()) { + auto ib = SkMesh::MakeIndexBuffer( + context, reinterpret_cast<const void*>(mIndexBufferData.data()), + mIndexBufferData.size()); + mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset, + ib, mIndexCount, mIndexOffset, mBuilder->fUniforms, + mBounds) + .mesh; + } else { + mMesh = SkMesh::Make(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset, + mBuilder->fUniforms, mBounds) + .mesh; + } + mIsDirty = false; + mGenerationId = genId; + } + } + + SkMesh& getSkMesh() const { + LOG_FATAL_IF(mIsDirty, + "Attempt to obtain SkMesh when Mesh is dirty, did you " + "forget to call updateSkMesh with a GrDirectContext? " + "Defensively creating a CPU mesh"); + return mMesh; + } + + void markDirty() { mIsDirty = true; } + + MeshUniformBuilder* uniformBuilder() { return mBuilder.get(); } + +private: + void copyToVector(std::vector<uint8_t>& dst, const void* src, size_t srcSize) { + if (src) { + dst.resize(srcSize); + memcpy(dst.data(), src, srcSize); + } + } + + sk_sp<SkMeshSpecification> mMeshSpec; + int mMode = 0; + + std::vector<uint8_t> mVertexBufferData; + size_t mVertexCount = 0; + size_t mVertexOffset = 0; + + std::vector<uint8_t> mIndexBufferData; + size_t mIndexCount = 0; + size_t mIndexOffset = 0; + + std::unique_ptr<MeshUniformBuilder> mBuilder; + SkRect mBounds{}; + + mutable SkMesh mMesh{}; + mutable bool mIsDirty = true; + mutable GrDirectContext::DirectContextID mGenerationId = GrDirectContext::DirectContextID(); +}; +#endif // MESH_H_ diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 659aec0fdf58..0b58406516e3 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -24,6 +24,7 @@ #include <experimental/type_traits> #include <utility> +#include "Mesh.h" #include "SkAndroidFrameworkUtils.h" #include "SkBlendMode.h" #include "SkCanvas.h" @@ -502,14 +503,14 @@ struct DrawVertices final : Op { c->drawVertices(vertices, mode, paint); } }; -struct DrawMesh final : Op { - static const auto kType = Type::DrawMesh; - DrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) +struct DrawSkMesh final : Op { + static const auto kType = Type::DrawSkMesh; + DrawSkMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) : cpuMesh(mesh), blender(std::move(blender)), paint(paint) { isGpuBased = false; } - SkMesh cpuMesh; + const SkMesh& cpuMesh; mutable SkMesh gpuMesh; sk_sp<SkBlender> blender; SkPaint paint; @@ -517,6 +518,7 @@ struct DrawMesh final : Op { mutable GrDirectContext::DirectContextID contextId; void draw(SkCanvas* c, const SkMatrix&) const { GrDirectContext* directContext = c->recordingContext()->asDirectContext(); + GrDirectContext::DirectContextID id = directContext->directContextID(); if (!isGpuBased || contextId != id) { sk_sp<SkMesh::VertexBuffer> vb = @@ -543,6 +545,18 @@ struct DrawMesh final : Op { c->drawMesh(gpuMesh, blender, paint); } }; + +struct DrawMesh final : Op { + static const auto kType = Type::DrawMesh; + DrawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) + : mesh(mesh), blender(std::move(blender)), paint(paint) {} + + const Mesh& mesh; + sk_sp<SkBlender> blender; + SkPaint paint; + + void draw(SkCanvas* c, const SkMatrix&) const { c->drawMesh(mesh.getSkMesh(), blender, paint); } +}; struct DrawAtlas final : Op { static const auto kType = Type::DrawAtlas; DrawAtlas(const SkImage* atlas, int count, SkBlendMode mode, const SkSamplingOptions& sampling, @@ -859,6 +873,10 @@ void DisplayListData::drawVertices(const SkVertices* vert, SkBlendMode mode, con } void DisplayListData::drawMesh(const SkMesh& mesh, const sk_sp<SkBlender>& blender, const SkPaint& paint) { + this->push<DrawSkMesh>(0, mesh, blender, paint); +} +void DisplayListData::drawMesh(const Mesh& mesh, const sk_sp<SkBlender>& blender, + const SkPaint& paint) { this->push<DrawMesh>(0, mesh, blender, paint); } void DisplayListData::drawAtlas(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[], @@ -1205,6 +1223,9 @@ void RecordingCanvas::onDrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) { fDL->drawMesh(mesh, blender, paint); } +void RecordingCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) { + fDL->drawMesh(mesh, blender, paint); +} void RecordingCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[], const SkColor colors[], int count, SkBlendMode bmode, const SkSamplingOptions& sampling, @@ -1223,5 +1244,14 @@ void RecordingCanvas::drawWebView(skiapipeline::FunctorDrawable* drawable) { fDL->drawWebView(drawable); } +[[nodiscard]] const SkMesh& DrawMeshPayload::getSkMesh() const { + LOG_FATAL_IF(!meshWrapper && !mesh, "One of Mesh or Mesh must be non-null"); + if (meshWrapper) { + return meshWrapper->getSkMesh(); + } else { + return *mesh; + } +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 8409e136b57b..1f4ba5d6d557 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -28,6 +28,7 @@ #include <log/log.h> #include <cstdlib> +#include <utility> #include <vector> #include "CanvasTransform.h" @@ -40,6 +41,7 @@ enum class SkBlendMode; class SkRRect; +class Mesh; namespace android { namespace uirenderer { @@ -66,6 +68,18 @@ struct DisplayListOp { static_assert(sizeof(DisplayListOp) == 4); +class DrawMeshPayload { +public: + explicit DrawMeshPayload(const SkMesh* mesh) : mesh(mesh) {} + explicit DrawMeshPayload(const Mesh* meshWrapper) : meshWrapper(meshWrapper) {} + + [[nodiscard]] const SkMesh& getSkMesh() const; + +private: + const SkMesh* mesh = nullptr; + const Mesh* meshWrapper = nullptr; +}; + struct DrawImagePayload { explicit DrawImagePayload(Bitmap& bitmap) : image(bitmap.makeImage()), palette(bitmap.palette()) { @@ -143,6 +157,7 @@ private: void drawDRRect(const SkRRect&, const SkRRect&, const SkPaint&); void drawMesh(const SkMesh&, const sk_sp<SkBlender>&, const SkPaint&); + void drawMesh(const Mesh&, const sk_sp<SkBlender>&, const SkPaint&); void drawAnnotation(const SkRect&, const char*, SkData*); void drawDrawable(SkDrawable*, const SkMatrix*); @@ -247,6 +262,7 @@ public: SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*) override; void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; + void drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint); void drawVectorDrawable(VectorDrawableRoot* tree); void drawWebView(skiapipeline::FunctorDrawable*); diff --git a/libs/hwui/SafeMath.h b/libs/hwui/SafeMath.h new file mode 100644 index 000000000000..4d6adf55c0cb --- /dev/null +++ b/libs/hwui/SafeMath.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 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. + */ + +#ifndef SkSafeMath_DEFINED +#define SkSafeMath_DEFINED + +#include <cstddef> +#include <cstdint> +#include <limits> + +// Copy of Skia's SafeMath API used to validate Mesh parameters to support +// deferred creation of SkMesh instances on RenderThread. +// SafeMath always check that a series of operations do not overflow. +// This must be correct for all platforms, because this is a check for safety at runtime. + +class SafeMath { +public: + SafeMath() = default; + + bool ok() const { return fOK; } + explicit operator bool() const { return fOK; } + + size_t mul(size_t x, size_t y) { + return sizeof(size_t) == sizeof(uint64_t) ? mul64(x, y) : mul32(x, y); + } + + size_t add(size_t x, size_t y) { + size_t result = x + y; + fOK &= result >= x; + return result; + } + + /** + * Return a + b, unless this result is an overflow/underflow. In those cases, fOK will + * be set to false, and it is undefined what this returns. + */ + int addInt(int a, int b) { + if (b < 0 && a < std::numeric_limits<int>::min() - b) { + fOK = false; + return a; + } else if (b > 0 && a > std::numeric_limits<int>::max() - b) { + fOK = false; + return a; + } + return a + b; + } + + // These saturate to their results + static size_t Add(size_t x, size_t y) { + SafeMath tmp; + size_t sum = tmp.add(x, y); + return tmp.ok() ? sum : SIZE_MAX; + } + + static size_t Mul(size_t x, size_t y) { + SafeMath tmp; + size_t prod = tmp.mul(x, y); + return tmp.ok() ? prod : SIZE_MAX; + } + +private: + uint32_t mul32(uint32_t x, uint32_t y) { + uint64_t bx = x; + uint64_t by = y; + uint64_t result = bx * by; + fOK &= result >> 32 == 0; + // Overflow information is capture in fOK. Return the result modulo 2^32. + return (uint32_t)result; + } + + uint64_t mul64(uint64_t x, uint64_t y) { + if (x <= std::numeric_limits<uint64_t>::max() >> 32 && + y <= std::numeric_limits<uint64_t>::max() >> 32) { + return x * y; + } else { + auto hi = [](uint64_t x) { return x >> 32; }; + auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; }; + + uint64_t lx_ly = lo(x) * lo(y); + uint64_t hx_ly = hi(x) * lo(y); + uint64_t lx_hy = lo(x) * hi(y); + uint64_t hx_hy = hi(x) * hi(y); + uint64_t result = 0; + result = this->add(lx_ly, (hx_ly << 32)); + result = this->add(result, (lx_hy << 32)); + fOK &= (hx_hy + (hx_ly >> 32) + (lx_hy >> 32)) == 0; + + return result; + } + } + bool fOK = true; +}; + +#endif // SkSafeMath_DEFINED diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index d0124f5d4bad..7a1276982d0a 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -39,21 +39,22 @@ #include <SkShader.h> #include <SkTextBlob.h> #include <SkVertices.h> +#include <log/log.h> +#include <ui/FatVector.h> #include <memory> #include <optional> #include <utility> #include "CanvasProperty.h" +#include "Mesh.h" #include "NinePatchUtils.h" #include "VectorDrawable.h" #include "hwui/Bitmap.h" #include "hwui/MinikinUtils.h" #include "hwui/PaintFilter.h" -#include <log/log.h> #include "pipeline/skia/AnimatedDrawables.h" #include "pipeline/skia/HolePunch.h" -#include <ui/FatVector.h> namespace android { @@ -572,8 +573,14 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawVertices(vertices, mode, p); }); } -void SkiaCanvas::drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) { - mCanvas->drawMesh(mesh, blender, paint); +void SkiaCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) { + GrDirectContext* context = nullptr; + auto recordingContext = mCanvas->recordingContext(); + if (recordingContext) { + context = recordingContext->asDirectContext(); + } + mesh.updateSkMesh(context); + mCanvas->drawMesh(mesh.getSkMesh(), blender, paint); } // ---------------------------------------------------------------------------- diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index f2c286a4fb46..b785989f35cb 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -129,8 +129,7 @@ public: float sweepAngle, bool useCenter, const Paint& paint) override; virtual void drawPath(const SkPath& path, const Paint& paint) override; virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) override; - virtual void drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, - const SkPaint& paint) override; + virtual void drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) override; virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override; virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override; diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 2a2019199bda..44ee31d34d23 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -16,25 +16,25 @@ #pragma once +#include <SaveFlags.h> +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkMatrix.h> +#include <androidfw/ResourceTypes.h> #include <cutils/compiler.h> #include <utils/Functor.h> -#include <SaveFlags.h> -#include <androidfw/ResourceTypes.h> #include "Properties.h" #include "pipeline/skia/AnimatedDrawables.h" #include "utils/Macros.h" -#include <SkBitmap.h> -#include <SkCanvas.h> -#include <SkMatrix.h> - class SkAnimatedImage; enum class SkBlendMode; class SkCanvasState; class SkRRect; class SkRuntimeShaderBuilder; class SkVertices; +class Mesh; namespace minikin { class Font; @@ -227,7 +227,7 @@ public: float sweepAngle, bool useCenter, const Paint& paint) = 0; virtual void drawPath(const SkPath& path, const Paint& paint) = 0; virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) = 0; - virtual void drawMesh(const SkMesh& mesh, sk_sp<SkBlender>, const SkPaint& paint) = 0; + virtual void drawMesh(const Mesh& mesh, sk_sp<SkBlender>, const Paint& paint) = 0; // Bitmap-based virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) = 0; diff --git a/libs/hwui/jni/Interpolator.cpp b/libs/hwui/jni/Interpolator.cpp index fc3d70b87f5a..c71e3085caf5 100644 --- a/libs/hwui/jni/Interpolator.cpp +++ b/libs/hwui/jni/Interpolator.cpp @@ -24,12 +24,8 @@ static void Interpolator_setKeyFrame(JNIEnv* env, jobject clazz, jlong interpHan AutoJavaFloatArray autoValues(env, valueArray); AutoJavaFloatArray autoBlend(env, blendArray, 4); -#ifdef SK_SCALAR_IS_FLOAT SkScalar* scalars = autoValues.ptr(); SkScalar* blend = autoBlend.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif interp->setKeyFrame(index, msec, scalars, blend); } diff --git a/libs/hwui/jni/Mesh.cpp b/libs/hwui/jni/Mesh.cpp deleted file mode 100644 index b13d9bafb417..000000000000 --- a/libs/hwui/jni/Mesh.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2022 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. - */ - -#include <GLES/gl.h> -#include <Mesh.h> -#include <SkMesh.h> - -#include "GraphicsJNI.h" -#include "graphics_jni_helpers.h" - -namespace android { - -sk_sp<SkMesh::VertexBuffer> genVertexBuffer(JNIEnv* env, jobject buffer, int size, - jboolean isDirect) { - auto buff = ScopedJavaNioBuffer(env, buffer, size, isDirect); - auto vertexBuffer = SkMesh::MakeVertexBuffer(nullptr, buff.data(), size); - return vertexBuffer; -} - -sk_sp<SkMesh::IndexBuffer> genIndexBuffer(JNIEnv* env, jobject buffer, int size, - jboolean isDirect) { - auto buff = ScopedJavaNioBuffer(env, buffer, size, isDirect); - auto indexBuffer = SkMesh::MakeIndexBuffer(nullptr, buff.data(), size); - return indexBuffer; -} - -static jlong make(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer, - jboolean isDirect, jint vertexCount, jint vertexOffset, jfloat left, jfloat top, - jfloat right, jfloat bottom) { - auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec)); - sk_sp<SkMesh::VertexBuffer> skVertexBuffer = - genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isDirect); - auto skRect = SkRect::MakeLTRB(left, top, right, bottom); - auto meshResult = SkMesh::Make( - skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, vertexOffset, - SkData::MakeWithCopy(skMeshSpec->uniforms().data(), skMeshSpec->uniformSize()), skRect); - - if (!meshResult.error.isEmpty()) { - jniThrowException(env, "java/lang/IllegalArgumentException", meshResult.error.c_str()); - } - - auto meshPtr = std::make_unique<MeshWrapper>( - MeshWrapper{meshResult.mesh, MeshUniformBuilder(skMeshSpec)}); - return reinterpret_cast<jlong>(meshPtr.release()); -} - -static jlong makeIndexed(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer, - jboolean isVertexDirect, jint vertexCount, jint vertexOffset, - jobject indexBuffer, jboolean isIndexDirect, jint indexCount, - jint indexOffset, jfloat left, jfloat top, jfloat right, jfloat bottom) { - auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec)); - sk_sp<SkMesh::VertexBuffer> skVertexBuffer = - genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isVertexDirect); - sk_sp<SkMesh::IndexBuffer> skIndexBuffer = - genIndexBuffer(env, indexBuffer, indexCount * gIndexByteSize, isIndexDirect); - auto skRect = SkRect::MakeLTRB(left, top, right, bottom); - - auto meshResult = SkMesh::MakeIndexed( - skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, vertexOffset, - skIndexBuffer, indexCount, indexOffset, - SkData::MakeWithCopy(skMeshSpec->uniforms().data(), skMeshSpec->uniformSize()), skRect); - - if (!meshResult.error.isEmpty()) { - jniThrowException(env, "java/lang/IllegalArgumentException", meshResult.error.c_str()); - } - auto meshPtr = std::make_unique<MeshWrapper>( - MeshWrapper{meshResult.mesh, MeshUniformBuilder(skMeshSpec)}); - return reinterpret_cast<jlong>(meshPtr.release()); -} - -static void updateMesh(JNIEnv* env, jobject, jlong meshWrapper, jboolean indexed) { - auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper); - auto mesh = wrapper->mesh; - if (indexed) { - wrapper->mesh = SkMesh::MakeIndexed(sk_ref_sp(mesh.spec()), mesh.mode(), - sk_ref_sp(mesh.vertexBuffer()), mesh.vertexCount(), - mesh.vertexOffset(), sk_ref_sp(mesh.indexBuffer()), - mesh.indexCount(), mesh.indexOffset(), - wrapper->builder.fUniforms, mesh.bounds()) - .mesh; - } else { - wrapper->mesh = SkMesh::Make(sk_ref_sp(mesh.spec()), mesh.mode(), - sk_ref_sp(mesh.vertexBuffer()), mesh.vertexCount(), - mesh.vertexOffset(), wrapper->builder.fUniforms, mesh.bounds()) - .mesh; - } -} - -static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args); - va_end(args); - return ret; -} - -static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) { - switch (type) { - case SkRuntimeEffect::Uniform::Type::kFloat: - case SkRuntimeEffect::Uniform::Type::kFloat2: - case SkRuntimeEffect::Uniform::Type::kFloat3: - case SkRuntimeEffect::Uniform::Type::kFloat4: - case SkRuntimeEffect::Uniform::Type::kFloat2x2: - case SkRuntimeEffect::Uniform::Type::kFloat3x3: - case SkRuntimeEffect::Uniform::Type::kFloat4x4: - return false; - case SkRuntimeEffect::Uniform::Type::kInt: - case SkRuntimeEffect::Uniform::Type::kInt2: - case SkRuntimeEffect::Uniform::Type::kInt3: - case SkRuntimeEffect::Uniform::Type::kInt4: - return true; - } -} - -static void nativeUpdateFloatUniforms(JNIEnv* env, MeshUniformBuilder* builder, - const char* uniformName, const float values[], int count, - bool isColor) { - MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName); - if (uniform.fVar == nullptr) { - ThrowIAEFmt(env, "unable to find uniform named %s", uniformName); - } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) { - if (isColor) { - jniThrowExceptionFmt( - env, "java/lang/IllegalArgumentException", - "attempting to set a color uniform using the non-color specific APIs: %s %x", - uniformName, uniform.fVar->flags); - } else { - ThrowIAEFmt(env, - "attempting to set a non-color uniform using the setColorUniform APIs: %s", - uniformName); - } - } else if (isIntUniformType(uniform.fVar->type)) { - ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s", - uniformName); - } else if (!uniform.set<float>(values, count)) { - ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", - uniform.fVar->sizeInBytes(), sizeof(float) * count); - } -} - -static void updateFloatUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName, - jfloat value1, jfloat value2, jfloat value3, jfloat value4, - jint count) { - auto* wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper); - ScopedUtfChars name(env, uniformName); - const float values[4] = {value1, value2, value3, value4}; - nativeUpdateFloatUniforms(env, &wrapper->builder, name.c_str(), values, count, false); -} - -static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring jUniformName, - jfloatArray jvalues, jboolean isColor) { - auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper); - ScopedUtfChars name(env, jUniformName); - AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess); - nativeUpdateFloatUniforms(env, &wrapper->builder, name.c_str(), autoValues.ptr(), - autoValues.length(), isColor); -} - -static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder, - const char* uniformName, const int values[], int count) { - MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName); - if (uniform.fVar == nullptr) { - ThrowIAEFmt(env, "unable to find uniform named %s", uniformName); - } else if (!isIntUniformType(uniform.fVar->type)) { - ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s", - uniformName); - } else if (!uniform.set<int>(values, count)) { - ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", - uniform.fVar->sizeInBytes(), sizeof(float) * count); - } -} - -static void updateIntUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName, - jint value1, jint value2, jint value3, jint value4, jint count) { - auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper); - ScopedUtfChars name(env, uniformName); - const int values[4] = {value1, value2, value3, value4}; - nativeUpdateIntUniforms(env, &wrapper->builder, name.c_str(), values, count); -} - -static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName, - jintArray values) { - auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper); - ScopedUtfChars name(env, uniformName); - AutoJavaIntArray autoValues(env, values, 0); - nativeUpdateIntUniforms(env, &wrapper->builder, name.c_str(), autoValues.ptr(), - autoValues.length()); -} - -static void MeshWrapper_destroy(MeshWrapper* wrapper) { - delete wrapper; -} - -static jlong getMeshFinalizer(JNIEnv*, jobject) { - return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshWrapper_destroy)); -} - -static const JNINativeMethod gMeshMethods[] = { - {"nativeGetFinalizer", "()J", (void*)getMeshFinalizer}, - {"nativeMake", "(JILjava/nio/Buffer;ZIIFFFF)J", (void*)make}, - {"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIFFFF)J", - (void*)makeIndexed}, - {"nativeUpdateMesh", "(JZ)V", (void*)updateMesh}, - {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", (void*)updateFloatArrayUniforms}, - {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V", (void*)updateFloatUniforms}, - {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V", (void*)updateIntArrayUniforms}, - {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)updateIntUniforms}}; - -int register_android_graphics_Mesh(JNIEnv* env) { - android::RegisterMethodsOrDie(env, "android/graphics/Mesh", gMeshMethods, NELEM(gMeshMethods)); - return 0; -} - -} // namespace android diff --git a/libs/hwui/jni/Mesh.h b/libs/hwui/jni/Mesh.h deleted file mode 100644 index 61c2260e3ad1..000000000000 --- a/libs/hwui/jni/Mesh.h +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (C) 2022 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. - */ - -#ifndef FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_ -#define FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_ - -#include <SkMesh.h> -#include <jni.h> - -#include <log/log.h> -#include <utility> - -#include "graphics_jni_helpers.h" - -#define gIndexByteSize 2 - -// A smart pointer that provides read only access to Java.nio.Buffer. This handles both -// direct and indrect buffers, allowing access to the underlying data in both -// situations. If passed a null buffer, we will throw NullPointerException, -// and c_data will return nullptr. -// -// This class draws from com_google_android_gles_jni_GLImpl.cpp for Buffer to void * -// conversion. -class ScopedJavaNioBuffer { -public: - ScopedJavaNioBuffer(JNIEnv* env, jobject buffer, jint size, jboolean isDirect) - : mEnv(env), mBuffer(buffer) { - if (buffer == nullptr) { - mDataBase = nullptr; - mData = nullptr; - jniThrowNullPointerException(env); - } else { - mArray = (jarray) nullptr; - if (isDirect) { - mData = getDirectBufferPointer(mEnv, mBuffer); - } else { - mData = setIndirectData(size); - } - } - } - - ScopedJavaNioBuffer(ScopedJavaNioBuffer&& rhs) noexcept { *this = std::move(rhs); } - - ~ScopedJavaNioBuffer() { reset(); } - - void reset() { - if (mDataBase) { - releasePointer(mEnv, mArray, mDataBase, JNI_FALSE); - mDataBase = nullptr; - } - } - - ScopedJavaNioBuffer& operator=(ScopedJavaNioBuffer&& rhs) noexcept { - if (this != &rhs) { - reset(); - - mEnv = rhs.mEnv; - mBuffer = rhs.mBuffer; - mDataBase = rhs.mDataBase; - mData = rhs.mData; - mArray = rhs.mArray; - rhs.mEnv = nullptr; - rhs.mData = nullptr; - rhs.mBuffer = nullptr; - rhs.mArray = nullptr; - rhs.mDataBase = nullptr; - } - return *this; - } - - const void* data() const { return mData; } - -private: - /** - * This code is taken and modified from com_google_android_gles_jni_GLImpl.cpp to extract data - * from a java.nio.Buffer. - */ - void* getDirectBufferPointer(JNIEnv* env, jobject buffer) { - if (buffer == nullptr) { - return nullptr; - } - - jint position; - jint limit; - jint elementSizeShift; - jlong pointer; - pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift); - if (pointer == 0) { - jniThrowException(mEnv, "java/lang/IllegalArgumentException", - "Must use a native order direct Buffer"); - return nullptr; - } - pointer += position << elementSizeShift; - return reinterpret_cast<void*>(pointer); - } - - static void releasePointer(JNIEnv* env, jarray array, void* data, jboolean commit) { - env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT); - } - - static void* getPointer(JNIEnv* env, jobject buffer, jarray* array, jint* remaining, - jint* offset) { - jint position; - jint limit; - jint elementSizeShift; - - jlong pointer; - pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift); - *remaining = (limit - position) << elementSizeShift; - if (pointer != 0L) { - *array = nullptr; - pointer += position << elementSizeShift; - return reinterpret_cast<void*>(pointer); - } - - *array = jniGetNioBufferBaseArray(env, buffer); - *offset = jniGetNioBufferBaseArrayOffset(env, buffer); - return nullptr; - } - - /** - * This is a copy of - * static void android_glBufferData__IILjava_nio_Buffer_2I - * from com_google_android_gles_jni_GLImpl.cpp - */ - void* setIndirectData(jint size) { - jint exception; - const char* exceptionType; - const char* exceptionMessage; - jint bufferOffset = (jint)0; - jint remaining; - void* tempData; - - if (mBuffer) { - tempData = - (void*)getPointer(mEnv, mBuffer, (jarray*)&mArray, &remaining, &bufferOffset); - if (remaining < size) { - exception = 1; - exceptionType = "java/lang/IllegalArgumentException"; - exceptionMessage = "remaining() < size < needed"; - goto exit; - } - } - if (mBuffer && tempData == nullptr) { - mDataBase = (char*)mEnv->GetPrimitiveArrayCritical(mArray, (jboolean*)0); - tempData = (void*)(mDataBase + bufferOffset); - } - return tempData; - exit: - if (mArray) { - releasePointer(mEnv, mArray, (void*)(mDataBase), JNI_FALSE); - } - if (exception) { - jniThrowException(mEnv, exceptionType, exceptionMessage); - } - return nullptr; - } - - JNIEnv* mEnv; - - // Java Buffer data - void* mData; - jobject mBuffer; - - // Indirect Buffer Data - jarray mArray; - char* mDataBase; -}; - -class MeshUniformBuilder { -public: - struct MeshUniform { - template <typename T> - std::enable_if_t<std::is_trivially_copyable<T>::value, MeshUniform> operator=( - const T& val) { - if (!fVar) { - LOG_FATAL("Assigning to missing variable"); - } else if (sizeof(val) != fVar->sizeInBytes()) { - LOG_FATAL("Incorrect value size"); - } else { - void* dst = reinterpret_cast<void*>( - reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset); - memcpy(dst, &val, sizeof(val)); - } - } - - MeshUniform& operator=(const SkMatrix& val) { - if (!fVar) { - LOG_FATAL("Assigning to missing variable"); - } else if (fVar->sizeInBytes() != 9 * sizeof(float)) { - LOG_FATAL("Incorrect value size"); - } else { - float* data = reinterpret_cast<float*>( - reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset); - data[0] = val.get(0); - data[1] = val.get(3); - data[2] = val.get(6); - data[3] = val.get(1); - data[4] = val.get(4); - data[5] = val.get(7); - data[6] = val.get(2); - data[7] = val.get(5); - data[8] = val.get(8); - } - return *this; - } - - template <typename T> - bool set(const T val[], const int count) { - static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable"); - if (!fVar) { - LOG_FATAL("Assigning to missing variable"); - return false; - } else if (sizeof(T) * count != fVar->sizeInBytes()) { - LOG_FATAL("Incorrect value size"); - return false; - } else { - void* dst = reinterpret_cast<void*>( - reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset); - memcpy(dst, val, sizeof(T) * count); - } - return true; - } - - MeshUniformBuilder* fOwner; - const SkRuntimeEffect::Uniform* fVar; - }; - MeshUniform uniform(std::string_view name) { return {this, fMeshSpec->findUniform(name)}; } - - explicit MeshUniformBuilder(sk_sp<SkMeshSpecification> meshSpec) { - fMeshSpec = sk_sp(meshSpec); - fUniforms = (SkData::MakeZeroInitialized(meshSpec->uniformSize())); - } - - sk_sp<SkData> fUniforms; - -private: - void* writableUniformData() { - if (!fUniforms->unique()) { - fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size()); - } - return fUniforms->writable_data(); - } - - sk_sp<SkMeshSpecification> fMeshSpec; -}; - -struct MeshWrapper { - SkMesh mesh; - MeshUniformBuilder builder; -}; -#endif // FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_ diff --git a/libs/hwui/jni/Path.cpp b/libs/hwui/jni/Path.cpp index 3694ce07b972..a5e04763d885 100644 --- a/libs/hwui/jni/Path.cpp +++ b/libs/hwui/jni/Path.cpp @@ -182,11 +182,7 @@ public: SkPath* obj = reinterpret_cast<SkPath*>(objHandle); SkPathDirection dir = static_cast<SkPathDirection>(dirHandle); AutoJavaFloatArray afa(env, array, 8); -#ifdef SK_SCALAR_IS_FLOAT const float* src = afa.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif obj->addRoundRect(rect, src, dir); } diff --git a/libs/hwui/jni/PathEffect.cpp b/libs/hwui/jni/PathEffect.cpp index f99bef7b7d58..3dbe1a67f52e 100644 --- a/libs/hwui/jni/PathEffect.cpp +++ b/libs/hwui/jni/PathEffect.cpp @@ -35,11 +35,7 @@ public: jfloatArray intervalArray, jfloat phase) { AutoJavaFloatArray autoInterval(env, intervalArray); int count = autoInterval.length() & ~1; // even number -#ifdef SK_SCALAR_IS_FLOAT - SkScalar* intervals = autoInterval.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif + SkScalar* intervals = autoInterval.ptr(); SkPathEffect* effect = SkDashPathEffect::Make(intervals, count, phase).release(); return reinterpret_cast<jlong>(effect); } diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index 8a0db1c91d46..75d45e5bd8aa 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -52,12 +52,7 @@ static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray) { AutoJavaFloatArray autoHSV(env, hsvArray, 3); -#ifdef SK_SCALAR_IS_FLOAT - SkScalar* hsv = autoHSV.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif - + SkScalar* hsv = autoHSV.ptr(); return static_cast<jint>(SkHSVToColor(alpha, hsv)); } @@ -149,11 +144,7 @@ static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr, std::vector<SkColor4f> colors = convertColorLongs(env, colorArray); AutoJavaFloatArray autoPos(env, posArray, colors.size()); -#ifdef SK_SCALAR_IS_FLOAT SkScalar* pos = autoPos.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0], GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), @@ -193,11 +184,7 @@ static jlong RadialGradient_create(JNIEnv* env, std::vector<SkColor4f> colors = convertColorLongs(env, colorArray); AutoJavaFloatArray autoPos(env, posArray, colors.size()); -#ifdef SK_SCALAR_IS_FLOAT SkScalar* pos = autoPos.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif auto colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); auto skTileMode = static_cast<SkTileMode>(tileMode); @@ -225,11 +212,7 @@ static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat std::vector<SkColor4f> colors = convertColorLongs(env, colorArray); AutoJavaFloatArray autoPos(env, jpositions, colors.size()); -#ifdef SK_SCALAR_IS_FLOAT SkScalar* pos = autoPos.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0], GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp index 8a4d4e17edb1..8ba750372d18 100644 --- a/libs/hwui/jni/android_graphics_Canvas.cpp +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -21,7 +21,6 @@ #else #define __ANDROID_API_P__ 28 #endif -#include <Mesh.h> #include <androidfw/ResourceTypes.h> #include <hwui/Canvas.h> #include <hwui/Paint.h> @@ -446,10 +445,10 @@ static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle, static void drawMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong meshHandle, jint modeHandle, jlong paintHandle) { - const SkMesh mesh = reinterpret_cast<MeshWrapper*>(meshHandle)->mesh; + const Mesh* mesh = reinterpret_cast<Mesh*>(meshHandle); SkBlendMode blendMode = static_cast<SkBlendMode>(modeHandle); - SkPaint* paint = reinterpret_cast<Paint*>(paintHandle); - get_canvas(canvasHandle)->drawMesh(mesh, SkBlender::Mode(blendMode), *paint); + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawMesh(*mesh, SkBlender::Mode(blendMode), *paint); } static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp index cf6702e45fff..ca667b0d09bc 100644 --- a/libs/hwui/jni/android_graphics_Matrix.cpp +++ b/libs/hwui/jni/android_graphics_Matrix.cpp @@ -23,8 +23,6 @@ namespace android { static_assert(sizeof(SkMatrix) == 40, "Unexpected sizeof(SkMatrix), " "update size in Matrix.java#NATIVE_ALLOCATION_SIZE and here"); -static_assert(SK_SCALAR_IS_FLOAT, "SK_SCALAR_IS_FLOAT is false, " - "only float scalar is supported"); class SkMatrixGlue { public: diff --git a/libs/hwui/jni/android_graphics_Mesh.cpp b/libs/hwui/jni/android_graphics_Mesh.cpp new file mode 100644 index 000000000000..04339dc8b9a5 --- /dev/null +++ b/libs/hwui/jni/android_graphics_Mesh.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <GrDirectContext.h> +#include <Mesh.h> +#include <SkMesh.h> +#include <jni.h> +#include <log/log.h> + +#include <utility> + +#include "GraphicsJNI.h" +#include "graphics_jni_helpers.h" + +#define gIndexByteSize 2 + +// A smart pointer that provides read only access to Java.nio.Buffer. This handles both +// direct and indrect buffers, allowing access to the underlying data in both +// situations. If passed a null buffer, we will throw NullPointerException, +// and c_data will return nullptr. +// +// This class draws from com_google_android_gles_jni_GLImpl.cpp for Buffer to void * +// conversion. +class ScopedJavaNioBuffer { +public: + ScopedJavaNioBuffer(JNIEnv* env, jobject buffer, size_t size, jboolean isDirect) + : mEnv(env), mBuffer(buffer) { + if (buffer == nullptr) { + mDataBase = nullptr; + mData = nullptr; + jniThrowNullPointerException(env); + } else { + mArray = (jarray) nullptr; + if (isDirect) { + mData = getDirectBufferPointer(mEnv, mBuffer); + } else { + mData = setIndirectData(size); + } + } + } + + ScopedJavaNioBuffer(ScopedJavaNioBuffer&& rhs) noexcept { *this = std::move(rhs); } + + ~ScopedJavaNioBuffer() { reset(); } + + void reset() { + if (mDataBase) { + releasePointer(mEnv, mArray, mDataBase, JNI_FALSE); + mDataBase = nullptr; + } + } + + ScopedJavaNioBuffer& operator=(ScopedJavaNioBuffer&& rhs) noexcept { + if (this != &rhs) { + reset(); + + mEnv = rhs.mEnv; + mBuffer = rhs.mBuffer; + mDataBase = rhs.mDataBase; + mData = rhs.mData; + mArray = rhs.mArray; + rhs.mEnv = nullptr; + rhs.mData = nullptr; + rhs.mBuffer = nullptr; + rhs.mArray = nullptr; + rhs.mDataBase = nullptr; + } + return *this; + } + + const void* data() const { return mData; } + +private: + /** + * This code is taken and modified from com_google_android_gles_jni_GLImpl.cpp to extract data + * from a java.nio.Buffer. + */ + void* getDirectBufferPointer(JNIEnv* env, jobject buffer) { + if (buffer == nullptr) { + return nullptr; + } + + jint position; + jint limit; + jint elementSizeShift; + jlong pointer; + pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift); + if (pointer == 0) { + jniThrowException(mEnv, "java/lang/IllegalArgumentException", + "Must use a native order direct Buffer"); + return nullptr; + } + pointer += position << elementSizeShift; + return reinterpret_cast<void*>(pointer); + } + + static void releasePointer(JNIEnv* env, jarray array, void* data, jboolean commit) { + env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT); + } + + static void* getPointer(JNIEnv* env, jobject buffer, jarray* array, jint* remaining, + jint* offset) { + jint position; + jint limit; + jint elementSizeShift; + + jlong pointer; + pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift); + *remaining = (limit - position) << elementSizeShift; + if (pointer != 0L) { + *array = nullptr; + pointer += position << elementSizeShift; + return reinterpret_cast<void*>(pointer); + } + + *array = jniGetNioBufferBaseArray(env, buffer); + *offset = jniGetNioBufferBaseArrayOffset(env, buffer); + return nullptr; + } + + /** + * This is a copy of + * static void android_glBufferData__IILjava_nio_Buffer_2I + * from com_google_android_gles_jni_GLImpl.cpp + */ + void* setIndirectData(size_t size) { + jint exception; + const char* exceptionType; + const char* exceptionMessage; + jint bufferOffset = (jint)0; + jint remaining; + void* tempData; + + if (mBuffer) { + tempData = + (void*)getPointer(mEnv, mBuffer, (jarray*)&mArray, &remaining, &bufferOffset); + if (remaining < size) { + exception = 1; + exceptionType = "java/lang/IllegalArgumentException"; + exceptionMessage = "remaining() < size < needed"; + goto exit; + } + } + if (mBuffer && tempData == nullptr) { + mDataBase = (char*)mEnv->GetPrimitiveArrayCritical(mArray, (jboolean*)0); + tempData = (void*)(mDataBase + bufferOffset); + } + return tempData; + exit: + if (mArray) { + releasePointer(mEnv, mArray, (void*)(mDataBase), JNI_FALSE); + } + if (exception) { + jniThrowException(mEnv, exceptionType, exceptionMessage); + } + return nullptr; + } + + JNIEnv* mEnv; + + // Java Buffer data + void* mData; + jobject mBuffer; + + // Indirect Buffer Data + jarray mArray; + char* mDataBase; +}; + +namespace android { + +static jlong make(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer, + jboolean isDirect, jint vertexCount, jint vertexOffset, jfloat left, jfloat top, + jfloat right, jfloat bottom) { + auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec)); + size_t bufferSize = vertexCount * skMeshSpec->stride(); + auto buff = ScopedJavaNioBuffer(env, vertexBuffer, bufferSize, isDirect); + auto skRect = SkRect::MakeLTRB(left, top, right, bottom); + auto meshPtr = new Mesh(skMeshSpec, mode, buff.data(), bufferSize, vertexCount, vertexOffset, + std::make_unique<MeshUniformBuilder>(skMeshSpec), skRect); + auto [valid, msg] = meshPtr->validate(); + if (!valid) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", msg.c_str()); + } + return reinterpret_cast<jlong>(meshPtr); +} + +static jlong makeIndexed(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer, + jboolean isVertexDirect, jint vertexCount, jint vertexOffset, + jobject indexBuffer, jboolean isIndexDirect, jint indexCount, + jint indexOffset, jfloat left, jfloat top, jfloat right, jfloat bottom) { + auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec)); + auto vertexBufferSize = vertexCount * skMeshSpec->stride(); + auto indexBufferSize = indexCount * gIndexByteSize; + auto vBuf = ScopedJavaNioBuffer(env, vertexBuffer, vertexBufferSize, isVertexDirect); + auto iBuf = ScopedJavaNioBuffer(env, indexBuffer, indexBufferSize, isIndexDirect); + auto skRect = SkRect::MakeLTRB(left, top, right, bottom); + auto meshPtr = new Mesh(skMeshSpec, mode, vBuf.data(), vertexBufferSize, vertexCount, + vertexOffset, iBuf.data(), indexBufferSize, indexCount, indexOffset, + std::make_unique<MeshUniformBuilder>(skMeshSpec), skRect); + auto [valid, msg] = meshPtr->validate(); + if (!valid) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", msg.c_str()); + } + + return reinterpret_cast<jlong>(meshPtr); +} + +static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args); + va_end(args); + return ret; +} + +static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) { + switch (type) { + case SkRuntimeEffect::Uniform::Type::kFloat: + case SkRuntimeEffect::Uniform::Type::kFloat2: + case SkRuntimeEffect::Uniform::Type::kFloat3: + case SkRuntimeEffect::Uniform::Type::kFloat4: + case SkRuntimeEffect::Uniform::Type::kFloat2x2: + case SkRuntimeEffect::Uniform::Type::kFloat3x3: + case SkRuntimeEffect::Uniform::Type::kFloat4x4: + return false; + case SkRuntimeEffect::Uniform::Type::kInt: + case SkRuntimeEffect::Uniform::Type::kInt2: + case SkRuntimeEffect::Uniform::Type::kInt3: + case SkRuntimeEffect::Uniform::Type::kInt4: + return true; + } +} + +static void nativeUpdateFloatUniforms(JNIEnv* env, MeshUniformBuilder* builder, + const char* uniformName, const float values[], int count, + bool isColor) { + MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName); + if (uniform.fVar == nullptr) { + ThrowIAEFmt(env, "unable to find uniform named %s", uniformName); + } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) { + if (isColor) { + jniThrowExceptionFmt( + env, "java/lang/IllegalArgumentException", + "attempting to set a color uniform using the non-color specific APIs: %s %x", + uniformName, uniform.fVar->flags); + } else { + ThrowIAEFmt(env, + "attempting to set a non-color uniform using the setColorUniform APIs: %s", + uniformName); + } + } else if (isIntUniformType(uniform.fVar->type)) { + ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s", + uniformName); + } else if (!uniform.set<float>(values, count)) { + ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", + uniform.fVar->sizeInBytes(), sizeof(float) * count); + } +} + +static void updateFloatUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName, + jfloat value1, jfloat value2, jfloat value3, jfloat value4, + jint count) { + auto* wrapper = reinterpret_cast<Mesh*>(meshWrapper); + ScopedUtfChars name(env, uniformName); + const float values[4] = {value1, value2, value3, value4}; + nativeUpdateFloatUniforms(env, wrapper->uniformBuilder(), name.c_str(), values, count, false); + wrapper->markDirty(); +} + +static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring jUniformName, + jfloatArray jvalues, jboolean isColor) { + auto wrapper = reinterpret_cast<Mesh*>(meshWrapper); + ScopedUtfChars name(env, jUniformName); + AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess); + nativeUpdateFloatUniforms(env, wrapper->uniformBuilder(), name.c_str(), autoValues.ptr(), + autoValues.length(), isColor); + wrapper->markDirty(); +} + +static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder, + const char* uniformName, const int values[], int count) { + MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName); + if (uniform.fVar == nullptr) { + ThrowIAEFmt(env, "unable to find uniform named %s", uniformName); + } else if (!isIntUniformType(uniform.fVar->type)) { + ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s", + uniformName); + } else if (!uniform.set<int>(values, count)) { + ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", + uniform.fVar->sizeInBytes(), sizeof(float) * count); + } +} + +static void updateIntUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName, + jint value1, jint value2, jint value3, jint value4, jint count) { + auto wrapper = reinterpret_cast<Mesh*>(meshWrapper); + ScopedUtfChars name(env, uniformName); + const int values[4] = {value1, value2, value3, value4}; + nativeUpdateIntUniforms(env, wrapper->uniformBuilder(), name.c_str(), values, count); + wrapper->markDirty(); +} + +static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName, + jintArray values) { + auto wrapper = reinterpret_cast<Mesh*>(meshWrapper); + ScopedUtfChars name(env, uniformName); + AutoJavaIntArray autoValues(env, values, 0); + nativeUpdateIntUniforms(env, wrapper->uniformBuilder(), name.c_str(), autoValues.ptr(), + autoValues.length()); + wrapper->markDirty(); +} + +static void MeshWrapper_destroy(Mesh* wrapper) { + delete wrapper; +} + +static jlong getMeshFinalizer(JNIEnv*, jobject) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshWrapper_destroy)); +} + +static const JNINativeMethod gMeshMethods[] = { + {"nativeGetFinalizer", "()J", (void*)getMeshFinalizer}, + {"nativeMake", "(JILjava/nio/Buffer;ZIIFFFF)J", (void*)make}, + {"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIFFFF)J", + (void*)makeIndexed}, + {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", (void*)updateFloatArrayUniforms}, + {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V", (void*)updateFloatUniforms}, + {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V", (void*)updateIntArrayUniforms}, + {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)updateIntUniforms}}; + +int register_android_graphics_Mesh(JNIEnv* env) { + android::RegisterMethodsOrDie(env, "android/graphics/Mesh", gMeshMethods, NELEM(gMeshMethods)); + return 0; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index fcfc4f82abed..af2d3b34bac7 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -23,6 +23,7 @@ #else #include "DamageAccumulator.h" #endif +#include "TreeInfo.h" #include "VectorDrawable.h" #ifdef __ANDROID__ #include "renderthread/CanvasContext.h" @@ -102,6 +103,12 @@ bool SkiaDisplayList::prepareListAndChildren( info.prepareTextures = false; info.canvasContext.unpinImages(); } + + auto grContext = info.canvasContext.getGrContext(); + for (auto mesh : mMeshes) { + mesh->updateSkMesh(grContext); + } + #endif bool hasBackwardProjectedNodesHere = false; @@ -168,6 +175,7 @@ void SkiaDisplayList::reset() { mDisplayList.reset(); + mMeshes.clear(); mMutableImages.clear(); mVectorDrawables.clear(); mAnimatedImages.clear(); diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index 2a677344b7b2..7af31a4dc4c6 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -18,6 +18,7 @@ #include <deque> +#include "Mesh.h" #include "RecordingCanvas.h" #include "RenderNodeDrawable.h" #include "TreeInfo.h" @@ -167,6 +168,7 @@ public: std::deque<RenderNodeDrawable> mChildNodes; std::deque<FunctorDrawable*> mChildFunctors; std::vector<SkImage*> mMutableImages; + std::vector<const Mesh*> mMeshes; private: std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index e1c8877a8b7a..3ca7eeb37a89 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -321,6 +321,11 @@ double SkiaRecordingCanvas::drawAnimatedImage(AnimatedImageDrawable* animatedIma return 0; } +void SkiaRecordingCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) { + mDisplayList->mMeshes.push_back(&mesh); + mRecorder.drawMesh(mesh, blender, paint); +} + } // namespace skiapipeline } // namespace uirenderer } // namespace android diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index 3fd8fa3aa9a9..a8e4580dc200 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -81,6 +81,7 @@ public: virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; virtual void enableZ(bool enableZ) override; + virtual void drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) override; virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; diff --git a/packages/AppPredictionLib/Android.bp b/packages/AppPredictionLib/Android.bp index 5a68fdc9ae75..31c193631602 100644 --- a/packages/AppPredictionLib/Android.bp +++ b/packages/AppPredictionLib/Android.bp @@ -25,7 +25,7 @@ android_library { name: "app_prediction", sdk_version: "system_current", - min_sdk_version: "system_current", + min_sdk_version: "current", srcs: [ "src/**/*.java", diff --git a/packages/CarrierDefaultApp/res/values-af/strings.xml b/packages/CarrierDefaultApp/res/values-af/strings.xml index 13ae4dad7727..cb911d8f95da 100644 --- a/packages/CarrierDefaultApp/res/values-af/strings.xml +++ b/packages/CarrierDefaultApp/res/values-af/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Byvoorbeeld, die aanmeldbladsy behoort dalk nie aan die organisasie wat gewys word nie."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Gaan in elk geval deur blaaier voort"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Prestasiehupstoot"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Verbeter jou 5G-ervaring"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s beveel aan dat jy ’n werkverrigtinghupstootpakket koop. Tik om deur %2$s te koop."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Nie nou nie"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Bestuur"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Koop ’n prestasiehupstoot."</string> diff --git a/packages/CarrierDefaultApp/res/values-am/strings.xml b/packages/CarrierDefaultApp/res/values-am/strings.xml index e1f91ceb9cc2..edaa2486a6f5 100644 --- a/packages/CarrierDefaultApp/res/values-am/strings.xml +++ b/packages/CarrierDefaultApp/res/values-am/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"ለምሳሌ፣ የመግቢያ ገጹ የሚታየው ድርጅት ላይሆን ይችላል።"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"ለማንኛውም በአሳሽ በኩል ይቀጥሉ"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"የአፈጻጸም ጭማሪ"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"የእርስዎን የ5ጂ ተሞክሮ ያሻሽሉ"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s የአፈጻጸም መጨመሪያ ዕቅድ መግዛትን ይመክራል። በ%2$s ለመግዛት መታ ያድርጉ።"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"አሁን አይደለም"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"አስተዳድር"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"የአፈጻጸም ጭማሪ ይግዙ።"</string> diff --git a/packages/CarrierDefaultApp/res/values-ar/strings.xml b/packages/CarrierDefaultApp/res/values-ar/strings.xml index c2e5ba80db46..9bc5e459f9ce 100644 --- a/packages/CarrierDefaultApp/res/values-ar/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ar/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"على سبيل المثال، قد لا تنتمي صفحة تسجيل الدخول إلى المؤسسة المعروضة."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"المتابعة على أي حال عبر المتصفح"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"تطبيق تعزيز الأداء"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"تحسين تجربة شبكة الجيل الخامس"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"هناك اقتراح من \"%1$s\" لشراء خطة لتعزيز الأداء. انقر للشراء من خلال \"%2$s\"."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"لاحقًا"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"إدارة"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"شراء تطبيق تعزيز الأداء"</string> diff --git a/packages/CarrierDefaultApp/res/values-as/strings.xml b/packages/CarrierDefaultApp/res/values-as/strings.xml index 8881940ff71a..732c52dc6717 100644 --- a/packages/CarrierDefaultApp/res/values-as/strings.xml +++ b/packages/CarrierDefaultApp/res/values-as/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"উদাহৰণস্বৰূপে, আপোনাক দেখুওৱা লগ ইনৰ পৃষ্ঠাটো প্ৰতিষ্ঠানটোৰ নিজা নহ\'বও পাৰে।"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"তথাপিও ব্ৰাউজাৰৰ জৰিয়তে অব্যাহত ৰাখক"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"কাৰ্যক্ষমতা পৰিৱৰ্ধন"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"আপোনাৰ 5G অভিজ্ঞতা উন্নত কৰক"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$sএ এখন কাৰ্যক্ষমতা পৰিৱৰ্ধন অঁচনি ক্ৰয় কৰাৰ চুপাৰিছ কৰে। %2$sৰ জৰিয়তে ক্ৰয় কৰিবলৈ টিপক।"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"এতিয়া নহয়"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"পৰিচালনা কৰক"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"এটা কাৰ্যক্ষমতা পৰিৱৰ্ধন ক্ৰয় কৰক।"</string> diff --git a/packages/CarrierDefaultApp/res/values-az/strings.xml b/packages/CarrierDefaultApp/res/values-az/strings.xml index f64fcb62ae95..05c300f5217b 100644 --- a/packages/CarrierDefaultApp/res/values-az/strings.xml +++ b/packages/CarrierDefaultApp/res/values-az/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Məsələn, giriş səhifəsi göstərilən təşkilata aid olmaya bilər."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Hər bir halda brazuer ilə davam edin"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performans artırması"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"5G təcrübənizi təkmilləşdirin"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s performans artırma planı almağı tövsiyə edir. %2$s ilə almaq üçün toxunun."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"İndi yox"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"İdarə edin"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Performans artırması alın."</string> diff --git a/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml b/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml index 5533bfd1d1e2..deeb5c732c69 100644 --- a/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml +++ b/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Na primer, stranica za prijavljivanje možda ne pripada prikazanoj organizaciji."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Ipak nastavi preko pregledača"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Poboljšanje učinka"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Poboljšajte 5G doživljaj"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s preporučuje kupovinu paketa za poboljšanje performansi. Dodirnite da biste kupili preko %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ne sada"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Upravljaj"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Kupite poboljšanje učinka."</string> diff --git a/packages/CarrierDefaultApp/res/values-be/strings.xml b/packages/CarrierDefaultApp/res/values-be/strings.xml index 0053cdaa70c3..4f820ca296f7 100644 --- a/packages/CarrierDefaultApp/res/values-be/strings.xml +++ b/packages/CarrierDefaultApp/res/values-be/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Напрыклад, старонка ўваходу можа не належаць указанай арганізацыі."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Усё роўна працягнуць праз браўзер"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Павышэнне прадукцыйнасці"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Пашырце магчымасці 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s рэкамендуе купіць план павышэння прадукцыйнасці. Націсніце, каб купіць праз %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Не цяпер"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Кіраваць"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Аплаціце павышэнне прадукцыйнасці."</string> diff --git a/packages/CarrierDefaultApp/res/values-bg/strings.xml b/packages/CarrierDefaultApp/res/values-bg/strings.xml index a37e0a36454e..a32c632e269c 100644 --- a/packages/CarrierDefaultApp/res/values-bg/strings.xml +++ b/packages/CarrierDefaultApp/res/values-bg/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Например страницата за вход може да не принадлежи на показаната организация."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Продължаване през браузър въпреки това"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Увеличаване на ефективността"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Подобряване на практическата работа с 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s препоръчва да купите план за увеличаване на ефективността. Докоснете, за да купите чрез %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Не сега"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Управление"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Купете пакет за увеличаване на ефективността."</string> diff --git a/packages/CarrierDefaultApp/res/values-bn/strings.xml b/packages/CarrierDefaultApp/res/values-bn/strings.xml index f78449c8aa98..ac4fab43dae1 100644 --- a/packages/CarrierDefaultApp/res/values-bn/strings.xml +++ b/packages/CarrierDefaultApp/res/values-bn/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"যেমন, লগ-ইন পৃষ্ঠাটি যে প্রতিষ্ঠানের পৃষ্ঠা বলে দেখানো আছে, আসলে তা নাও হতে পারে৷"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"যাই হোক, ব্রাউজারের মাধ্যমে চালিয়ে যান"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"পারফর্ম্যান্স বুস্ট"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"আপনার 5G অভিজ্ঞতা উন্নত করুন"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s পারফর্ম্যান্স বুস্ট প্ল্যান কেনার সাজেশন দেয়। %2$s-এর মাধ্যমে কিনতে ট্যাপ করুন।"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"এখন নয়"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"ম্যানেজ করুন"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"পারফর্ম্যান্স বুস্ট সংক্রান্ত ফিচার কিনুন।"</string> diff --git a/packages/CarrierDefaultApp/res/values-bs/strings.xml b/packages/CarrierDefaultApp/res/values-bs/strings.xml index 7be8e2b9d177..cc59f9dbfdde 100644 --- a/packages/CarrierDefaultApp/res/values-bs/strings.xml +++ b/packages/CarrierDefaultApp/res/values-bs/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Naprimjer, stranica za prijavljivanje možda ne pripada prikazanoj organizaciji."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Ipak nastavi preko preglednika"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Pojačavanje performansi"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Poboljšajte iskustvo s 5G mrežom"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s preporučuje kupovinu paketa za poboljšanje performansi. Dodirnite da kupite koristeći %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ne sada"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Upravljajte"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Kupite pojačavanje performansi."</string> diff --git a/packages/CarrierDefaultApp/res/values-ca/strings.xml b/packages/CarrierDefaultApp/res/values-ca/strings.xml index 54c9e6ed43f1..ded2263b3c46 100644 --- a/packages/CarrierDefaultApp/res/values-ca/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ca/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Per exemple, la pàgina d\'inici de sessió podria no pertànyer a l\'organització que es mostra."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continua igualment mitjançant el navegador"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Optimització de rendiment"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Millora l\'experiència 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomana comprar un pla d\'optimització de rendiment. Toca per comprar-lo mitjançant %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ara no"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gestiona"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Compra una optimització de rendiment."</string> diff --git a/packages/CarrierDefaultApp/res/values-cs/strings.xml b/packages/CarrierDefaultApp/res/values-cs/strings.xml index 8a09421cd369..d21c500dcae1 100644 --- a/packages/CarrierDefaultApp/res/values-cs/strings.xml +++ b/packages/CarrierDefaultApp/res/values-cs/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Přihlašovací stránka například nemusí patřit zobrazované organizaci."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Přesto pokračovat prostřednictvím prohlížeče"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Zvýšení výkonu"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Získejte rychlejší připojení 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s doporučuje zakoupit zvýšení výkonu. Klepnutím ho zakoupíte přes operátora %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Teď ne"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Spravovat"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Kupte si zvýšení výkonu."</string> diff --git a/packages/CarrierDefaultApp/res/values-da/strings.xml b/packages/CarrierDefaultApp/res/values-da/strings.xml index cd411c917fb6..192036d10b4f 100644 --- a/packages/CarrierDefaultApp/res/values-da/strings.xml +++ b/packages/CarrierDefaultApp/res/values-da/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Det er f.eks. ikke sikkert, at loginsiden tilhører den anførte organisation."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Fortsæt alligevel via browseren"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Ydeevneboost"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Få en bedre 5G-oplevelse"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s anbefaler, at du køber et abonnement med ydeevneboost. Tryk for at købe via %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ikke nu"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Administrer"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Køb et ydeevneboost."</string> diff --git a/packages/CarrierDefaultApp/res/values-de/strings.xml b/packages/CarrierDefaultApp/res/values-de/strings.xml index d20a1e88fcda..0226d9f70ba8 100644 --- a/packages/CarrierDefaultApp/res/values-de/strings.xml +++ b/packages/CarrierDefaultApp/res/values-de/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Beispiel: Die Log-in-Seite gehört eventuell nicht zur angezeigten Organisation."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Trotzdem in einem Browser fortfahren"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Leistungs-Boost"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"5G-Nutzung verbessern"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s empfiehlt den Kauf eines Tarifs mit Leistungs-Boost. Du kannst tippen, um über %2$s einen zu kaufen."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Nicht jetzt"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Verwalten"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Leistungs-Boost erwerben."</string> diff --git a/packages/CarrierDefaultApp/res/values-el/strings.xml b/packages/CarrierDefaultApp/res/values-el/strings.xml index 0759011f5f39..96e3eb342e4c 100644 --- a/packages/CarrierDefaultApp/res/values-el/strings.xml +++ b/packages/CarrierDefaultApp/res/values-el/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Για παράδειγμα, η σελίδα σύνδεσης ενδέχεται να μην ανήκει στον οργανισμό που εμφανίζεται."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Συνέχεια ούτως ή άλλως μέσω του προγράμματος περιήγησης"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Ενίσχυση απόδοσης"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Βελτιώστε την εμπειρία 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"Το %1$s προτείνει την αγορά ενός προγράμματος ενίσχυσης απόδοσης. Πατήστε για αγορά μέσω %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Όχι τώρα"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Διαχείριση"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Αγοράστε μια ενίσχυση απόδοσης."</string> diff --git a/packages/CarrierDefaultApp/res/values-en-rAU/strings.xml b/packages/CarrierDefaultApp/res/values-en-rAU/strings.xml index 720dbc7fe7c0..aac44fc876a7 100644 --- a/packages/CarrierDefaultApp/res/values-en-rAU/strings.xml +++ b/packages/CarrierDefaultApp/res/values-en-rAU/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"For example, the login page might not belong to the organisation shown."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continue anyway via browser"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performance boost"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Improve your 5G experience"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recommends buying a performance boost plan. Tap to buy through %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Not now"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Manage"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Purchase a performance boost."</string> diff --git a/packages/CarrierDefaultApp/res/values-en-rCA/strings.xml b/packages/CarrierDefaultApp/res/values-en-rCA/strings.xml index 87978ac33372..d8ec2104098c 100644 --- a/packages/CarrierDefaultApp/res/values-en-rCA/strings.xml +++ b/packages/CarrierDefaultApp/res/values-en-rCA/strings.xml @@ -15,8 +15,8 @@ <string name="ssl_error_example" msgid="6188711843183058764">"For example, the login page may not belong to the organization shown."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continue anyway via browser"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performance boost"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Improve your 5G experience"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recommends buying a performance boost plan. Tap to buy through %2$s."</string> + <string name="performance_boost_notification_title" msgid="6091638924925876776">"Improve your app experience"</string> + <string name="performance_boost_notification_detail" msgid="86969987181456032">"Tap to visit %s\'s website and learn more."</string> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Not now"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Manage"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Purchase a performance boost."</string> diff --git a/packages/CarrierDefaultApp/res/values-en-rGB/strings.xml b/packages/CarrierDefaultApp/res/values-en-rGB/strings.xml index 720dbc7fe7c0..aac44fc876a7 100644 --- a/packages/CarrierDefaultApp/res/values-en-rGB/strings.xml +++ b/packages/CarrierDefaultApp/res/values-en-rGB/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"For example, the login page might not belong to the organisation shown."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continue anyway via browser"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performance boost"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Improve your 5G experience"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recommends buying a performance boost plan. Tap to buy through %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Not now"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Manage"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Purchase a performance boost."</string> diff --git a/packages/CarrierDefaultApp/res/values-en-rIN/strings.xml b/packages/CarrierDefaultApp/res/values-en-rIN/strings.xml index 720dbc7fe7c0..aac44fc876a7 100644 --- a/packages/CarrierDefaultApp/res/values-en-rIN/strings.xml +++ b/packages/CarrierDefaultApp/res/values-en-rIN/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"For example, the login page might not belong to the organisation shown."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continue anyway via browser"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performance boost"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Improve your 5G experience"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recommends buying a performance boost plan. Tap to buy through %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Not now"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Manage"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Purchase a performance boost."</string> diff --git a/packages/CarrierDefaultApp/res/values-en-rXC/strings.xml b/packages/CarrierDefaultApp/res/values-en-rXC/strings.xml index 7324a5bcc1f9..87b007f25650 100644 --- a/packages/CarrierDefaultApp/res/values-en-rXC/strings.xml +++ b/packages/CarrierDefaultApp/res/values-en-rXC/strings.xml @@ -15,8 +15,8 @@ <string name="ssl_error_example" msgid="6188711843183058764">"For example, the login page may not belong to the organization shown."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continue anyway via browser"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performance boost"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Improve your 5G experience"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recommends buying a performance boost plan. Tap to buy through %2$s."</string> + <string name="performance_boost_notification_title" msgid="6091638924925876776">"Improve your app experience"</string> + <string name="performance_boost_notification_detail" msgid="86969987181456032">"Tap to visit %s\'s website and learn more."</string> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Not now"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Manage"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Purchase a performance boost."</string> diff --git a/packages/CarrierDefaultApp/res/values-es-rUS/strings.xml b/packages/CarrierDefaultApp/res/values-es-rUS/strings.xml index fedf1ac6b54b..1ad775129883 100644 --- a/packages/CarrierDefaultApp/res/values-es-rUS/strings.xml +++ b/packages/CarrierDefaultApp/res/values-es-rUS/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Por ejemplo, es posible que la página de acceso no pertenezca a la organización que aparece."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continuar de todos modos desde el navegador"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Aumento de rendimiento"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Mejora tu experiencia de 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomienda que compres un plan de aumento de rendimiento. Presiona para comprar mediante %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ahora no"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Administrar"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Compra un aumento de rendimiento."</string> diff --git a/packages/CarrierDefaultApp/res/values-es/strings.xml b/packages/CarrierDefaultApp/res/values-es/strings.xml index 85642b8d7934..972afa9dc4b0 100644 --- a/packages/CarrierDefaultApp/res/values-es/strings.xml +++ b/packages/CarrierDefaultApp/res/values-es/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Por ejemplo, es posible que la página de inicio de sesión no pertenezca a la organización mostrada."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continuar de todos modos a través del navegador"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Mejora de rendimiento"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Mejora tu experiencia 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomienda comprar un plan de mejora del rendimiento. Toca para comprarlo mediante %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ahora no"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gestionar"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Comprar una mejora de rendimiento."</string> diff --git a/packages/CarrierDefaultApp/res/values-et/strings.xml b/packages/CarrierDefaultApp/res/values-et/strings.xml index 769f24001c68..1ac991cf0c85 100644 --- a/packages/CarrierDefaultApp/res/values-et/strings.xml +++ b/packages/CarrierDefaultApp/res/values-et/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Näiteks ei pruugi sisselogimisleht kuuluda kuvatavale organisatsioonile."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Jätka siiski brauseris"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Jõudluse võimendus"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Täiustage oma 5G-kogemust"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s soovitab osta jõudluse võimendusega paketi. Puudutage teenuse %2$s kaudu ostmiseks."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Mitte praegu"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Haldamine"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Ostke jõudluse võimendus."</string> diff --git a/packages/CarrierDefaultApp/res/values-eu/strings.xml b/packages/CarrierDefaultApp/res/values-eu/strings.xml index 7274bce1b492..fedf29d6563e 100644 --- a/packages/CarrierDefaultApp/res/values-eu/strings.xml +++ b/packages/CarrierDefaultApp/res/values-eu/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Adibidez, baliteke saioa hasteko orria adierazitako erakundearena ez izatea."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Jarraitu arakatzailearen bidez, halere"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Errendimendu-hobekuntza"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Hobetu 5G bidezko konexioa"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s zerbitzuak errendimendua hobetzeko kidetza bat erostea gomendatzen du. Sakatu hau %2$s bidez erosteko."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Orain ez"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Kudeatu"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Erosi errendimendu-hobekuntza bat."</string> diff --git a/packages/CarrierDefaultApp/res/values-fa/strings.xml b/packages/CarrierDefaultApp/res/values-fa/strings.xml index 2cbe29793ada..a9ac1570d44e 100644 --- a/packages/CarrierDefaultApp/res/values-fa/strings.xml +++ b/packages/CarrierDefaultApp/res/values-fa/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"به عنوان مثال، صفحه ورود به سیستم ممکن است متعلق به سازمان نشان داده شده نباشد."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"درهر صورت ازطریق مرورگر ادامه یابد"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"تقویتکننده عملکرد"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"تجربه نسل پنجم شبکه تلفن همراه را بهبود دهید"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s توصیه میکند طرح تقویت عملکرد خریداری شود. برای خرید ازطریق %2$s، ضربه بزنید."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"اکنون نه"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"مدیریت"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"تقویتکننده عملکرد خریداری کنید."</string> diff --git a/packages/CarrierDefaultApp/res/values-fi/strings.xml b/packages/CarrierDefaultApp/res/values-fi/strings.xml index 1a388cdc52ef..e0bf1e768c54 100644 --- a/packages/CarrierDefaultApp/res/values-fi/strings.xml +++ b/packages/CarrierDefaultApp/res/values-fi/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Kirjautumissivu ei välttämättä kuulu näytetylle organisaatiolle."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Jatka selaimen kautta"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Suorituskykyboosti"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Paranna 5G-kokemusta"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s suosittelee suorituskykyboostipaketin ostamista. Napauta ja tee ostos operaattorilla %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ei nyt"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Muuta"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Osta suorituskykyboosti."</string> diff --git a/packages/CarrierDefaultApp/res/values-fr-rCA/strings.xml b/packages/CarrierDefaultApp/res/values-fr-rCA/strings.xml index ad6c794db5f4..a56a4db3adbc 100644 --- a/packages/CarrierDefaultApp/res/values-fr-rCA/strings.xml +++ b/packages/CarrierDefaultApp/res/values-fr-rCA/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Par exemple, la page de connexion pourrait ne pas appartenir à l\'organisation représentée."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continuer quand même dans un navigateur"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Optimiseur de performances"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Améliorer votre expérience de la 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recommande d\'acheter un forfait d\'amélioration des performances. Touchez pour acheter par l\'intermédiaire de %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Plus tard"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gérer"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Achetez un optimiseur de performances."</string> diff --git a/packages/CarrierDefaultApp/res/values-fr/strings.xml b/packages/CarrierDefaultApp/res/values-fr/strings.xml index 92b47bed7065..699d4b330f8c 100644 --- a/packages/CarrierDefaultApp/res/values-fr/strings.xml +++ b/packages/CarrierDefaultApp/res/values-fr/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Par exemple, la page de connexion peut ne pas appartenir à l\'organisation représentée."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continuer quand même dans le navigateur"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Boost de performances"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Améliorer votre expérience 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recommande d\'acheter un forfait d\'amélioration des performances. Appuyez pour acheter via %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Pas maintenant"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gérer"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Achetez un boost de performances."</string> diff --git a/packages/CarrierDefaultApp/res/values-gl/strings.xml b/packages/CarrierDefaultApp/res/values-gl/strings.xml index 2c1be52bdabd..b252b8c4cb17 100644 --- a/packages/CarrierDefaultApp/res/values-gl/strings.xml +++ b/packages/CarrierDefaultApp/res/values-gl/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Por exemplo, é posible que a páxina de inicio de sesión non pertenza á organización que se mostra."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continuar igualmente co navegador"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Mellora de rendemento"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Mellora a túa experiencia 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomenda comprar un plan de mellora do rendemento. Toca para realizar a compra a través de %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Agora non"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Xestionar"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Comprar unha mellora de rendemento."</string> diff --git a/packages/CarrierDefaultApp/res/values-gu/strings.xml b/packages/CarrierDefaultApp/res/values-gu/strings.xml index af09d1375c10..8f1174a2eebf 100644 --- a/packages/CarrierDefaultApp/res/values-gu/strings.xml +++ b/packages/CarrierDefaultApp/res/values-gu/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"ઉદાહરણ તરીકે, લોગિન પૃષ્ઠ બતાવવામાં આવેલી સંસ્થાનું ન પણ હોય."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"તો પણ બ્રાઉઝર મારફતે ચાલુ રાખો"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"પર્ફોર્મન્સ બૂસ્ટ"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"તમારા 5G અનુભવને બહેતર બનાવો"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s પર્ફોર્મન્સ બૂસ્ટ પ્લાન ખરીદવાનો સુઝાવ આપે છે. %2$s મારફતે ખરીદવા માટે ટૅપ કરો."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"હમણાં નહીં"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"મેનેજ કરો"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"પર્ફોર્મન્સ બૂસ્ટ ખરીદો."</string> diff --git a/packages/CarrierDefaultApp/res/values-hi/strings.xml b/packages/CarrierDefaultApp/res/values-hi/strings.xml index e51b1a97dffd..52da32280e17 100644 --- a/packages/CarrierDefaultApp/res/values-hi/strings.xml +++ b/packages/CarrierDefaultApp/res/values-hi/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"उदाहरण के लिए, हो सकता है कि लॉगिन पेज दिखाए गए संगठन का ना हो."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"ब्राउज़र के ज़रिए किसी भी तरह जारी रखें"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"परफ़ॉर्मेंस बूस्ट"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"5G का बेहतर अनुभव पाएं"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s, परफ़ॉर्मेंस को बेहतर बनाने वाले प्लान को खरीदने का सुझाव देता है. %2$s से प्लान खरीदने के लिए टैप करें."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"अभी नहीं"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"मैनेज करें"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"कोई परफ़ॉर्मेंस बूस्ट खरीदें."</string> diff --git a/packages/CarrierDefaultApp/res/values-hr/strings.xml b/packages/CarrierDefaultApp/res/values-hr/strings.xml index 5a22ad54d884..1b601231d738 100644 --- a/packages/CarrierDefaultApp/res/values-hr/strings.xml +++ b/packages/CarrierDefaultApp/res/values-hr/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Na primjer, stranica za prijavu možda ne pripada prikazanoj organizaciji."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Ipak nastavi putem preglednika"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Poboljšanje izvedbe"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Poboljšajte svoj 5G doživljaj"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s preporučuje kupnju paketa za poboljšanje izvedbe. Dodirnite da biste kupili putem usluge %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ne sad"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Upravljajte"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Kupite poboljšanje izvedbe."</string> diff --git a/packages/CarrierDefaultApp/res/values-hu/strings.xml b/packages/CarrierDefaultApp/res/values-hu/strings.xml index a841cb24dc19..026586b893d1 100644 --- a/packages/CarrierDefaultApp/res/values-hu/strings.xml +++ b/packages/CarrierDefaultApp/res/values-hu/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Például lehetséges, hogy a bejelentkezési oldal nem a megjelenített szervezethez tartozik."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Folytatás ennek ellenére böngészőn keresztül"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Teljesítménynövelés"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Az 5G-élmény javítása"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"A(z) %1$s teljesítménynövelő csomag vásárlását javasolja. Koppintson a(z) %2$s szolgáltatón keresztüli vásárláshoz."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Most nem"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Kezelés"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Vásároljon teljesítménynövelést."</string> diff --git a/packages/CarrierDefaultApp/res/values-hy/strings.xml b/packages/CarrierDefaultApp/res/values-hy/strings.xml index 49fbece29a8b..dc93d6e012fc 100644 --- a/packages/CarrierDefaultApp/res/values-hy/strings.xml +++ b/packages/CarrierDefaultApp/res/values-hy/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Օրինակ՝ մուտքի էջը կարող է ցուցադրված կազմակերպության էջը չլինել:"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Շարունակել դիտարկիչի միջոցով"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Արտադրողականության բարձրացում"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Բարելավեք 5G-ի օգտագործման ձեր փորձառությունը"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s օպերատորը խորհուրդ է տալիս ձեռք բերել արդյունավետությունը բարձրացնող սակագնային պլան։ Հպեք՝ %2$s-ի միջոցով գնելու համար։"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ոչ հիմա"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Կառավարել"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Բարձրացրեք ցանցի արտադրողականությունը վճարի դիմաց։"</string> diff --git a/packages/CarrierDefaultApp/res/values-in/strings.xml b/packages/CarrierDefaultApp/res/values-in/strings.xml index 170bd76ca4e6..56b8b2ea6deb 100644 --- a/packages/CarrierDefaultApp/res/values-in/strings.xml +++ b/packages/CarrierDefaultApp/res/values-in/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Misalnya, halaman login mungkin bukan milik organisasi yang ditampilkan."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Tetap lanjutkan melalui browser"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Penguat sinyal"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Tingkatkan pengalaman 5G Anda"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s merekomendasikan pembelian paket penguat sinyal. Ketuk untuk membeli melalui %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Lain kali"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Kelola"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Beli penguat sinyal."</string> diff --git a/packages/CarrierDefaultApp/res/values-is/strings.xml b/packages/CarrierDefaultApp/res/values-is/strings.xml index 8684ee62d626..510d5fdd986c 100644 --- a/packages/CarrierDefaultApp/res/values-is/strings.xml +++ b/packages/CarrierDefaultApp/res/values-is/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Til dæmis getur verið að innskráningarsíðan tilheyri ekki fyrirtækinu sem birtist."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Halda samt áfram í vafra"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Afkastaaukning"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Bættu upplifun þína af 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s mælir með að kaupa áskrift sem eykur afköst. Ýttu til að kaupa í gegnum %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ekki núna"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Stjórna"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Kaupa afkastaaukningu."</string> diff --git a/packages/CarrierDefaultApp/res/values-it/strings.xml b/packages/CarrierDefaultApp/res/values-it/strings.xml index ea884571303f..324aa4f7ce3e 100644 --- a/packages/CarrierDefaultApp/res/values-it/strings.xml +++ b/packages/CarrierDefaultApp/res/values-it/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Ad esempio, la pagina di accesso potrebbe non appartenere all\'organizzazione indicata."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continua comunque dal browser"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Aumento di prestazioni"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Migliora la tua esperienza 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s consiglia l\'acquisto di un piano di miglioramento delle prestazioni. Tocca per acquistare tramite %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Non ora"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gestisci"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Acquista un aumento di prestazioni."</string> diff --git a/packages/CarrierDefaultApp/res/values-iw/strings.xml b/packages/CarrierDefaultApp/res/values-iw/strings.xml index c7229ba56418..9e2f31cb77d8 100644 --- a/packages/CarrierDefaultApp/res/values-iw/strings.xml +++ b/packages/CarrierDefaultApp/res/values-iw/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"לדוגמה, ייתכן שדף ההתחברות אינו שייך לארגון המוצג."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"המשך בכל זאת באמצעות דפדפן"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"שיפור ביצועים"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"שיפור חווית השימוש ב-5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"יש המלצה של %1$s לקנות תוכנית לשיפור הביצועים. אפשר להקיש כדי לקנות דרך %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"לא עכשיו"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"ניהול"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"רכישת שיפור ביצועים."</string> diff --git a/packages/CarrierDefaultApp/res/values-ja/strings.xml b/packages/CarrierDefaultApp/res/values-ja/strings.xml index 2fd86c8a805e..833f8a5b1c04 100644 --- a/packages/CarrierDefaultApp/res/values-ja/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ja/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"たとえば、ログインページが表示されている組織に属していない可能性があります。"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"ブラウザから続行"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"パフォーマンス ブースト"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"5G のエクスペリエンスを改善"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s さんがパフォーマンス ブースト プランの購入をおすすめしています。%2$sまでにタップして購入しましょう。"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"後で"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"管理"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"パフォーマンス ブーストを購入してください。"</string> diff --git a/packages/CarrierDefaultApp/res/values-ka/strings.xml b/packages/CarrierDefaultApp/res/values-ka/strings.xml index 76f72737a7df..507e0d9fe378 100644 --- a/packages/CarrierDefaultApp/res/values-ka/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ka/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"მაგალითად, სისტემაში შესვლის გვერდი შეიძლება არ ეკუთვნოდეს ნაჩვენებ ორგანიზაციას."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"მაინც ბრაუზერში გაგრძელება"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"ეფექტურობის გაძლიერება"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"გააუმჯობესეთ თქვენი 5G გამოცდილება"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s-ის მიერ რეკომენდებულია ეფექტურობის გაძლიერების გეგმის შეძენა. შეეხეთ %2$s-ის დახმარებით შესაძენად."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ახლა არა"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"მართვა"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"ეფექტურობის გაძლიერების შეძენა."</string> diff --git a/packages/CarrierDefaultApp/res/values-kk/strings.xml b/packages/CarrierDefaultApp/res/values-kk/strings.xml index 4a895f47e461..32eae44a3fc9 100644 --- a/packages/CarrierDefaultApp/res/values-kk/strings.xml +++ b/packages/CarrierDefaultApp/res/values-kk/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Мысалы, кіру беті көрсетілген ұйымға тиесілі болмауы мүмкін."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Бәрібір браузер арқылы жалғастыру"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Өнімділікті арттыру"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"5G желісімен жұмысыңызды жақсартыңыз"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s өнімділікті арттыру жоспарын сатып алуды ұсынады. %2$s операторынан сатып алу үшін түртіңіз."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Қазір емес"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Басқару"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Өнімділікті арттыру құралын сатып алыңыз."</string> diff --git a/packages/CarrierDefaultApp/res/values-km/strings.xml b/packages/CarrierDefaultApp/res/values-km/strings.xml index 51a51ff9698f..531226fcfa07 100644 --- a/packages/CarrierDefaultApp/res/values-km/strings.xml +++ b/packages/CarrierDefaultApp/res/values-km/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"ឧទាហរណ៍៖ ទំព័រចូលនេះអាចនឹងមិនមែនជាកម្មសិទ្ធិរបស់ស្ថាប័នដែលបានបង្ហាញនេះទេ។"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"យ៉ាងណាក៏ដោយនៅតែបន្តតាមរយៈកម្មវិធីរុករកតាមអ៊ីនធឺណិត"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"ការបង្កើនប្រតិបត្តិការ"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"កែលម្អបទពិសោធន៍ប្រើ 5G របស់អ្នក"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s ណែនាំឱ្យទិញផែនការជំរុញប្រតិបត្តិការ។ ចុចដើម្បីទិញតាមរយៈ %2$s។"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"កុំទាន់"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"គ្រប់គ្រង"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"ទិញការបង្កើនប្រតិបត្តិការ។"</string> diff --git a/packages/CarrierDefaultApp/res/values-kn/strings.xml b/packages/CarrierDefaultApp/res/values-kn/strings.xml index c97d6f011e0b..4335d0c8285f 100644 --- a/packages/CarrierDefaultApp/res/values-kn/strings.xml +++ b/packages/CarrierDefaultApp/res/values-kn/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"ಉದಾಹರಣೆಗೆ, ಲಾಗಿನ್ ಪುಟವು ತೋರಿಸಲಾಗಿರುವ ಸಂಸ್ಥೆಗೆ ಸಂಬಂಧಿಸಿಲ್ಲದಿರಬಹುದು."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"ಪರವಾಗಿಲ್ಲ, ಬ್ರೌಸರ್ ಮೂಲಕ ಮುಂದುವರಿಸಿ"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"ಕಾರ್ಯಕ್ಷಮತೆ ಬೂಸ್ಟ್"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"ನಿಮ್ಮ 5G ಅನುಭವವನ್ನು ಸುಧಾರಿಸಿ"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಹೆಚ್ಚಿಸುವ ಪ್ಲಾನ್ ಅನ್ನು ಖರೀದಿಸಲು %1$s ಶಿಫಾರಸು ಮಾಡುತ್ತದೆ. %2$s ಮೂಲಕ ಖರೀದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ಈಗ ಬೇಡ"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"ನಿರ್ವಹಿಸಿ"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"ಕಾರ್ಯಕ್ಷಮತೆ ಬೂಸ್ಟ್ ಅನ್ನು ಖರೀದಿಸಿ."</string> diff --git a/packages/CarrierDefaultApp/res/values-ko/strings.xml b/packages/CarrierDefaultApp/res/values-ko/strings.xml index 395627da1571..3bb5628a680c 100644 --- a/packages/CarrierDefaultApp/res/values-ko/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ko/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"예를 들어 로그인 페이지가 표시된 조직에 속하지 않을 수 있습니다."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"브라우저를 통해 계속하기"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"성능 향상"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"5G 사용 환경 개선"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s에서 성능 향상 계획 구매를 추천합니다. %2$s을(를) 통해 구매하려면 탭하세요."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"나중에"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"관리"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"성능 향상 구매"</string> diff --git a/packages/CarrierDefaultApp/res/values-ky/strings.xml b/packages/CarrierDefaultApp/res/values-ky/strings.xml index b3970e394b42..07bb61844531 100644 --- a/packages/CarrierDefaultApp/res/values-ky/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ky/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Мисалы, аккаунтка кирүү баракчасы көрсөтүлгөн уюмга таандык эмес болушу мүмкүн."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Баары бир серепчи аркылуу улантуу"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Иштин майнаптуулугун жогорулатуу"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"5G менен оңой иштеңиз"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s майнаптуулугун жогорулата турган тарифтик планды сатып алууну сунуштайт. %2$s аркылуу сатып алуу үчүн таптаңыз."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Азыр эмес"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Тескөө"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Иштин майнаптуулугун жогорулатууну сатып алыңыз."</string> diff --git a/packages/CarrierDefaultApp/res/values-lo/strings.xml b/packages/CarrierDefaultApp/res/values-lo/strings.xml index 70d88885008c..480a7ce78751 100644 --- a/packages/CarrierDefaultApp/res/values-lo/strings.xml +++ b/packages/CarrierDefaultApp/res/values-lo/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"ຕົວຢ່າງ, ໜ້າເຂົ້າສູ່ລະບົບອາດຈະບໍ່ແມ່ນຂອງອົງກອນທີ່ປາກົດ."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"ດຳເນີນການຕໍ່ຜ່ານໂປຣແກຣມທ່ອງເວັບ"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"ເລັ່ງປະສິດທິພາບ"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"ປັບປຸງປະສົບການ 5G ຂອງທ່ານ"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s ແນະນຳໃຫ້ຊື້ແຜນການເລັ່ງປະສິດທິພາບ. ແຕະເພື່ອຊື້ຜ່ານ %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ບໍ່ຟ້າວເທື່ອ"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"ຈັດການ"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"ຊື້ການເລັ່ງປະສິດທິພາບ."</string> diff --git a/packages/CarrierDefaultApp/res/values-lt/strings.xml b/packages/CarrierDefaultApp/res/values-lt/strings.xml index 80684169317a..1f4a4339471b 100644 --- a/packages/CarrierDefaultApp/res/values-lt/strings.xml +++ b/packages/CarrierDefaultApp/res/values-lt/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Pavyzdžiui, prisijungimo puslapis gali nepriklausyti rodomai organizacijai."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Vis tiek tęsti naudojant naršyklę"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Našumo pagerinimas"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Pagerinkite 5G ryšį"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"„%1$s“ rekomenduoja įsigyti našumo pagerinimo planą. Palieskite, kad įsigytumėte naudodamiesi „%2$s“ paslaugomis."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ne dabar"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Tvarkyti"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Įsigykite našumo pagerinimo paslaugą."</string> diff --git a/packages/CarrierDefaultApp/res/values-lv/strings.xml b/packages/CarrierDefaultApp/res/values-lv/strings.xml index 1fefe92669b1..2fd983775444 100644 --- a/packages/CarrierDefaultApp/res/values-lv/strings.xml +++ b/packages/CarrierDefaultApp/res/values-lv/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Piemēram, pieteikšanās lapa, iespējams, nepieder norādītajai organizācijai."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Tomēr turpināt, izmantojot pārlūkprogrammu"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Veiktspējas uzlabojums"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Uzlabojiet 5G iespējas"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s iesaka iegādāties veiktspējas uzlabošanas plānu. Pieskarieties, lai to iegādātos no %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Vēlāk"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Pārvaldīt"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Iegādājieties veiktspējas uzlabojumu."</string> diff --git a/packages/CarrierDefaultApp/res/values-mk/strings.xml b/packages/CarrierDefaultApp/res/values-mk/strings.xml index 425edfc493e1..6eca2b50a4ba 100644 --- a/packages/CarrierDefaultApp/res/values-mk/strings.xml +++ b/packages/CarrierDefaultApp/res/values-mk/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"На пример, страницата за најавување може да не припаѓа на прикажаната организација."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Сепак продолжи преку прелистувач"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Засилување на изведбата"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Подобрете го вашето доживување со 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s препорачува да купите пакет за засилување на изведбата. Допрете за да купите преку %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Не сега"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Управувајте"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Купете засилување на изведбата."</string> diff --git a/packages/CarrierDefaultApp/res/values-ml/strings.xml b/packages/CarrierDefaultApp/res/values-ml/strings.xml index f2584118516d..2577a144b247 100644 --- a/packages/CarrierDefaultApp/res/values-ml/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ml/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"ഉദാഹരണത്തിന്, കാണിച്ചിരിക്കുന്ന ഓർഗനൈസേഷന്റേതായിരിക്കില്ല ലോഗിൻ പേജ്."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"എന്തായാലും ബ്രൗസർ വഴി തുടരുക"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"പ്രകടന ബൂസ്റ്റ്"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"നിങ്ങളുടെ 5G അനുഭവം മെച്ചപ്പെടുത്തുക"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"പ്രകടന ബൂസ്റ്റ് പ്ലാൻ വാങ്ങാൻ %1$s നിർദ്ദേശിക്കുന്നു. %2$s വഴി വാങ്ങാൻ ടാപ്പ് ചെയ്യുക."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ഇപ്പോൾ വേണ്ട"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"മാനേജ് ചെയ്യുക"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"പ്രകടന ബൂസ്റ്റ് വാങ്ങൂ."</string> diff --git a/packages/CarrierDefaultApp/res/values-mn/strings.xml b/packages/CarrierDefaultApp/res/values-mn/strings.xml index 12e17194ffae..f0fc546cacb4 100644 --- a/packages/CarrierDefaultApp/res/values-mn/strings.xml +++ b/packages/CarrierDefaultApp/res/values-mn/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Жишээлбэл нэвтрэх хуудас нь харагдаж буй байгууллагынх биш байж болно."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Ямар ч тохиолдолд хөтчөөр үргэлжлүүлэх"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Гүйцэтгэлийн идэвхжүүлэлт"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"5G-н хэрэглээгээ сайжруулах"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s гүйцэтгэл нэмэгдүүлэх багцыг худалдаж авахыг зөвлөж байна. %2$s-р дамжуулан худалдан авах бол товшино уу."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Одоо биш"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Удирдах"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Гүйцэтгэлийн идэвхжүүлэлтийг худалдаж аваарай."</string> diff --git a/packages/CarrierDefaultApp/res/values-mr/strings.xml b/packages/CarrierDefaultApp/res/values-mr/strings.xml index a48c60518be1..75cbb1be3e6a 100644 --- a/packages/CarrierDefaultApp/res/values-mr/strings.xml +++ b/packages/CarrierDefaultApp/res/values-mr/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"उदाहरणार्थ, लॉग इन पृष्ठ दर्शवलेल्या संस्थेच्या मालकीचे नसू शकते."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"तरीही ब्राउझरद्वारे सुरू ठेवा"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"परफॉर्मन्स बूस्ट"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"तुमच्या 5G अनुभवामध्ये सुधारणा करा"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s हे परफॉर्मन्स बूस्ट प्लॅन खरेदी करण्याची शिफारस करते. %2$s वरून खरेदी करण्यासाठी टॅप करा."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"आता नको"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"व्यवस्थापित करा"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"परफॉर्मन्स बूस्ट खरेदी करा."</string> diff --git a/packages/CarrierDefaultApp/res/values-ms/strings.xml b/packages/CarrierDefaultApp/res/values-ms/strings.xml index 85651f42b4b3..14842335e668 100644 --- a/packages/CarrierDefaultApp/res/values-ms/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ms/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Contohnya, halaman log masuk mungkin bukan milik organisasi yang ditunjukkan."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Teruskan juga melalui penyemak imbas"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Peningkatan prestasi"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Tingkatkan pengalaman 5G anda"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s mengesyorkan pembelian pelan peningkatan prestasi. Ketik untuk membeli melalui %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Bukan sekarang"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Urus"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Beli perangsang prestasi."</string> diff --git a/packages/CarrierDefaultApp/res/values-my/strings.xml b/packages/CarrierDefaultApp/res/values-my/strings.xml index 34c54b9ea835..e7a860e30ab5 100644 --- a/packages/CarrierDefaultApp/res/values-my/strings.xml +++ b/packages/CarrierDefaultApp/res/values-my/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"ဥပမာ− ဝင်ရောက်ရန် စာမျက်နှာသည် ပြသထားသည့် အဖွဲ့အစည်းနှင့် သက်ဆိုင်မှုမရှိခြင်း ဖြစ်နိုင်ပါသည်။"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"မည်သို့ပင်ဖြစ်စေ ဘရောက်ဇာမှတစ်ဆင့် ရှေ့ဆက်ရန်"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"စွမ်းဆောင်ရည် မြှင့်တင်အက်ပ်"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"5G အသုံးပြုမှု ပိုမိုကောင်းမွန်စေခြင်း"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s သည် စွမ်းဆောင်ရည်မြှင့်တင်သော အစီအစဉ်ဝယ်ရန် အကြံပြုပါသည်။ %2$s မှတစ်ဆင့် ဝယ်ရန် တို့ပါ။"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ယခုမလုပ်ပါ"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"စီမံရန်"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"စွမ်းဆောင်ရည် မြှင့်တင်အက်ပ် ဝယ်ယူရန်။"</string> diff --git a/packages/CarrierDefaultApp/res/values-nb/strings.xml b/packages/CarrierDefaultApp/res/values-nb/strings.xml index b30e3d9783fe..f0d0d7e95256 100644 --- a/packages/CarrierDefaultApp/res/values-nb/strings.xml +++ b/packages/CarrierDefaultApp/res/values-nb/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Det er for eksempel mulig at påloggingssiden ikke tilhører organisasjonen som vises."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Fortsett likevel via nettleseren"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Bedre ytelse"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Få en bedre 5G-opplevelse"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s anbefaler at du kjøper et abonnement for bedre ytelse. Trykk for å kjøpe via %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ikke nå"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Administrer"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Kjøp bedre ytelse."</string> diff --git a/packages/CarrierDefaultApp/res/values-ne/strings.xml b/packages/CarrierDefaultApp/res/values-ne/strings.xml index 4dccdb91f209..f2b0f6efd220 100644 --- a/packages/CarrierDefaultApp/res/values-ne/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ne/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"उदाहरणका लागि, लग इन पृष्ठ देखाइएको संस्थाको नहुन सक्छ।"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"जे भए पनि ब्राउजर मार्फत जारी राख्नुहोस्"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"पर्फर्मेन्स बुस्ट"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"5G प्रयोग गर्दा अझ राम्रो सुविधा पाउनुहोस्"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s ले पर्फर्मेन्स बुस्ट योजना खरिद गर्न सिफारिस गर्छ। %2$s मार्फत खरिद गर्न ट्याप गर्नुहोस्।"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"अहिले होइन"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"व्यवस्थापन गर्नुहोस्"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"पर्फर्मेन्स बुस्ट किन्नुहोस्।"</string> diff --git a/packages/CarrierDefaultApp/res/values-nl/strings.xml b/packages/CarrierDefaultApp/res/values-nl/strings.xml index 6a4642c618dd..f8ff38ad035a 100644 --- a/packages/CarrierDefaultApp/res/values-nl/strings.xml +++ b/packages/CarrierDefaultApp/res/values-nl/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Zo hoort de weergegeven inlogpagina misschien niet bij de weergegeven organisatie."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Toch doorgaan via browser"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Prestatieboost"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Verbeter je 5G-functionaliteit"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s beveelt je aan een prestatieboostabonnement te kopen. Tik om er een te kopen via %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Niet nu"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Beheren"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Koop een prestatieboost."</string> diff --git a/packages/CarrierDefaultApp/res/values-or/strings.xml b/packages/CarrierDefaultApp/res/values-or/strings.xml index f7349f9e68a7..bbe6f25c2b34 100644 --- a/packages/CarrierDefaultApp/res/values-or/strings.xml +++ b/packages/CarrierDefaultApp/res/values-or/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"ଉଦାହରଣସ୍ୱରୂପ, ଲଗଇନ୍ ପୃଷ୍ଠା ଦେଖାଯାଇଥିବା ସଂସ୍ଥାର ହୋଇନଥାଇପାରେ।"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"ବ୍ରାଉଜର୍ ଜରିଆରେ ଯେମିତିବି ହେଉ ଜାରି ରଖନ୍ତୁ"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"ପରଫରମାନ୍ସ ବୁଷ୍ଟ"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"ଆପଣଙ୍କ 5G ଅନୁଭୂତିକୁ ଉନ୍ନତ କରନ୍ତୁ"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s ଏକ ପରଫରମାନ୍ସ ବୁଷ୍ଟ ପ୍ଲାନ କିଣିବା ପାଇଁ ସୁପାରିଶ କରେ। %2$s ମାଧ୍ୟମରେ କିଣିବା ପାଇଁ ଟାପ କରନ୍ତୁ।"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ବର୍ତ୍ତମାନ ନୁହେଁ"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"ପରିଚାଳନା କରନ୍ତୁ"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"ଏକ ପରଫରମାନ୍ସ ବୁଷ୍ଟ କିଣନ୍ତୁ।"</string> diff --git a/packages/CarrierDefaultApp/res/values-pa/strings.xml b/packages/CarrierDefaultApp/res/values-pa/strings.xml index 540a54cc267e..811eca91280f 100644 --- a/packages/CarrierDefaultApp/res/values-pa/strings.xml +++ b/packages/CarrierDefaultApp/res/values-pa/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"ਉਦਾਹਰਣ ਵੱਜੋਂ, ਲੌਗ-ਇਨ ਪੰਨਾ ਦਿਖਾਈ ਗਈ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਨਹੀਂ ਹੋ ਸਕਦਾ ਹੈ।"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"ਬ੍ਰਾਊਜ਼ਰ ਰਾਹੀਂ ਫਿਰ ਵੀ ਜਾਰੀ ਰੱਖੋ"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"ਕਾਰਗੁਜ਼ਾਰੀ ਬੂਸਟ"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"ਆਪਣਾ 5G ਅਨੁਭਵ ਬਿਹਤਰ ਬਣਾਓ"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s ਵੱਲੋਂ ਕਾਰਗੁਜ਼ਾਰੀ ਬੂਸਟ ਪਲਾਨ ਖਰੀਦਣ ਦੀ ਸਿਫ਼ਾਰਸ਼ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। %2$s ਰਾਹੀਂ ਖਰੀਦਣ ਲਈ ਟੈਪ ਕਰੋ।"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ਹੁਣੇ ਨਹੀਂ"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"ਕਾਰਗੁਜ਼ਾਰੀ ਬੂਸਟ ਖਰੀਦੋ।"</string> diff --git a/packages/CarrierDefaultApp/res/values-pl/strings.xml b/packages/CarrierDefaultApp/res/values-pl/strings.xml index de957a934d5e..3cd32972ad3e 100644 --- a/packages/CarrierDefaultApp/res/values-pl/strings.xml +++ b/packages/CarrierDefaultApp/res/values-pl/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Na przykład strona logowania może nie należeć do wyświetlanej organizacji."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Kontynuuj mimo to w przeglądarce"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Zwiększenie wydajności"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Lepiej wykorzystaj potencjał 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"Operator %1$s zaleca zakup abonamentu o zwiększonej wydajności. Kliknij, aby kupić u operatora %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Nie teraz"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Zarządzaj"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Kup wzmocnienie wydajności"</string> diff --git a/packages/CarrierDefaultApp/res/values-pt-rBR/strings.xml b/packages/CarrierDefaultApp/res/values-pt-rBR/strings.xml index 4a144be2a214..85f049d11399 100644 --- a/packages/CarrierDefaultApp/res/values-pt-rBR/strings.xml +++ b/packages/CarrierDefaultApp/res/values-pt-rBR/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Por exemplo, a página de login pode não pertencer à organização mostrada."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continuar mesmo assim pelo navegador"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Aumento de performance"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Melhore sua experiência 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomenda a compra de um plano para aumento de desempenho. Toque para comprar em %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Agora não"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gerenciar"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Comprar um aumento de performance."</string> diff --git a/packages/CarrierDefaultApp/res/values-pt-rPT/strings.xml b/packages/CarrierDefaultApp/res/values-pt-rPT/strings.xml index 56e0c2d70e55..981cc5f3eaf6 100644 --- a/packages/CarrierDefaultApp/res/values-pt-rPT/strings.xml +++ b/packages/CarrierDefaultApp/res/values-pt-rPT/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Por exemplo, a página de início de sessão pode não pertencer à entidade apresentada."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continuar mesmo assim através do navegador"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Aumento do desempenho"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Melhore a sua experiência 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomenda comprar um plano de melhoria do desempenho. Toque para comprar através do %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Agora não"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gerir"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Compre um aumento do desempenho."</string> diff --git a/packages/CarrierDefaultApp/res/values-pt/strings.xml b/packages/CarrierDefaultApp/res/values-pt/strings.xml index 4a144be2a214..85f049d11399 100644 --- a/packages/CarrierDefaultApp/res/values-pt/strings.xml +++ b/packages/CarrierDefaultApp/res/values-pt/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Por exemplo, a página de login pode não pertencer à organização mostrada."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continuar mesmo assim pelo navegador"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Aumento de performance"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Melhore sua experiência 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomenda a compra de um plano para aumento de desempenho. Toque para comprar em %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Agora não"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gerenciar"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Comprar um aumento de performance."</string> diff --git a/packages/CarrierDefaultApp/res/values-ro/strings.xml b/packages/CarrierDefaultApp/res/values-ro/strings.xml index f8611379188c..287be5ae4b8b 100644 --- a/packages/CarrierDefaultApp/res/values-ro/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ro/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"De exemplu, este posibil ca pagina de conectare să nu aparțină organizației afișate."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Continuă oricum prin browser"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Boost de performanță"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Îmbunătățește-ți experiența cu tehnologia 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomandă cumpărarea unui plan pentru îmbunătățirea performanței. Atinge pentru a cumpăra de la %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Nu acum"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gestionează"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Achiziționează un boost de performanță."</string> diff --git a/packages/CarrierDefaultApp/res/values-ru/strings.xml b/packages/CarrierDefaultApp/res/values-ru/strings.xml index f0bff17dbaef..fd1328a3e25e 100644 --- a/packages/CarrierDefaultApp/res/values-ru/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ru/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Например, страница входа в аккаунт может быть фиктивной."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Продолжить в браузере"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Повышение производительности"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Сделайте работу с 5G удобнее"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"\"%1$s\" рекомендует купить тарифный план, повышающий производительность. Чтобы приобрести тариф у оператора \"%2$s\", коснитесь экрана."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Не сейчас"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Настроить"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Повысьте производительность сети за плату."</string> diff --git a/packages/CarrierDefaultApp/res/values-si/strings.xml b/packages/CarrierDefaultApp/res/values-si/strings.xml index 166af5a86a7c..a1cd21d4a6e3 100644 --- a/packages/CarrierDefaultApp/res/values-si/strings.xml +++ b/packages/CarrierDefaultApp/res/values-si/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"උදාහරණයක් ලෙස, පුරනය වන පිටුව පෙන්වා ඇති සංවිධානයට අයිති නැති විය හැක."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"කෙසේ වුවත් බ්රවුසරය හරහා ඉදිරියට යන්න"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"කාර්ය සාධනය ඉහළ නැංවීම"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"ඔබේ 5G අත්දැකීම වැඩි දියුණු කරන්න"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s කාර්ය සාධනය වැඩි කිරීමේ සැලසුමක් මිල දී ගැනීම නිර්දේශ කරයි. %2$s හරහා මිල දී ගැනීමට තට්ටු කරන්න."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"දැන් නොවේ"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"කළමනාකරණය කරන්න"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"කාර්ය සාධනය ඉහළ නැංවීමක් මිල දී ගන්න."</string> diff --git a/packages/CarrierDefaultApp/res/values-sk/strings.xml b/packages/CarrierDefaultApp/res/values-sk/strings.xml index a58bd06491c5..265506547d08 100644 --- a/packages/CarrierDefaultApp/res/values-sk/strings.xml +++ b/packages/CarrierDefaultApp/res/values-sk/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Napríklad prihlasovacia stránka nemusí patriť uvedenej organizácii."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Pokračovať pomocou prehliadača"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Zvýšenie výkonu"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Zlepšite svoje prostredie 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s odporúča zakúpiť tarifu na zvýšenie výkonnosti. Klepnutím kúpte cez %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Teraz nie"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Spravovať"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Kúpte si zvýšenie výkonu."</string> diff --git a/packages/CarrierDefaultApp/res/values-sl/strings.xml b/packages/CarrierDefaultApp/res/values-sl/strings.xml index 3c1fd3ec80f4..32c26599206b 100644 --- a/packages/CarrierDefaultApp/res/values-sl/strings.xml +++ b/packages/CarrierDefaultApp/res/values-sl/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Stran za prijavo na primer morda ne pripada prikazani organizaciji."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Vseeno nadaljuj v brskalniku"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Ojačevalnik zmogljivosti"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Izboljšajte izkušnjo omrežja 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s priporoča nakup paketa ojačevalnika zmogljivosti. Dotaknite se za nakup prek »%2$s«."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ne zdaj"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Upravljanje"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Kupite ojačevalnik zmogljivosti."</string> diff --git a/packages/CarrierDefaultApp/res/values-sq/strings.xml b/packages/CarrierDefaultApp/res/values-sq/strings.xml index 618e33573486..f72af39fcd8e 100644 --- a/packages/CarrierDefaultApp/res/values-sq/strings.xml +++ b/packages/CarrierDefaultApp/res/values-sq/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"për shembull, faqja e identifikimit mund të mos i përkasë organizatës së shfaqur."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Vazhdo gjithsesi nëpërmjet shfletuesit"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Përforcimi i performancës"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Përmirëso përvojën tënde 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s rekomandon blerjen e një plani të përforcimit të performancës. Trokit për të blerë nëpërmjet %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Jo tani"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Menaxho"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Bli një paketë përforcimi të performancës."</string> diff --git a/packages/CarrierDefaultApp/res/values-sr/strings.xml b/packages/CarrierDefaultApp/res/values-sr/strings.xml index d28bacca1a07..466e38cffa52 100644 --- a/packages/CarrierDefaultApp/res/values-sr/strings.xml +++ b/packages/CarrierDefaultApp/res/values-sr/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"На пример, страница за пријављивање можда не припада приказаној организацији."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Ипак настави преко прегледача"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Побољшање учинка"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Побољшајте 5G доживљај"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s препоручује куповину пакета за побољшање перформанси. Додирните да бисте купили преко %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Не сада"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Управљај"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Купите побољшање учинка."</string> diff --git a/packages/CarrierDefaultApp/res/values-sv/strings.xml b/packages/CarrierDefaultApp/res/values-sv/strings.xml index ac044ec517b9..ff438b3c74a4 100644 --- a/packages/CarrierDefaultApp/res/values-sv/strings.xml +++ b/packages/CarrierDefaultApp/res/values-sv/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Det kan t.ex. hända att inloggningssidan inte tillhör den organisation som visas."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Fortsätt ändå via webbläsaren"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Prestandahöjning"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Förbättra din 5G-upplevelse"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s rekommenderar att du köper en prenumeration som kan höja prestandan. Tryck för att köpa via %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Inte nu"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Hantera"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Köp en prestandahöjning."</string> diff --git a/packages/CarrierDefaultApp/res/values-sw/strings.xml b/packages/CarrierDefaultApp/res/values-sw/strings.xml index 1c72f30bbc45..17bcc0fca430 100644 --- a/packages/CarrierDefaultApp/res/values-sw/strings.xml +++ b/packages/CarrierDefaultApp/res/values-sw/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Kwa mfano, ukurasa wa kuingia katika akaunti unaweza usiwe unamilikiwa na shirika lililoonyeshwa."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Endelea hata hivyo kupitia kivinjari"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Kuongeza utendaji"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Boresha hali yako ya utumiaji wa 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s anapendekeza ununue mpango wa kuongeza utendaji. Gusa ili ununue kupitia %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Si sasa"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Dhibiti"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Nunua programu ya kuongeza utendaji."</string> diff --git a/packages/CarrierDefaultApp/res/values-ta/strings.xml b/packages/CarrierDefaultApp/res/values-ta/strings.xml index cfc46e869515..3c347e116a25 100644 --- a/packages/CarrierDefaultApp/res/values-ta/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ta/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"எடுத்துக்காட்டாக, உள்நுழைவுப் பக்கமானது காட்டப்படும் அமைப்பிற்குச் சொந்தமானதாக இல்லாமல் இருக்கலாம்."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"பரவாயில்லை, உலாவி வழியாகத் தொடர்க"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"பெர்ஃபார்மென்ஸ் பூஸ்ட்"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"உங்கள் 5G அனுபவத்தை மேம்படுத்துங்கள்"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"செயல்திறன் மேம்பாட்டுத் திட்டத்தை வாங்க %1$s பரிந்துரைக்கிறது. %2$s மூலம் வாங்க தட்டவும்."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"இப்போது வேண்டாம்"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"நிர்வகியுங்கள்"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"ஒரு பெர்ஃபார்மென்ஸ் பூஸ்ட்டைப் பர்ச்சேஸ் செய்யுங்கள்."</string> diff --git a/packages/CarrierDefaultApp/res/values-te/strings.xml b/packages/CarrierDefaultApp/res/values-te/strings.xml index f31291e6ae7f..003df39d41d7 100644 --- a/packages/CarrierDefaultApp/res/values-te/strings.xml +++ b/packages/CarrierDefaultApp/res/values-te/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"ఉదాహరణకు, లాగిన్ పేజీ చూపిన సంస్థకు చెందినది కాకపోవచ్చు."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"ఏదేమైనా బ్రౌజర్ ద్వారా కొనసాగించండి"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"పనితీరు బూస్ట్"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"మీ 5G అనుభవాన్ని మెరుగుపరుచుకోండి"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"పనితీరును బూస్ట్ చేసే ప్లాన్ను కొనుగోలు చేయమని %1$s సిఫార్సు చేస్తున్నారు. %2$s ద్వారా కొనుగోలు చేయడానికి ట్యాప్ చేయండి."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ఇప్పుడు కాదు"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"మేనేజ్ చేయండి"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"పనితీరు బూస్ట్ను కొనుగోలు చేయండి."</string> diff --git a/packages/CarrierDefaultApp/res/values-th/strings.xml b/packages/CarrierDefaultApp/res/values-th/strings.xml index f20346e5f399..16705d453110 100644 --- a/packages/CarrierDefaultApp/res/values-th/strings.xml +++ b/packages/CarrierDefaultApp/res/values-th/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"ตัวอย่างเช่น หน้าเข้าสู่ระบบอาจไม่ใช่ขององค์กรที่แสดงไว้"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"ดำเนินการต่อผ่านเบราว์เซอร์"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"การเพิ่มประสิทธิภาพ"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"ปรับปรุงประสบการณ์การใช้งาน 5G ของคุณ"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s แนะนำให้ซื้อแพ็กเกจเพิ่มประสิทธิภาพ แตะเพื่อซื้อผ่าน %2$s"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ไว้ทีหลัง"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"จัดการ"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"ซื้อการเพิ่มประสิทธิภาพ"</string> diff --git a/packages/CarrierDefaultApp/res/values-tl/strings.xml b/packages/CarrierDefaultApp/res/values-tl/strings.xml index 9e8029aeaf3d..28cd237d8cc1 100644 --- a/packages/CarrierDefaultApp/res/values-tl/strings.xml +++ b/packages/CarrierDefaultApp/res/values-tl/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Halimbawa, maaaring hindi pag-aari ng ipinapakitang organisasyon ang page ng login."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Magpatuloy pa rin sa pamamagitan ng browser"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Pag-boost ng performance"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Pagandahin ang iyong karanasan sa 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"Inirerekomenda ng %1$s na bumili ng plan sa performance boost. I-tap para bumili sa pamamagitan ng %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Huwag muna"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Pamahalaan"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Bumili ng pang-boost ng performance."</string> diff --git a/packages/CarrierDefaultApp/res/values-tr/strings.xml b/packages/CarrierDefaultApp/res/values-tr/strings.xml index c8fb73b42da2..d35db1de4c97 100644 --- a/packages/CarrierDefaultApp/res/values-tr/strings.xml +++ b/packages/CarrierDefaultApp/res/values-tr/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Örneğin, giriş sayfası, gösterilen kuruluşa ait olmayabilir."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Yine de tarayıcıyla devam et"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performans artışı"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"5G deneyiminizi iyileştirin"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s, performans artırma planı satın almanızı öneriyor. %2$s aracılığıyla satın almak için dokunun."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Şimdi değil"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Yönet"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Performans artışı satın alın."</string> diff --git a/packages/CarrierDefaultApp/res/values-uk/strings.xml b/packages/CarrierDefaultApp/res/values-uk/strings.xml index 9927ea05bb3f..5d6e34a45bfa 100644 --- a/packages/CarrierDefaultApp/res/values-uk/strings.xml +++ b/packages/CarrierDefaultApp/res/values-uk/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Наприклад, сторінка входу може не належати вказаній організації."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Усе одно продовжити у веб-переглядачі"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Підвищення продуктивності"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Покращте продуктивність свого з’єднання 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s рекомендує придбати тарифний план для підвищення продуктивності. Натисніть, щоб придбати через оператора %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Не зараз"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Керувати"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Придбайте підвищення продуктивності."</string> diff --git a/packages/CarrierDefaultApp/res/values-ur/strings.xml b/packages/CarrierDefaultApp/res/values-ur/strings.xml index 681998bbecbd..466b6c85a27a 100644 --- a/packages/CarrierDefaultApp/res/values-ur/strings.xml +++ b/packages/CarrierDefaultApp/res/values-ur/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"مثال کے طور پر ہو سکتا ہے کہ لاگ ان صفحہ دکھائی گئی تنظیم سے تعلق نہ رکھتا ہو۔"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"براؤزر کے ذریعے بہرحال جاری رکھیں"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"پرفارمینس بوسٹ"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"اپنے 5G تجربے کو بہتر بنائیں"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s ایک پرفارمنس بوسٹ پلان کی تجویز کرتا ہے۔ %2$s استعمال کر کے خریدنے کے لیے تھپتھپائیں۔"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ابھی نہیں"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"نظم کریں"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"پرفارمینس بوسٹ خریدیں۔"</string> diff --git a/packages/CarrierDefaultApp/res/values-uz/strings.xml b/packages/CarrierDefaultApp/res/values-uz/strings.xml index 47006f680e29..82da958c63aa 100644 --- a/packages/CarrierDefaultApp/res/values-uz/strings.xml +++ b/packages/CarrierDefaultApp/res/values-uz/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Masalan, tizimga kirish sahifasi ko‘rsatilgan tashkilotga tegishli bo‘lmasligi mumkin."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Brauzerda davom ettirish"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Unumdorlikni kuchaytirish"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"5G bilan ishlashni qulaylashtiring"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s unumdorlikni oshiradigan tarif rejasini xarid qilishni tavsiya etadi. %2$s orqali xarid qilish uchun bosing."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Hozir emas"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Boshqarish"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Unumdorlikni kuchaytirish xizmatini xarid qiling."</string> diff --git a/packages/CarrierDefaultApp/res/values-vi/strings.xml b/packages/CarrierDefaultApp/res/values-vi/strings.xml index 968b6e19e769..225e07b99468 100644 --- a/packages/CarrierDefaultApp/res/values-vi/strings.xml +++ b/packages/CarrierDefaultApp/res/values-vi/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Ví dụ: trang đăng nhập có thể không thuộc về tổ chức được hiển thị."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Vẫn tiếp tục qua trình duyệt"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Tăng hiệu suất"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Cải thiện trải nghiệm sử dụng 5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s đề xuất bạn mua gói tăng cường hiệu suất. Nhấn để mua thông qua %2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Để sau"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Quản lý"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Mua gói tăng hiệu suất."</string> diff --git a/packages/CarrierDefaultApp/res/values-zh-rCN/strings.xml b/packages/CarrierDefaultApp/res/values-zh-rCN/strings.xml index 563117f83aab..5be55bc8d5d7 100644 --- a/packages/CarrierDefaultApp/res/values-zh-rCN/strings.xml +++ b/packages/CarrierDefaultApp/res/values-zh-rCN/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"例如,登录页面可能并不属于页面上显示的单位。"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"仍然通过浏览器继续操作"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"性能提升方案"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"改善 5G 体验"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s建议购买性能提升计划。点按即可通过%2$s进行购买。"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"以后再说"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"管理"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"购买一份性能提升方案。"</string> diff --git a/packages/CarrierDefaultApp/res/values-zh-rHK/strings.xml b/packages/CarrierDefaultApp/res/values-zh-rHK/strings.xml index 7ed0c3c03897..92f53d3cc36a 100644 --- a/packages/CarrierDefaultApp/res/values-zh-rHK/strings.xml +++ b/packages/CarrierDefaultApp/res/values-zh-rHK/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"例如,登入頁面可能並不屬於所顯示的機構。"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"仍要透過瀏覽器繼續操作"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"效能提升服務"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"改善 5G 體驗"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"「%1$s」建議購買效能提升服務計劃。輕按即可透過「%2$s」購買。"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"暫時不要"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"管理"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"購買效能提升服務。"</string> diff --git a/packages/CarrierDefaultApp/res/values-zh-rTW/strings.xml b/packages/CarrierDefaultApp/res/values-zh-rTW/strings.xml index 332ab9c809dd..6cb4b943f1b5 100644 --- a/packages/CarrierDefaultApp/res/values-zh-rTW/strings.xml +++ b/packages/CarrierDefaultApp/res/values-zh-rTW/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"例如,登入網頁中顯示的機構可能並非該網頁實際隸屬的機構。"</string> <string name="ssl_error_continue" msgid="1138548463994095584">"仍要透過瀏覽器繼續操作"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"效能提升方案"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"改善 5G 體驗"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"「%1$s」建議購買效能提升方案,輕觸即可透過「%2$s」購買。"</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"暫時不要"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"管理"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"購買效能提升方案。"</string> diff --git a/packages/CarrierDefaultApp/res/values-zu/strings.xml b/packages/CarrierDefaultApp/res/values-zu/strings.xml index ae846955bb0d..f8bc50ceb446 100644 --- a/packages/CarrierDefaultApp/res/values-zu/strings.xml +++ b/packages/CarrierDefaultApp/res/values-zu/strings.xml @@ -15,8 +15,10 @@ <string name="ssl_error_example" msgid="6188711843183058764">"Isibonelo, ikhasi lokungena ngemvume kungenzeka lingelenhlangano ebonisiwe."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Qhubeka noma kunjalo ngesiphequluli"</string> <string name="performance_boost_notification_channel" msgid="3475440855635538592">"I-boost yokusebenza"</string> - <string name="performance_boost_notification_title" msgid="946857427149305992">"Thuthukisa umuzwa wakho we-5G"</string> - <string name="performance_boost_notification_detail" msgid="362407668982648351">"I-%1$s incoma ukuthenga uhlelo lokuthuthukisa ukusebenza. Thepha ukuze uthenge nge-%2$s."</string> + <!-- no translation found for performance_boost_notification_title (6091638924925876776) --> + <skip /> + <!-- no translation found for performance_boost_notification_detail (86969987181456032) --> + <skip /> <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Hhayi manje"</string> <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Phatha"</string> <string name="slice_purchase_app_label" msgid="7170191659233241166">"Thenga i-boost yokusebenza."</string> diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml index 720e46dc9b8c..e91d35bf6979 100644 --- a/packages/CarrierDefaultApp/res/values/strings.xml +++ b/packages/CarrierDefaultApp/res/values/strings.xml @@ -19,7 +19,7 @@ <!-- Notification title text for the performance boost notification. --> <string name="performance_boost_notification_title">Improve your app experience</string> <!-- Notification detail text for the performance boost notification. --> - <string name="performance_boost_notification_detail">Tap to visit %s\'s website and learn more.</string> + <string name="performance_boost_notification_detail">Tap to visit %s\'s website and learn more</string> <!-- Notification button text to cancel the performance boost notification. --> <string name="performance_boost_notification_button_not_now">Not now</string> <!-- Notification button text to manage the performance boost notification. --> diff --git a/packages/CredentialManager/res/values-af/strings.xml b/packages/CredentialManager/res/values-af/strings.xml index 3d32642884d5..f0f040f4a866 100644 --- a/packages/CredentialManager/res/values-af/strings.xml +++ b/packages/CredentialManager/res/values-af/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Maak sigblad toe"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gaan terug na die vorige bladsy"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Maak toe"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gebruik jou gestoorde wagwoordsleutel vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gebruik jou gestoorde aanmelding vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Kies ’n gestoorde aanmelding vir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-am/strings.xml b/packages/CredentialManager/res/values-am/strings.xml index 76533e54c94f..730b0b020eeb 100644 --- a/packages/CredentialManager/res/values-am/strings.xml +++ b/packages/CredentialManager/res/values-am/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"ሌሎች የይለፍ ቃል አስተዳዳሪዎች"</string> <string name="close_sheet" msgid="1393792015338908262">"ሉህን ዝጋ"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ወደ ቀዳሚው ገፅ ይመለሱ"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"ዝጋ"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"የተቀመጠ የይለፍ ቁልፍዎን ለ<xliff:g id="APP_NAME">%1$s</xliff:g> ይጠቀሙ?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"የተቀመጠ መግቢያዎን ለ<xliff:g id="APP_NAME">%1$s</xliff:g> ይጠቀሙ?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"የተቆለፉ የሚስጥር ቁልፍ አስተዳዳሪዎች"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"ለመክፈት መታ ያድርጉ"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"ምንም የመግቢያ ማስረጃ የለም"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> ውስጥ ምንም የመግቢያ መረጃ የለም"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"መግቢያዎችን ያስተዳድሩ"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ከሌላ መሣሪያ"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"የተለየ መሣሪያ ይጠቀሙ"</string> diff --git a/packages/CredentialManager/res/values-ar/strings.xml b/packages/CredentialManager/res/values-ar/strings.xml index c5de5c84316d..666f903514c6 100644 --- a/packages/CredentialManager/res/values-ar/strings.xml +++ b/packages/CredentialManager/res/values-ar/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"خدمات مدراء كلمات المرور الأخرى"</string> <string name="close_sheet" msgid="1393792015338908262">"إغلاق ورقة البيانات"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"العودة إلى الصفحة السابقة"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"إغلاق"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"هل تريد استخدام مفتاح المرور المحفوظ لتطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"هل تريد استخدام بيانات اعتماد تسجيل الدخول المحفوظة لتطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"خدمات إدارة كلمات المرور المقفولة"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"انقر لفتح القفل."</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"ما مِن معلومات تسجيل دخول."</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"ليس هناك معلومات تسجيل دخول في <xliff:g id="SOURCE">%1$s</xliff:g>."</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"إداراة عمليات تسجيل الدخول"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"من جهاز آخر"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"استخدام جهاز مختلف"</string> diff --git a/packages/CredentialManager/res/values-as/strings.xml b/packages/CredentialManager/res/values-as/strings.xml index 1ee0a4690aa8..6202de5dec45 100644 --- a/packages/CredentialManager/res/values-as/strings.xml +++ b/packages/CredentialManager/res/values-as/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"শ্বীট বন্ধ কৰক"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"পূৰ্বৱৰ্তী পৃষ্ঠালৈ ঘূৰি যাওক"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"বন্ধ কৰক"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে আপোনাৰ ছেভ হৈ থকা পাছকী ব্যৱহাৰ কৰিবনে?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে আপোনাৰ ছেভ হৈ থকা ছাইন ইন তথ্য ব্যৱহাৰ কৰিবনে?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে ছেভ হৈ থকা এটা ছাইন ইন বাছনি কৰক"</string> diff --git a/packages/CredentialManager/res/values-az/strings.xml b/packages/CredentialManager/res/values-az/strings.xml index 778b78fcc77c..2481a5cf8d80 100644 --- a/packages/CredentialManager/res/values-az/strings.xml +++ b/packages/CredentialManager/res/values-az/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Səhifəni bağlayın"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Əvvəlki səhifəyə qayıdın"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Bağlayın"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün yadda saxlanmış giriş açarı istifadə edilsin?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün yadda saxlanmış girişdən istifadə edilsin?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün yadda saxlanmış girişi seçin"</string> diff --git a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml index e18aed534001..1e7e92e1efc1 100644 --- a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml +++ b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Drugi menadžeri lozinki"</string> <string name="close_sheet" msgid="1393792015338908262">"Zatvorite tabelu"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Vratite se na prethodnu stranicu"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Zatvorite"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Želite da koristite sačuvani pristupni kôd za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Želite da koristite sačuvane podatke za prijavljivanje za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Menadžeri zaključanih lozinki"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Dodirnite da biste otključali"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Nema podataka za prijavljivanje"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Nema podataka za prijavljivanje u: <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Upravljajte prijavljivanjima"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Sa drugog uređaja"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Koristi drugi uređaj"</string> diff --git a/packages/CredentialManager/res/values-be/strings.xml b/packages/CredentialManager/res/values-be/strings.xml index d3512ba38ef0..f89454a58a53 100644 --- a/packages/CredentialManager/res/values-be/strings.xml +++ b/packages/CredentialManager/res/values-be/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Іншыя спосабы ўваходу"</string> <string name="close_sheet" msgid="1393792015338908262">"Закрыць аркуш"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Вярнуцца да папярэдняй старонкі"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Закрыць"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Скарыстаць захаваны ключ доступу для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Скарыстаць захаваныя спосабы ўваходу для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заблакіраваныя спосабы ўваходу"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Націсніце, каб разблакіраваць"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Няма даных для ўваходу"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Ва ўліковым запісе <xliff:g id="SOURCE">%1$s</xliff:g> адсутнічае інфармацыя для ўваходу"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Кіраваць спосабамі ўваходу"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"З іншай прылады"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Скарыстаць іншую прыладу"</string> diff --git a/packages/CredentialManager/res/values-bg/strings.xml b/packages/CredentialManager/res/values-bg/strings.xml index 35926ca216d6..530c8230d18b 100644 --- a/packages/CredentialManager/res/values-bg/strings.xml +++ b/packages/CredentialManager/res/values-bg/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Други мениджъри на пароли"</string> <string name="close_sheet" msgid="1393792015338908262">"Затваряне на таблицата"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Назад към предишната страница"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Затваряне"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Да се използва ли запазеният ви код за достъп за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Да се използват ли запазените ви данни за вход за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заключени мениджъри на пароли"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Докоснете, за да отключите"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Няма данни за вход"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> не съдържа данни за вход"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управление на данните за вход"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"От друго устройство"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Използване на друго устройство"</string> diff --git a/packages/CredentialManager/res/values-bn/strings.xml b/packages/CredentialManager/res/values-bn/strings.xml index 3273aa48caba..c3f0e4b264b1 100644 --- a/packages/CredentialManager/res/values-bn/strings.xml +++ b/packages/CredentialManager/res/values-bn/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"অন্যান্য Password Manager"</string> <string name="close_sheet" msgid="1393792015338908262">"শিট বন্ধ করুন"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"আগের পৃষ্ঠায় ফিরে যান"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"বন্ধ করুন"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর জন্য আপনার সেভ করা পাসকী ব্যবহার করবেন?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর জন্য আপনার সেভ করা সাইন-ইন সম্পর্কিত ক্রেডেনশিয়াল ব্যবহার করবেন?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"লক করা Password Manager"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"আনলক করতে ট্যাপ করুন"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"সাইন-ইন সম্পর্কিত তথ্য নেই"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g>-এ সাইন-ইন সম্পর্কিত কোনও তথ্য নেই"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"সাইন-ইন করার ক্রেডেনশিয়াল ম্যানেজ করুন"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"অন্য ডিভাইস থেকে"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"আলাদা ডিভাইস ব্যবহার করুন"</string> diff --git a/packages/CredentialManager/res/values-bs/strings.xml b/packages/CredentialManager/res/values-bs/strings.xml index 27516513f363..d98061432492 100644 --- a/packages/CredentialManager/res/values-bs/strings.xml +++ b/packages/CredentialManager/res/values-bs/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Drugi upravitelji lozinki"</string> <string name="close_sheet" msgid="1393792015338908262">"Zatvaranje tabele"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Povratak na prethodnu stranicu"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Zatvaranje"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Koristiti sačuvani pristupni ključ za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Koristiti sačuvanu prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Zaključani upravitelji lozinki"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Dodirnite da otključate"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Nema podataka za prijavu"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Nema informacija za prijavu na <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Upravljajte prijavama"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"S drugog uređaja"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Upotrijebite drugi uređaj"</string> diff --git a/packages/CredentialManager/res/values-ca/strings.xml b/packages/CredentialManager/res/values-ca/strings.xml index 0d115d5d08c5..afbfe505d9b0 100644 --- a/packages/CredentialManager/res/values-ca/strings.xml +++ b/packages/CredentialManager/res/values-ca/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Altres gestors de contrasenyes"</string> <string name="close_sheet" msgid="1393792015338908262">"Tanca el full"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Torna a la pàgina anterior"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Tanca"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vols utilitzar la clau d\'accés desada per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vols utilitzar l\'inici de sessió desat per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestors de contrasenyes bloquejats"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Toca per desbloquejar"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Sense informació d\'inici de sessió"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Sense informació d\'inici de sessió per a <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestiona els inicis de sessió"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Des d\'un altre dispositiu"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Utilitza un dispositiu diferent"</string> diff --git a/packages/CredentialManager/res/values-cs/strings.xml b/packages/CredentialManager/res/values-cs/strings.xml index f3e290bf65c4..72e5525e220b 100644 --- a/packages/CredentialManager/res/values-cs/strings.xml +++ b/packages/CredentialManager/res/values-cs/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Další správci hesel"</string> <string name="close_sheet" msgid="1393792015338908262">"Zavřít list"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Zpět na předchozí stránku"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Zavřít"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Použít uložený přístupový klíč pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Použít uložené přihlášení pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Uzamčení správci hesel"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Klepnutím odemknete"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Žádné informace o přihlášení"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> neobsahuje žádné přihlašovací údaje"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Spravovat přihlášení"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Z jiného zařízení"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Použít jiné zařízení"</string> diff --git a/packages/CredentialManager/res/values-da/strings.xml b/packages/CredentialManager/res/values-da/strings.xml index 76c8a8138bd9..148bba83be33 100644 --- a/packages/CredentialManager/res/values-da/strings.xml +++ b/packages/CredentialManager/res/values-da/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Andre adgangskodeadministratorer"</string> <string name="close_sheet" msgid="1393792015338908262">"Luk arket"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gå tilbage til den forrige side"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Luk"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vil du bruge din gemte adgangsnøgle til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vil du bruge din gemte loginmetode til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Låste adgangskodeadministratorer"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Tryk for at låse op"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Ingen loginoplysninger"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Der er ingen loginoplysninger i <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Administrer loginmetoder"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Fra en anden enhed"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Brug en anden enhed"</string> diff --git a/packages/CredentialManager/res/values-de/strings.xml b/packages/CredentialManager/res/values-de/strings.xml index adcaae4af520..103a3d2edc10 100644 --- a/packages/CredentialManager/res/values-de/strings.xml +++ b/packages/CredentialManager/res/values-de/strings.xml @@ -49,6 +49,8 @@ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Zurück zur vorherigen Seite"</string> <!-- no translation found for accessibility_close_button (1163435587545377687) --> <skip /> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gespeicherten Passkey für <xliff:g id="APP_NAME">%1$s</xliff:g> verwenden?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gespeicherte Anmeldedaten für <xliff:g id="APP_NAME">%1$s</xliff:g> verwenden?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Gespeicherte Anmeldedaten für <xliff:g id="APP_NAME">%1$s</xliff:g> auswählen"</string> diff --git a/packages/CredentialManager/res/values-el/strings.xml b/packages/CredentialManager/res/values-el/strings.xml index df1b1a718c45..8cb3f2816a26 100644 --- a/packages/CredentialManager/res/values-el/strings.xml +++ b/packages/CredentialManager/res/values-el/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Κλείσιμο φύλλου"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Επιστροφή στην προηγούμενη σελίδα"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Κλείσιμο"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Να χρησιμοποιηθεί το αποθηκευμένο κλειδί πρόσβασης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Να χρησιμοποιηθούν τα αποθηκευμένα στοιχεία σύνδεσης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Επιλογή αποθηκευμένων στοιχείων σύνδεσης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-en-rAU/strings.xml b/packages/CredentialManager/res/values-en-rAU/strings.xml index e350449cded6..ffba3ce6727b 100644 --- a/packages/CredentialManager/res/values-en-rAU/strings.xml +++ b/packages/CredentialManager/res/values-en-rAU/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Close"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-en-rCA/strings.xml b/packages/CredentialManager/res/values-en-rCA/strings.xml index f6ab350abbb5..32bbe449dde8 100644 --- a/packages/CredentialManager/res/values-en-rCA/strings.xml +++ b/packages/CredentialManager/res/values-en-rCA/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Close"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-en-rGB/strings.xml b/packages/CredentialManager/res/values-en-rGB/strings.xml index e350449cded6..ffba3ce6727b 100644 --- a/packages/CredentialManager/res/values-en-rGB/strings.xml +++ b/packages/CredentialManager/res/values-en-rGB/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Close"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-en-rIN/strings.xml b/packages/CredentialManager/res/values-en-rIN/strings.xml index e350449cded6..ffba3ce6727b 100644 --- a/packages/CredentialManager/res/values-en-rIN/strings.xml +++ b/packages/CredentialManager/res/values-en-rIN/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Close"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-en-rXC/strings.xml b/packages/CredentialManager/res/values-en-rXC/strings.xml index 7f5f0c11a479..49db2fc56ee7 100644 --- a/packages/CredentialManager/res/values-en-rXC/strings.xml +++ b/packages/CredentialManager/res/values-en-rXC/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Close"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-es-rUS/strings.xml b/packages/CredentialManager/res/values-es-rUS/strings.xml index d700ed65236e..91afadc9df11 100644 --- a/packages/CredentialManager/res/values-es-rUS/strings.xml +++ b/packages/CredentialManager/res/values-es-rUS/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Otros administradores de contraseñas"</string> <string name="close_sheet" msgid="1393792015338908262">"Cerrar hoja"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volver a la página anterior"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Cerrar"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"¿Quieres usar tu llave de acceso guardada para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"¿Quieres usar tu acceso guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Administradores de contraseñas bloqueados"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Presiona para desbloquear"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"No hay información de acceso"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"No hay información de acceso en <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Administrar accesos"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Desde otro dispositivo"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar otra voz"</string> diff --git a/packages/CredentialManager/res/values-es/strings.xml b/packages/CredentialManager/res/values-es/strings.xml index ee056af4706c..b1ff41411138 100644 --- a/packages/CredentialManager/res/values-es/strings.xml +++ b/packages/CredentialManager/res/values-es/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Otros gestores de contraseñas"</string> <string name="close_sheet" msgid="1393792015338908262">"Cerrar hoja"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volver a la página anterior"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Cerrar"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"¿Usar la llave de acceso guardada para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"¿Usar el inicio de sesión guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestores de contraseñas bloqueados"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Tocar para desbloquear"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"No hay información de inicio de sesión"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"No hay información de inicio de sesión en <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestionar inicios de sesión"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De otro dispositivo"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar otro dispositivo"</string> diff --git a/packages/CredentialManager/res/values-et/strings.xml b/packages/CredentialManager/res/values-et/strings.xml index accdea5fd7fc..5bacae35d09c 100644 --- a/packages/CredentialManager/res/values-et/strings.xml +++ b/packages/CredentialManager/res/values-et/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Muud paroolihaldurid"</string> <string name="close_sheet" msgid="1393792015338908262">"Sule leht"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Minge tagasi eelmisele lehele"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Sule"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Kas kasutada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks salvestatud pääsuvõtit?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Kas kasutada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks salvestatud sisselogimisandmeid?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Lukustatud paroolihaldurid"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Avamiseks puudutage"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Sisselogimisteave puudub"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Kontol <xliff:g id="SOURCE">%1$s</xliff:g> puudub sisselogimisteave"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Sisselogimisandmete haldamine"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Muus seadmes"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Kasuta teist seadet"</string> diff --git a/packages/CredentialManager/res/values-eu/strings.xml b/packages/CredentialManager/res/values-eu/strings.xml index 18d653227578..9052bd2e0945 100644 --- a/packages/CredentialManager/res/values-eu/strings.xml +++ b/packages/CredentialManager/res/values-eu/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Beste pasahitz-kudeatzaile batzuk"</string> <string name="close_sheet" msgid="1393792015338908262">"Itxi orria"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Itzuli aurreko orrira"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Itxi"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako gorde duzun sarbide-gakoa erabili nahi duzu?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako gorde dituzun kredentzialak erabili nahi dituzu?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Blokeatutako pasahitz-kudeatzaileak"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Desblokeatzeko, sakatu hau"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Ez dago saioa hasteko informaziorik"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Ez dago saioa hasteko informaziorik <xliff:g id="SOURCE">%1$s</xliff:g> kontuan"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Kudeatu kredentzialak"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Beste gailu batean gordetakoak"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Erabili beste gailu bat"</string> diff --git a/packages/CredentialManager/res/values-fa/strings.xml b/packages/CredentialManager/res/values-fa/strings.xml index 68ec685e419d..9e01bf0cef6b 100644 --- a/packages/CredentialManager/res/values-fa/strings.xml +++ b/packages/CredentialManager/res/values-fa/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"دیگر مدیران گذرواژه"</string> <string name="close_sheet" msgid="1393792015338908262">"بستن برگ"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"برگشتن به صفحه قبلی"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"بستن"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"گذرکلید ذخیرهشده برای <xliff:g id="APP_NAME">%1$s</xliff:g> استفاده شود؟"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ورود به سیستم ذخیرهشده برای <xliff:g id="APP_NAME">%1$s</xliff:g> استفاده شود؟"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"مدیران گذرواژه قفلشده"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"برای باز کردن قفل ضربه بزنید"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"اطلاعات ورود به سیستم موجود نیست"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"هیچ اطلاعات ورود به سیستمی در <xliff:g id="SOURCE">%1$s</xliff:g> وجود ندارد"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"مدیریت ورود به سیستمها"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"از دستگاهی دیگر"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"استفاده از دستگاه دیگری"</string> diff --git a/packages/CredentialManager/res/values-fi/strings.xml b/packages/CredentialManager/res/values-fi/strings.xml index 266a2a9297ee..268dbca45a58 100644 --- a/packages/CredentialManager/res/values-fi/strings.xml +++ b/packages/CredentialManager/res/values-fi/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Muut salasanojen ylläpitotyökalut"</string> <string name="close_sheet" msgid="1393792015338908262">"Sulje taulukko"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Takaisin edelliselle sivulle"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Sulje"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Käytetäänkö tallennettua avainkoodiasi täällä: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Käytetäänkö tallennettuja kirjautumistietoja täällä: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Lukitut salasanojen ylläpitotyökalut"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Avaa napauttamalla"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Ei kirjautumistietoja"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Ei kirjautumistietoja (<xliff:g id="SOURCE">%1$s</xliff:g>)"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Muuta kirjautumistietoja"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Toiselta laitteelta"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Käytä toista laitetta"</string> diff --git a/packages/CredentialManager/res/values-fr-rCA/strings.xml b/packages/CredentialManager/res/values-fr-rCA/strings.xml index 914ee55e4241..d5fd858a7867 100644 --- a/packages/CredentialManager/res/values-fr-rCA/strings.xml +++ b/packages/CredentialManager/res/values-fr-rCA/strings.xml @@ -49,6 +49,8 @@ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Retourner à la page précédente"</string> <!-- no translation found for accessibility_close_button (1163435587545377687) --> <skip /> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Utiliser votre clé d\'accès enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Utiliser votre connexion enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choisir une connexion enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-fr/strings.xml b/packages/CredentialManager/res/values-fr/strings.xml index faca62dfcd30..95917e10481a 100644 --- a/packages/CredentialManager/res/values-fr/strings.xml +++ b/packages/CredentialManager/res/values-fr/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Autres gestionnaires de mots de passe"</string> <string name="close_sheet" msgid="1393792015338908262">"Fermer la feuille"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Revenir à la page précédente"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Fermer"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Utiliser votre clé d\'accès enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Utiliser vos informations de connexion enregistrées pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestionnaires de mots de passe verrouillés"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Appuyer pour déverrouiller"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Aucune information de connexion"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Aucune info de connexion dans <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gérer les connexions"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Depuis un autre appareil"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Utiliser un autre appareil"</string> diff --git a/packages/CredentialManager/res/values-gl/strings.xml b/packages/CredentialManager/res/values-gl/strings.xml index 3b2d6f602510..88dc4a0afa20 100644 --- a/packages/CredentialManager/res/values-gl/strings.xml +++ b/packages/CredentialManager/res/values-gl/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Outros xestores de contrasinais"</string> <string name="close_sheet" msgid="1393792015338908262">"Pechar folla"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volver á páxina anterior"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Pechar"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Queres usar a clave de acceso gardada para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Queres usar o método de inicio de sesión gardado para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Xestores de contrasinais bloqueados"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Toca para desbloquear"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Sen información de inicio de sesión"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Non hai información de inicio de sesión para <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Xestionar os métodos de inicio de sesión"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Doutro dispositivo"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar outro dispositivo"</string> diff --git a/packages/CredentialManager/res/values-gu/strings.xml b/packages/CredentialManager/res/values-gu/strings.xml index d3339b746fa6..1237483f1b59 100644 --- a/packages/CredentialManager/res/values-gu/strings.xml +++ b/packages/CredentialManager/res/values-gu/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"અન્ય પાસવર્ડ મેનેજર"</string> <string name="close_sheet" msgid="1393792015338908262">"શીટ બંધ કરો"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"પાછલા પેજ પર પરત જાઓ"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"બંધ કરો"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે શું તમારી સાચવેલી પાસકીનો ઉપયોગ કરીએ?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે શું તમારા સાચવેલા સાઇન-ઇનનો ઉપયોગ કરીએ?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"લૉક કરેલા પાસવર્ડ મેનેજર"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"અનલૉક કરવા માટે ટૅપ કરો"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"સાઇન-ઇનની કોઈ માહિતી નથી"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g>માં સાઇન-ઇનની કોઈ માહિતી નથી"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"સાઇન-ઇન મેનેજ કરો"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"કોઈ અન્ય ડિવાઇસમાંથી"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"કોઈ અન્ય ડિવાઇસનો ઉપયોગ કરો"</string> diff --git a/packages/CredentialManager/res/values-hi/strings.xml b/packages/CredentialManager/res/values-hi/strings.xml index f2790f4e6e75..7f3bb4937988 100644 --- a/packages/CredentialManager/res/values-hi/strings.xml +++ b/packages/CredentialManager/res/values-hi/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"दूसरे पासवर्ड मैनेजर"</string> <string name="close_sheet" msgid="1393792015338908262">"शीट बंद करें"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"पिछले पेज पर वापस जाएं"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"बंद करें"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"क्या आपको <xliff:g id="APP_NAME">%1$s</xliff:g> पर साइन इन करने के लिए, सेव की गई पासकी का इस्तेमाल करना है?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"क्या आपको <xliff:g id="APP_NAME">%1$s</xliff:g> पर साइन इन करने के लिए, सेव की गई जानकारी का इस्तेमाल करना है?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"लॉक किए गए पासवर्ड मैनेजर"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"अनलॉक करने के लिए टैप करें"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"साइन-इन करने से जुड़ी कोई जानकारी उपलब्ध नहीं है"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> में साइन-इन की जानकारी नहीं है"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"साइन इन करने की सुविधा को मैनेज करें"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"किसी दूसरे डिवाइस से"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"दूसरे डिवाइस का इस्तेमाल करें"</string> diff --git a/packages/CredentialManager/res/values-hr/strings.xml b/packages/CredentialManager/res/values-hr/strings.xml index 3d813f9e1e39..7a8354ac770e 100644 --- a/packages/CredentialManager/res/values-hr/strings.xml +++ b/packages/CredentialManager/res/values-hr/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Drugi upravitelji zaporki"</string> <string name="close_sheet" msgid="1393792015338908262">"Zatvaranje lista"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Vratite se na prethodnu stranicu"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Zatvori"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Želite li upotrijebiti spremljeni pristupni ključ za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Želite li upotrijebiti spremljene podatke za prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Upravitelji zaključanih zaporki"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Dodirnite za otključavanje"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Nema podataka o prijavi"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Nema podataka o prijavi u aplikaciji <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Upravljanje prijavama"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Na drugom uređaju"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Upotrijebite drugi uređaj"</string> diff --git a/packages/CredentialManager/res/values-hu/strings.xml b/packages/CredentialManager/res/values-hu/strings.xml index e3b0fec5e672..ae136be950cb 100644 --- a/packages/CredentialManager/res/values-hu/strings.xml +++ b/packages/CredentialManager/res/values-hu/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Egyéb jelszókezelők"</string> <string name="close_sheet" msgid="1393792015338908262">"Munkalap bezárása"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Vissza az előző oldalra"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Bezárás"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Szeretné a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz mentett azonosítókulcsot használni?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Szeretné a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz mentett bejelentkezési adatait használni?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Zárolt jelszókezelők"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Koppintson a feloldáshoz"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Nincsenek bejelentkezési adatok"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Nem találhatók bejelentkezési adatok itt: <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Bejelentkezési adatok kezelése"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Másik eszközről"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Másik eszköz használata"</string> diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml index 6cdc76fad3fc..6b7c97317504 100644 --- a/packages/CredentialManager/res/values-hy/strings.xml +++ b/packages/CredentialManager/res/values-hy/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Գաղտնաբառերի այլ կառավարիչներ"</string> <string name="close_sheet" msgid="1393792015338908262">"Փակել թերթը"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Անցնել նախորդ էջ"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Փակել"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Օգտագործե՞լ պահված անցաբառը <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի համար"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Օգտագործե՞լ մուտքի պահված տվյալները <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի համար"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Գաղտնաբառերի կողպված կառավարիչներ"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Հպեք ապակողպելու համար"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Մուտքի տվյալներ չկան"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Մուտքի տվյալներ չկան այստեղ՝ <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Մուտքի կառավարում"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Մեկ այլ սարքից"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Օգտագործել այլ սարք"</string> diff --git a/packages/CredentialManager/res/values-in/strings.xml b/packages/CredentialManager/res/values-in/strings.xml index f0f00896d155..9eb3c659e306 100644 --- a/packages/CredentialManager/res/values-in/strings.xml +++ b/packages/CredentialManager/res/values-in/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Pengelola sandi lainnya"</string> <string name="close_sheet" msgid="1393792015338908262">"Tutup sheet"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Kembali ke halaman sebelumnya"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Tutup"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gunakan kunci sandi tersimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gunakan info login tersimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Pengelola sandi terkunci"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Ketuk untuk membuka kunci"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Tidak ada info login"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Tidak ada info login di <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Kelola login"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Dari perangkat lain"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gunakan perangkat lain"</string> diff --git a/packages/CredentialManager/res/values-is/strings.xml b/packages/CredentialManager/res/values-is/strings.xml index 7ee8b5d3e813..b6270580cd11 100644 --- a/packages/CredentialManager/res/values-is/strings.xml +++ b/packages/CredentialManager/res/values-is/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Önnur aðgangsorðastjórnun"</string> <string name="close_sheet" msgid="1393792015338908262">"Loka blaði"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Fara aftur á fyrri síðu"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Loka"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Notað vistaðan aðgangslykil fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Nota vistaða innskráningu fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Læst aðgangsorðastjórnun"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Ýttu til að opna"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Engar innskráningarupplýsingar"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Engar innskráningarupplýsingar á <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Stjórna innskráningu"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Úr öðru tæki"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Nota annað tæki"</string> diff --git a/packages/CredentialManager/res/values-it/strings.xml b/packages/CredentialManager/res/values-it/strings.xml index 7f39c1db42a4..9497ceae9f53 100644 --- a/packages/CredentialManager/res/values-it/strings.xml +++ b/packages/CredentialManager/res/values-it/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Altri gestori delle password"</string> <string name="close_sheet" msgid="1393792015338908262">"Chiudi il foglio"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Torna alla pagina precedente"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Chiudi"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vuoi usare la passkey salvata per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vuoi usare l\'accesso salvato per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestori delle password bloccati"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Tocca per sbloccare"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Nessuna informazione di accesso"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Non sono presenti dati di accesso in <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestisci gli accessi"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Da un altro dispositivo"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usa un dispositivo diverso"</string> diff --git a/packages/CredentialManager/res/values-iw/strings.xml b/packages/CredentialManager/res/values-iw/strings.xml index 9100b517ad55..9197ac65c416 100644 --- a/packages/CredentialManager/res/values-iw/strings.xml +++ b/packages/CredentialManager/res/values-iw/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"מנהלי סיסמאות אחרים"</string> <string name="close_sheet" msgid="1393792015338908262">"סגירת הגיליון"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"חזרה לדף הקודם"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"סגירה"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"להשתמש במפתח גישה שנשמר עבור <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"להשתמש בפרטי הכניסה שנשמרו עבור <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"מנהלי סיסמאות נעולים"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"יש להקיש כדי לבטל את הנעילה"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"אין פרטי כניסה"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"אין פרטי כניסה ב-<xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ניהול כניסות"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ממכשיר אחר"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"צריך להשתמש במכשיר אחר"</string> diff --git a/packages/CredentialManager/res/values-ja/strings.xml b/packages/CredentialManager/res/values-ja/strings.xml index c49dd398d328..3c97777637bf 100644 --- a/packages/CredentialManager/res/values-ja/strings.xml +++ b/packages/CredentialManager/res/values-ja/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"シートを閉じます"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"前のページに戻ります"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"閉じる"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したパスキーを使用しますか?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したログイン情報を使用しますか?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したログイン情報の選択"</string> diff --git a/packages/CredentialManager/res/values-ka/strings.xml b/packages/CredentialManager/res/values-ka/strings.xml index f5eb7c94efa0..4d0d9f7e6d95 100644 --- a/packages/CredentialManager/res/values-ka/strings.xml +++ b/packages/CredentialManager/res/values-ka/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"ფურცლის დახურვა"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"წინა გვერდზე დაბრუნება"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"დახურვა"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"გსურთ თქვენი დამახსოვრებული წვდომის გასაღების გამოყენება აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"გსურთ თქვენი დამახსოვრებული სისტემაში შესვლის მონაცემების გამოყენება აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"აირჩიეთ სისტემაში შესვლის ინფორმაცია აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-kk/strings.xml b/packages/CredentialManager/res/values-kk/strings.xml index 8864cb43a258..4be32d86d48e 100644 --- a/packages/CredentialManager/res/values-kk/strings.xml +++ b/packages/CredentialManager/res/values-kk/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Басқа құпия сөз менеджерлері"</string> <string name="close_sheet" msgid="1393792015338908262">"Парақты жабу"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Алдыңғы бетке оралу"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Жабу"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін сақталған кіру кілті пайдаланылсын ба?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін сақталған тіркелу деректері пайдаланылсын ба?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Құлыпталған құпия сөз менеджерлері"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Құлыпты ашу үшін түртіңіз."</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Кіру ақпараты жоқ."</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> аккаунтында кіру туралы ешқандай ақпарат жоқ."</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Кіру әрекеттерін басқару"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Басқа құрылғыдан жасау"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Басқа құрылғыны пайдалану"</string> diff --git a/packages/CredentialManager/res/values-km/strings.xml b/packages/CredentialManager/res/values-km/strings.xml index f257789f9237..313c0c552512 100644 --- a/packages/CredentialManager/res/values-km/strings.xml +++ b/packages/CredentialManager/res/values-km/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"បិទសន្លឹក"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ត្រឡប់ទៅទំព័រមុនវិញ"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"បិទ"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ប្រើកូដសម្ងាត់ដែលបានរក្សាទុករបស់អ្នកសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ប្រើការចូលគណនីដែលបានរក្សាទុករបស់អ្នកសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"ជ្រើសរើសការចូលគណនីដែលបានរក្សាទុកសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-kn/strings.xml b/packages/CredentialManager/res/values-kn/strings.xml index c41bd6312243..750c51bf0c81 100644 --- a/packages/CredentialManager/res/values-kn/strings.xml +++ b/packages/CredentialManager/res/values-kn/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"ಶೀಟ್ ಮುಚ್ಚಿರಿ"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ಹಿಂದಿನ ಪುಟಕ್ಕೆ ಹಿಂದಿರುಗಿ"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"ಮುಚ್ಚಿರಿ"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಉಳಿಸಲಾದ ನಿಮ್ಮ ಪಾಸ್ಕೀ ಅನ್ನು ಬಳಸಬೇಕೆ?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಉಳಿಸಲಾದ ನಿಮ್ಮ ಸೈನ್-ಇನ್ ಅನ್ನು ಬಳಸಬೇಕೆ?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಉಳಿಸಲಾದ ಸೈನ್-ಇನ್ ಮಾಹಿತಿಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> diff --git a/packages/CredentialManager/res/values-ko/strings.xml b/packages/CredentialManager/res/values-ko/strings.xml index 20b734a9ed5f..89b5a3f977f6 100644 --- a/packages/CredentialManager/res/values-ko/strings.xml +++ b/packages/CredentialManager/res/values-ko/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"기타 비밀번호 관리자"</string> <string name="close_sheet" msgid="1393792015338908262">"시트 닫기"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"이전 페이지로 돌아가기"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"닫기"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱용 저장된 패스키를 사용하시겠습니까?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱용 저장된 로그인 정보를 사용하시겠습니까?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"잠긴 비밀번호 관리자"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"탭하여 잠금 해제"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"로그인 정보 없음"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g>의 로그인 정보 없음"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"로그인 관리"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"다른 기기에서"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"다른 기기 사용"</string> diff --git a/packages/CredentialManager/res/values-ky/strings.xml b/packages/CredentialManager/res/values-ky/strings.xml index 895d0e7f63db..3e172b4ace67 100644 --- a/packages/CredentialManager/res/values-ky/strings.xml +++ b/packages/CredentialManager/res/values-ky/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Башка сырсөздөрдү башкаргычтар"</string> <string name="close_sheet" msgid="1393792015338908262">"Баракты жабуу"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Мурунку бетке кайтуу"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Жабуу"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн сакталган мүмкүндүк алуу ачкычын колдоносузбу?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн сакталган кирүү параметрин колдоносузбу?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Кулпуланган сырсөздөрдү башкаргычтар"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Кулпусун ачуу үчүн таптаңыз"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Аккаунтка кирүү тууралуу маалымат жок"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> аккаунтунда кирүү маалыматы жок"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Кирүү параметрлерин тескөө"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Башка түзмөктөн"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Башка түзмөктү колдонуу"</string> diff --git a/packages/CredentialManager/res/values-lo/strings.xml b/packages/CredentialManager/res/values-lo/strings.xml index 757bf135b494..f08d522c71a0 100644 --- a/packages/CredentialManager/res/values-lo/strings.xml +++ b/packages/CredentialManager/res/values-lo/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"ຕົວຈັດການລະຫັດຜ່ານອື່ນໆ"</string> <string name="close_sheet" msgid="1393792015338908262">"ປິດຊີດ"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ກັບຄືນໄປຫາໜ້າກ່ອນໜ້ານີ້"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"ປິດ"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ໃຊ້ກະແຈຜ່ານທີ່ບັນທຶກໄວ້ຂອງທ່ານສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ໃຊ້ການເຂົ້າສູ່ລະບົບທີ່ບັນທຶກໄວ້ຂອງທ່ານສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ຕົວຈັດການລະຫັດຜ່ານທີ່ລັອກໄວ້"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"ແຕະເພື່ອປົດລັອກ"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"ບໍ່ມີຂໍ້ມູນການເຂົ້າສູ່ລະບົບ"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"ບໍ່ມີຂໍ້ມູນການເຂົ້າສູ່ລະບົບໃນ <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ຈັດການການເຂົ້າສູ່ລະບົບ"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ຈາກອຸປະກອນອື່ນ"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ໃຊ້ອຸປະກອນອື່ນ"</string> diff --git a/packages/CredentialManager/res/values-lt/strings.xml b/packages/CredentialManager/res/values-lt/strings.xml index d75993f66286..71b50a834c07 100644 --- a/packages/CredentialManager/res/values-lt/strings.xml +++ b/packages/CredentialManager/res/values-lt/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Kitos slaptažodžių tvarkyklės"</string> <string name="close_sheet" msgid="1393792015338908262">"Uždaryti lapą"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Grįžti į ankstesnį puslapį"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Uždaryti"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Naudoti išsaugotą „passkey“ programai „<xliff:g id="APP_NAME">%1$s</xliff:g>“?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Naudoti išsaugotą prisijungimo informaciją programai „<xliff:g id="APP_NAME">%1$s</xliff:g>“?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Užrakintos slaptažodžių tvarkyklės"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Palieskite, kad atrakintumėte"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Prisijungimo informacijos nėra"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Nėra prisijungimo informacijos <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Tvarkyti prisijungimo informaciją"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Naudojant kitą įrenginį"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Naudoti kitą įrenginį"</string> diff --git a/packages/CredentialManager/res/values-lv/strings.xml b/packages/CredentialManager/res/values-lv/strings.xml index 01c6d55dd471..86a7b8ac65f4 100644 --- a/packages/CredentialManager/res/values-lv/strings.xml +++ b/packages/CredentialManager/res/values-lv/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Citi paroļu pārvaldnieki"</string> <string name="close_sheet" msgid="1393792015338908262">"Aizvērt lapu"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Atgriezties iepriekšējā lapā"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Aizvērt"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vai izmantot saglabāto piekļuves atslēgu lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vai izmantot saglabāto pierakstīšanās informāciju lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Paroļu pārvaldnieki, kuros nepieciešams autentificēties"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Pieskarieties, lai atbloķētu"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Nav pierakstīšanās informācijas"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Kontā <xliff:g id="SOURCE">%1$s</xliff:g> nav pierakstīšanās informācijas."</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Pierakstīšanās informācijas pārvaldība"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"No citas ierīces"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Izmantot citu ierīci"</string> diff --git a/packages/CredentialManager/res/values-mk/strings.xml b/packages/CredentialManager/res/values-mk/strings.xml index a844bc978cb5..393bca1dd836 100644 --- a/packages/CredentialManager/res/values-mk/strings.xml +++ b/packages/CredentialManager/res/values-mk/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Други управници со лозинки"</string> <string name="close_sheet" msgid="1393792015338908262">"Затворете го листот"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Врати се на претходната страница"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Затвори"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Да се користи вашиот зачуван криптографски клуч за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Да се користи вашето зачувано најавување за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заклучени управници со лозинки"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Допрете за да отклучите"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Нема податоци за најавување"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Нема податоци за најавување во <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управувајте со најавувањата"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Од друг уред"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Употребете друг уред"</string> diff --git a/packages/CredentialManager/res/values-ml/strings.xml b/packages/CredentialManager/res/values-ml/strings.xml index de88b28d56c7..efe614c2c04f 100644 --- a/packages/CredentialManager/res/values-ml/strings.xml +++ b/packages/CredentialManager/res/values-ml/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"ഷീറ്റ് അടയ്ക്കുക"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"മുമ്പത്തെ പേജിലേക്ക് മടങ്ങുക"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"അടയ്ക്കുക"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി നിങ്ങൾ സംരക്ഷിച്ച പാസ്കീ ഉപയോഗിക്കണോ?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി നിങ്ങൾ സംരക്ഷിച്ച സൈൻ ഇൻ ഉപയോഗിക്കണോ?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി ഒരു സംരക്ഷിച്ച സൈൻ ഇൻ തിരഞ്ഞെടുക്കുക"</string> diff --git a/packages/CredentialManager/res/values-mn/strings.xml b/packages/CredentialManager/res/values-mn/strings.xml index ee5d66f11b5c..6c7ba7a49f32 100644 --- a/packages/CredentialManager/res/values-mn/strings.xml +++ b/packages/CredentialManager/res/values-mn/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Нууц үгний бусад менежер"</string> <string name="close_sheet" msgid="1393792015338908262">"Хүснэгтийг хаах"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Өмнөх хуудас руу буцах"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Хаах"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д өөрийн хадгалсан passkey-г ашиглах уу?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д хадгалсан нэвтрэх мэдээллээ ашиглах уу?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Түгжээтэй нууц үгний менежерүүд"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Түгжээг тайлахын тулд товшино уу"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Ямар ч нэвтрэх мэдээлэл байхгүй"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g>-д ямар ч нэвтрэх мэдээлэл алга"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Нэвтрэлтийг удирдах"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Өөр төхөөрөмжөөс"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Өөр төхөөрөмж ашиглах"</string> diff --git a/packages/CredentialManager/res/values-mr/strings.xml b/packages/CredentialManager/res/values-mr/strings.xml index bc032124dc7f..ca0480c59b24 100644 --- a/packages/CredentialManager/res/values-mr/strings.xml +++ b/packages/CredentialManager/res/values-mr/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"इतर पासवर्ड व्यवस्थापक"</string> <string name="close_sheet" msgid="1393792015338908262">"शीट बंद करा"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"मागील पेजवर परत जा"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"बंद करा"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी तुमची सेव्ह केलेली पासकी वापरायची का?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी तुमचे सेव्ह केलेले साइन-इन वापरायचे का?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"लॉक केलेले पासवर्ड व्यवस्थापक"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"अनलॉक करण्यासाठी टॅप करा"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"साइन-इन माहिती नाही"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> मध्ये कोणतीही साइन-इन माहिती नाही"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"साइन-इन व्यवस्थापित करा"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"दुसऱ्या डिव्हाइस वरून"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"वेगळे डिव्हाइस वापरा"</string> diff --git a/packages/CredentialManager/res/values-ms/strings.xml b/packages/CredentialManager/res/values-ms/strings.xml index ab2d9536266a..7719f91575d4 100644 --- a/packages/CredentialManager/res/values-ms/strings.xml +++ b/packages/CredentialManager/res/values-ms/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Tutup helaian"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Kembali ke halaman sebelumnya"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Tutup"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gunakan kunci laluan anda yang telah disimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gunakan maklumat log masuk anda yang telah disimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pilih log masuk yang telah disimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-my/strings.xml b/packages/CredentialManager/res/values-my/strings.xml index 92a9e49cab4d..9b60d5c0a6c3 100644 --- a/packages/CredentialManager/res/values-my/strings.xml +++ b/packages/CredentialManager/res/values-my/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"အခြားစကားဝှက်မန်နေဂျာများ"</string> <string name="close_sheet" msgid="1393792015338908262">"စာမျက်နှာ ပိတ်ရန်"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ယခင်စာမျက်နှာကို ပြန်သွားပါ"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"ပိတ်ရန်"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် သိမ်းထားသောလျှို့ဝှက်ကီး သုံးမလား။"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် သိမ်းထားသောလက်မှတ်ထိုးဝင်မှု သုံးမလား။"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"လော့ခ်ချထားသည့် စကားဝှက်မန်နေဂျာများ"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"ဖွင့်ရန် တို့ပါ"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"လက်မှတ်ထိုးဝင်သည့် အချက်အလက် မရှိပါ"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> တွင် လက်မှတ်ထိုးဝင်ရန်အချက်အလက် မရှိပါ"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"လက်မှတ်ထိုးဝင်မှုများ စီမံခြင်း"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"စက်နောက်တစ်ခုမှ"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"အခြားစက်သုံးရန်"</string> diff --git a/packages/CredentialManager/res/values-nb/strings.xml b/packages/CredentialManager/res/values-nb/strings.xml index ac8f7c122225..88692cb8513f 100644 --- a/packages/CredentialManager/res/values-nb/strings.xml +++ b/packages/CredentialManager/res/values-nb/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Andre løsninger for passordlagring"</string> <string name="close_sheet" msgid="1393792015338908262">"Lukk arket"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gå tilbake til den forrige siden"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Lukk"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vil du bruke den lagrede tilgangsnøkkelen for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vil du bruke den lagrede påloggingen for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Låste løsninger for passordlagring"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Trykk for å låse opp"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Ingen påloggingsinformasjon"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Ingen påloggingsinformasjon i <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Administrer pålogginger"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Fra en annen enhet"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Bruk en annen enhet"</string> diff --git a/packages/CredentialManager/res/values-ne/strings.xml b/packages/CredentialManager/res/values-ne/strings.xml index 70ae649c7477..45c9f8ce0fcb 100644 --- a/packages/CredentialManager/res/values-ne/strings.xml +++ b/packages/CredentialManager/res/values-ne/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"अन्य पासवर्ड म्यानेजरहरू"</string> <string name="close_sheet" msgid="1393792015338908262">"पाना बन्द गर्नुहोस्"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"अघिल्लो पेजमा फर्कनुहोस्"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"बन्द गर्नुहोस्"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"आफूले सेभ गरेको पासकी प्रयोग गरी <xliff:g id="APP_NAME">%1$s</xliff:g> मा साइन इन गर्ने हो?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"आफूले सेभ गरेको साइन इनसम्बन्धी जानकारी प्रयोग गरी <xliff:g id="APP_NAME">%1$s</xliff:g> मा साइन इन गर्ने हो?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"लक गरिएका पासवर्ड म्यानेजरहरू"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"अनलक गर्न ट्याप गर्नुहोस्"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"साइन गर्न प्रयोग गरिनु पर्ने जानकारी उपलब्ध छैन"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> मा साइन इन गर्नेसम्बन्धी कुनै पनि जानकारी छैन"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"साइन इनसम्बन्धी विकल्पहरू व्यवस्थापन गर्नुहोस्"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"अर्को डिभाइसका लागि"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"अर्कै डिभाइस प्रयोग गरी हेर्नुहोस्"</string> diff --git a/packages/CredentialManager/res/values-nl/strings.xml b/packages/CredentialManager/res/values-nl/strings.xml index f3d72886dcad..32307ea2910f 100644 --- a/packages/CredentialManager/res/values-nl/strings.xml +++ b/packages/CredentialManager/res/values-nl/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Andere wachtwoordmanagers"</string> <string name="close_sheet" msgid="1393792015338908262">"Blad sluiten"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Ga terug naar de vorige pagina"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Sluiten"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Je opgeslagen toegangssleutel voor <xliff:g id="APP_NAME">%1$s</xliff:g> gebruiken?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Je opgeslagen inloggegevens voor <xliff:g id="APP_NAME">%1$s</xliff:g> gebruiken?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Vergrendelde wachtwoordmanagers"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Tik om te ontgrendelen"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Geen inloggegevens"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Geen inloggevens in <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Inloggegevens beheren"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Via een ander apparaat"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Een ander apparaat gebruiken"</string> diff --git a/packages/CredentialManager/res/values-or/strings.xml b/packages/CredentialManager/res/values-or/strings.xml index 539b3ddbd3c2..953e9b2b3a29 100644 --- a/packages/CredentialManager/res/values-or/strings.xml +++ b/packages/CredentialManager/res/values-or/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"ଅନ୍ୟ Password Manager"</string> <string name="close_sheet" msgid="1393792015338908262">"ସିଟ ବନ୍ଦ କରନ୍ତୁ"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ପୂର୍ବବର୍ତ୍ତୀ ପୃଷ୍ଠାକୁ ଫେରନ୍ତୁ"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"ବନ୍ଦ କରନ୍ତୁ"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ସେଭ କରାଯାଇଥିବା ଆପଣଙ୍କ ପାସକୀ ବ୍ୟବହାର କରିବେ?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ସେଭ କରାଯାଇଥିବା ଆପଣଙ୍କ ସାଇନ-ଇନ ବ୍ୟବହାର କରିବେ?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ଲକ ଥିବା Password Manager"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"ଅନଲକ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"କୌଣସି ସାଇନ-ଇନ ସୂଚନା ନାହିଁ"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g>ରେ କୌଣସି ସାଇନ-ଇନ ସୂଚନା ନାହିଁ"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ସାଇନ-ଇନ ପରିଚାଳନା କରନ୍ତୁ"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ଅନ୍ୟ ଏକ ଡିଭାଇସରୁ"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ଏକ ଭିନ୍ନ ଡିଭାଇସ ବ୍ୟବହାର କରନ୍ତୁ"</string> diff --git a/packages/CredentialManager/res/values-pa/strings.xml b/packages/CredentialManager/res/values-pa/strings.xml index 7e4676a5e56f..2dd6a2ca2e3e 100644 --- a/packages/CredentialManager/res/values-pa/strings.xml +++ b/packages/CredentialManager/res/values-pa/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"ਹੋਰ ਪਾਸਵਰਡ ਪ੍ਰਬੰਧਕ"</string> <string name="close_sheet" msgid="1393792015338908262">"ਸ਼ੀਟ ਬੰਦ ਕਰੋ"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ਪਿਛਲੇ ਪੰਨੇ \'ਤੇ ਵਾਪਸ ਜਾਓ"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"ਬੰਦ ਕਰੋ"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਆਪਣੀ ਰੱਖਿਅਤ ਕੀਤੀ ਪਾਸਕੀ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਆਪਣੀ ਰੱਖਿਅਤ ਕੀਤੀ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ਲਾਕ ਕੀਤੇ ਪਾਸਵਰਡ ਪ੍ਰਬੰਧਕ"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> ਵਿੱਚ ਕੋਈ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਨਹੀਂ"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ਸਾਈਨ-ਇਨਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ਹੋਰ ਡੀਵਾਈਸ ਤੋਂ"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ਵੱਖਰੇ ਡੀਵਾਈਸ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string> diff --git a/packages/CredentialManager/res/values-pl/strings.xml b/packages/CredentialManager/res/values-pl/strings.xml index c77d846674d8..74a0d8fddfdc 100644 --- a/packages/CredentialManager/res/values-pl/strings.xml +++ b/packages/CredentialManager/res/values-pl/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Inne menedżery haseł"</string> <string name="close_sheet" msgid="1393792015338908262">"Zamknij arkusz"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Wróć do poprzedniej strony"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Zamknij"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Użyć zapisanego klucza dla aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Użyć zapisanych danych logowania dla aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Zablokowane menedżery haseł"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Kliknij, aby odblokować"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Brak danych logowania"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g>: brak danych logowania"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Zarządzanie danymi logowania"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Na innym urządzeniu"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Użyj innego urządzenia"</string> diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml index 90433e171664..9eaf99bc4056 100644 --- a/packages/CredentialManager/res/values-pt-rBR/strings.xml +++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Fechar página"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Voltar à página anterior"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Fechar"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Usar sua chave de acesso salva para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Usar suas informações de login salvas para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Escolher um login salvo para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-pt-rPT/strings.xml b/packages/CredentialManager/res/values-pt-rPT/strings.xml index 1abc85d24ae2..ad927e0110ca 100644 --- a/packages/CredentialManager/res/values-pt-rPT/strings.xml +++ b/packages/CredentialManager/res/values-pt-rPT/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Fechar folha"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volte à página anterior"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Fechar"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Usar a sua token de acesso guardada na app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Usar o seu início de sessão guardado na app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Escolha um início de sessão guardado para a app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml index 90433e171664..9eaf99bc4056 100644 --- a/packages/CredentialManager/res/values-pt/strings.xml +++ b/packages/CredentialManager/res/values-pt/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Fechar página"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Voltar à página anterior"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Fechar"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Usar sua chave de acesso salva para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Usar suas informações de login salvas para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Escolher um login salvo para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-ro/strings.xml b/packages/CredentialManager/res/values-ro/strings.xml index 11b3b4987f5b..198ff1d65a9a 100644 --- a/packages/CredentialManager/res/values-ro/strings.xml +++ b/packages/CredentialManager/res/values-ro/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Alți manageri de parole"</string> <string name="close_sheet" msgid="1393792015338908262">"Închide foaia"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Revino la pagina precedentă"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Închide"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Folosești cheia de acces salvată pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Folosești datele de conectare salvate pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Manageri de parole blocate"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Atinge pentru a debloca"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Fără informații de conectare"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Nu există informații de conectare în contul <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestionează acreditările"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De pe alt dispozitiv"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Folosește alt dispozitiv"</string> diff --git a/packages/CredentialManager/res/values-ru/strings.xml b/packages/CredentialManager/res/values-ru/strings.xml index c7d253eb4b7d..846df541961e 100644 --- a/packages/CredentialManager/res/values-ru/strings.xml +++ b/packages/CredentialManager/res/values-ru/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Другие менеджеры паролей"</string> <string name="close_sheet" msgid="1393792015338908262">"Закрыть лист"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Вернуться на предыдущую страницу"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Закрыть"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Использовать сохраненный ключ доступа для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Использовать сохраненные учетные данные для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заблокированные менеджеры паролей"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Нажмите для разблокировки"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Необходимо ввести учетные данные"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"В аккаунте <xliff:g id="SOURCE">%1$s</xliff:g> нет учетных данных"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управление входом"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"С другого устройства"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Использовать другое устройство"</string> diff --git a/packages/CredentialManager/res/values-si/strings.xml b/packages/CredentialManager/res/values-si/strings.xml index 36ce3f8588c7..1df8ea236be8 100644 --- a/packages/CredentialManager/res/values-si/strings.xml +++ b/packages/CredentialManager/res/values-si/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"වෙනත් මුරපද කළමනාකරුවන්"</string> <string name="close_sheet" msgid="1393792015338908262">"පත්රය වසන්න"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"පෙර පිටුවට ආපසු යන්න"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"වසන්න"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා ඔබේ සුරැකි මුරයතුර භාවිතා කරන්න ද?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා ඔබේ සුරැකි පුරනය භාවිතා කරන්න ද?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"අගුළු දැමූ මුරපද කළමනාකරුවන්"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"අගුළු හැරීමට තට්ටු කරන්න"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"පුරනය වීමේ තතු නැත"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> තුළ පුරනය වීමේ තතු නැත"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"පුරනය වීම් කළමනාකරණය කරන්න"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"වෙනත් උපාංගයකින්"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"වෙනස් උපාංගයක් භාවිතා කරන්න"</string> diff --git a/packages/CredentialManager/res/values-sk/strings.xml b/packages/CredentialManager/res/values-sk/strings.xml index 09bf265e1e19..eb187965dd8c 100644 --- a/packages/CredentialManager/res/values-sk/strings.xml +++ b/packages/CredentialManager/res/values-sk/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Iní správcovia hesiel"</string> <string name="close_sheet" msgid="1393792015338908262">"Zavrieť hárok"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Prejsť späť na predchádzajúcu stránku"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Zavrieť"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Chcete pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> použiť uložený prístupový kľúč?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Chcete pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> použiť uložené prihlasovacie údaje?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Správcovia uzamknutých hesiel"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Odomknúť klepnutím"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Žiadne prihlasovacie údaje"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Žiadne prihlasovacie údaje v zdroji <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Spravovať prihlasovacie údaje"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Z iného zariadenia"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Použiť iné zariadenie"</string> diff --git a/packages/CredentialManager/res/values-sl/strings.xml b/packages/CredentialManager/res/values-sl/strings.xml index 29b6410e77c0..15bcbae8c133 100644 --- a/packages/CredentialManager/res/values-sl/strings.xml +++ b/packages/CredentialManager/res/values-sl/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Zapri list"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Nazaj na prejšnjo stran"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Zapri"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Želite uporabiti shranjeni ključ za dostop do aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Želite uporabiti shranjene podatke za prijavo v aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Izberite shranjene podatke za prijavo v aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/CredentialManager/res/values-sq/strings.xml b/packages/CredentialManager/res/values-sq/strings.xml index 6650668abce6..1e4653836789 100644 --- a/packages/CredentialManager/res/values-sq/strings.xml +++ b/packages/CredentialManager/res/values-sq/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Menaxherët e tjerë të fjalëkalimeve"</string> <string name="close_sheet" msgid="1393792015338908262">"Mbyll fletën"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Kthehu te faqja e mëparshme"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Mbyll"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Të përdoret fjalëkalimi yt i ruajtur për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Të përdoret identifikimi yt i ruajtur për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Menaxherët e fjalëkalimeve të kyçura"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Trokit për të shkyçur"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Nuk ka informacione për identifikimin"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Nuk ka informacione regjistrimi në <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Identifikimet e menaxhimit"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Nga një pajisje tjetër"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Përdor një pajisje tjetër"</string> diff --git a/packages/CredentialManager/res/values-sr/strings.xml b/packages/CredentialManager/res/values-sr/strings.xml index 877d45c94c4a..bbb54633084d 100644 --- a/packages/CredentialManager/res/values-sr/strings.xml +++ b/packages/CredentialManager/res/values-sr/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Други менаџери лозинки"</string> <string name="close_sheet" msgid="1393792015338908262">"Затворите табелу"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Вратите се на претходну страницу"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Затворите"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Желите да користите сачувани приступни кôд за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Желите да користите сачуване податке за пријављивање за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Менаџери закључаних лозинки"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Додирните да бисте откључали"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Нема података за пријављивање"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Нема података за пријављивање у: <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управљајте пријављивањима"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Са другог уређаја"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Користи други уређај"</string> diff --git a/packages/CredentialManager/res/values-sv/strings.xml b/packages/CredentialManager/res/values-sv/strings.xml index 9ec317ef8a70..3d85e14ebb27 100644 --- a/packages/CredentialManager/res/values-sv/strings.xml +++ b/packages/CredentialManager/res/values-sv/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Andra lösenordshanterare"</string> <string name="close_sheet" msgid="1393792015338908262">"Stäng kalkylarket"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gå tillbaka till föregående sida"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Stäng"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vill du använda din sparade nyckel för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vill du använda dina sparade inloggningsuppgifter för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Låsta lösenordshanterare"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Tryck för att låsa upp"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Ingen inloggningsinformation"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Det finns inga inloggningsuppgifter i <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Hantera inloggningar"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Via en annan enhet"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Använd en annan enhet"</string> diff --git a/packages/CredentialManager/res/values-sw/strings.xml b/packages/CredentialManager/res/values-sw/strings.xml index 1a5499c3a654..f0f1db5ef08c 100644 --- a/packages/CredentialManager/res/values-sw/strings.xml +++ b/packages/CredentialManager/res/values-sw/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Vidhibiti vinginevyo vya manenosiri"</string> <string name="close_sheet" msgid="1393792015338908262">"Funga laha"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Rudi kwenye ukurasa uliotangulia"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Funga"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Ungependa kutumia ufunguo wa siri uliohifadhiwa wa<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Ungependa kutumia kitambulisho kilichohifadhiwa cha kuingia katika akaunti ya <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Vidhibiti vya manenosiri vilivyofungwa"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Gusa ili ufungue"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Hakuna maelezo ya kuingia katika akaunti"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Hakuna maelezo ya kuingia katika akaunti kwenye <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Dhibiti michakato ya kuingia katika akaunti"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Kutoka kwenye kifaa kingine"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Tumia kifaa tofauti"</string> diff --git a/packages/CredentialManager/res/values-ta/strings.xml b/packages/CredentialManager/res/values-ta/strings.xml index 19a871f1c644..09a4fcbbd3f5 100644 --- a/packages/CredentialManager/res/values-ta/strings.xml +++ b/packages/CredentialManager/res/values-ta/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"பிற கடவுச்சொல் நிர்வாகிகள்"</string> <string name="close_sheet" msgid="1393792015338908262">"ஷீட்டை மூடும்"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"முந்தைய பக்கத்திற்குச் செல்லும்"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"மூடும்"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கு ஏற்கெனவே சேமிக்கப்பட்ட கடவுக்குறியீட்டைப் பயன்படுத்தவா?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கு ஏற்கெனவே சேமிக்கப்பட்ட உள்நுழைவுத் தகவலைப் பயன்படுத்தவா?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"பூட்டப்பட்ட கடவுச்சொல் நிர்வாகிகள்"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"அன்லாக் செய்ய தட்டவும்"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"உள்நுழைவு விவரங்கள் இல்லை"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> கணக்கில் உள்நுழைவு விவரங்கள் இல்லை"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"உள்நுழைவுகளை நிர்வகித்தல்"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"மற்றொரு சாதனத்திலிருந்து பயன்படுத்து"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"வேறு சாதனத்தைப் பயன்படுத்து"</string> diff --git a/packages/CredentialManager/res/values-te/strings.xml b/packages/CredentialManager/res/values-te/strings.xml index 04ee0feb7c25..e93ff204f981 100644 --- a/packages/CredentialManager/res/values-te/strings.xml +++ b/packages/CredentialManager/res/values-te/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"షీట్ను మూసివేయండి"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"మునుపటి పేజీకి తిరిగి వెళ్లండి"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"మూసివేస్తుంది"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం మీ సేవ్ చేసిన పాస్-కీ వివరాలను ఉపయోగించాలా?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం మీరు సేవ్ చేసిన సైన్ ఇన్ వివరాలను ఉపయోగించాలా?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం సేవ్ చేసిన సైన్ ఇన్ వివరాలను ఎంచుకోండి"</string> diff --git a/packages/CredentialManager/res/values-th/strings.xml b/packages/CredentialManager/res/values-th/strings.xml index 29e87a0bf331..e44a66d043b8 100644 --- a/packages/CredentialManager/res/values-th/strings.xml +++ b/packages/CredentialManager/res/values-th/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"เครื่องมือจัดการรหัสผ่านอื่นๆ"</string> <string name="close_sheet" msgid="1393792015338908262">"ปิดชีต"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"กลับไปยังหน้าก่อนหน้า"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"ปิด"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ใช้พาสคีย์ที่บันทึกไว้สำหรับ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ใช่ไหม"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ใช้การลงชื่อเข้าใช้ที่บันทึกไว้สำหรับ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ใช่ไหม"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"เครื่องมือจัดการรหัสผ่านที่ล็อกไว้"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"แตะเพื่อปลดล็อก"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"ไม่มีข้อมูลการลงชื่อเข้าใช้"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"ไม่มีข้อมูลการลงชื่อเข้าใช้ใน <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"จัดการการลงชื่อเข้าใช้"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"จากอุปกรณ์อื่น"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ใช้อุปกรณ์อื่น"</string> diff --git a/packages/CredentialManager/res/values-tl/strings.xml b/packages/CredentialManager/res/values-tl/strings.xml index 773039fc75ff..6b48af148bca 100644 --- a/packages/CredentialManager/res/values-tl/strings.xml +++ b/packages/CredentialManager/res/values-tl/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Iba pang password manager"</string> <string name="close_sheet" msgid="1393792015338908262">"Isara ang sheet"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Bumalik sa nakaraang page"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Isara"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gamitin ang iyong naka-save na passkey para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gamitin ang iyong naka-save na sign-in para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Mga naka-lock na password manager"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"I-tap para i-unlock"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Walang impormasyon sa pag-sign in"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Walang impormasyon sa pag-sign in sa <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Pamahalaan ang mga sign-in"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Mula sa ibang device"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gumamit ng ibang device"</string> diff --git a/packages/CredentialManager/res/values-tr/strings.xml b/packages/CredentialManager/res/values-tr/strings.xml index 083add361914..b0a354c7ce51 100644 --- a/packages/CredentialManager/res/values-tr/strings.xml +++ b/packages/CredentialManager/res/values-tr/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Diğer şifre yöneticileri"</string> <string name="close_sheet" msgid="1393792015338908262">"Sayfayı kapat"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Önceki sayfaya geri dön"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Kapat"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> için kayıtlı şifre anahtarınız kullanılsın mı?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> için kayıtlı oturum açma bilgileriniz kullanılsın mı?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Kilitli şifre yöneticileri"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Kilidi açmak için dokunun"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Oturum açma bilgisi yok"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> hesabında oturum açma bilgisi yok"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Oturum açma bilgilerini yönetin"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Başka bir cihazdan"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Farklı bir cihaz kullan"</string> diff --git a/packages/CredentialManager/res/values-uk/strings.xml b/packages/CredentialManager/res/values-uk/strings.xml index dd112e0330c7..ec12bc6df62a 100644 --- a/packages/CredentialManager/res/values-uk/strings.xml +++ b/packages/CredentialManager/res/values-uk/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Інші менеджери паролів"</string> <string name="close_sheet" msgid="1393792015338908262">"Закрити аркуш"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Повернутися на попередню сторінку"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Закрити"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Використати збережений ключ доступу для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Використати збережені дані для входу для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заблоковані менеджери паролів"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Торкніться, щоб розблокувати"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Немає даних для входу"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Немає даних для входу в обліковий запис <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Керування даними для входу"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"З іншого пристрою"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Використовувати інший пристрій"</string> diff --git a/packages/CredentialManager/res/values-ur/strings.xml b/packages/CredentialManager/res/values-ur/strings.xml index 3a704fe72aa9..b4bf77f060b2 100644 --- a/packages/CredentialManager/res/values-ur/strings.xml +++ b/packages/CredentialManager/res/values-ur/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"دیگر پاس ورڈ مینیجرز"</string> <string name="close_sheet" msgid="1393792015338908262">"شیٹ بند کریں"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"گزشتہ صفحے پر واپس جائیں"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"بند کریں"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے اپنی محفوظ کردہ پاس کی استعمال کریں؟"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے اپنے محفوظ کردہ سائن ان کو استعمال کریں؟"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"مقفل کردہ پاس ورڈ مینیجرز"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"غیر مقفل کرنے کیلئے تھپتھپائیں"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"سائن ان کی کوئی معلومات نہیں"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> میں سائن ان کی کوئی معلومات نہیں ہے"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"سائن انز کا نظم کریں"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"دوسرے آلے سے"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ایک مختلف آلہ استعمال کریں"</string> diff --git a/packages/CredentialManager/res/values-uz/strings.xml b/packages/CredentialManager/res/values-uz/strings.xml index 8248d52f54b2..92c40eea8ab3 100644 --- a/packages/CredentialManager/res/values-uz/strings.xml +++ b/packages/CredentialManager/res/values-uz/strings.xml @@ -48,6 +48,8 @@ <string name="close_sheet" msgid="1393792015338908262">"Varaqni yopish"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Avvalgi sahifaga qaytish"</string> <string name="accessibility_close_button" msgid="1163435587545377687">"Yopish"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> + <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun saqlangan kalit ishlatilsinmi?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun saqlangan maʼlumotlar ishlatilsinmi?"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> hisob maʼlumotlarini tanlang"</string> diff --git a/packages/CredentialManager/res/values-vi/strings.xml b/packages/CredentialManager/res/values-vi/strings.xml index 058e20721706..dd0aeed5cd9b 100644 --- a/packages/CredentialManager/res/values-vi/strings.xml +++ b/packages/CredentialManager/res/values-vi/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Trình quản lý mật khẩu khác"</string> <string name="close_sheet" msgid="1393792015338908262">"Đóng trang tính"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Quay lại trang trước"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Đóng"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Dùng mã xác thực bạn đã lưu cho <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Dùng thông tin đăng nhập bạn đã lưu cho <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Trình quản lý mật khẩu đã khoá"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Nhấn để mở khoá"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Không có thông tin đăng nhập"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Không có thông tin đăng nhập trong <xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Quản lý thông tin đăng nhập"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Từ một thiết bị khác"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Dùng thiết bị khác"</string> diff --git a/packages/CredentialManager/res/values-zh-rCN/strings.xml b/packages/CredentialManager/res/values-zh-rCN/strings.xml index 976ef57f8739..87bdce3ead2d 100644 --- a/packages/CredentialManager/res/values-zh-rCN/strings.xml +++ b/packages/CredentialManager/res/values-zh-rCN/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"其他密码管理工具"</string> <string name="close_sheet" msgid="1393792015338908262">"关闭工作表"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"返回上一页"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"关闭"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"将您已保存的通行密钥用于<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"将您已保存的登录信息用于<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"已锁定的密码管理工具"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"点按即可解锁"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"无登录信息"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> 中没有任何登录信息"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"管理登录信息"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"通过另一台设备"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"使用其他设备"</string> diff --git a/packages/CredentialManager/res/values-zh-rHK/strings.xml b/packages/CredentialManager/res/values-zh-rHK/strings.xml index 0014d7dcec28..b8d5fc56d5f9 100644 --- a/packages/CredentialManager/res/values-zh-rHK/strings.xml +++ b/packages/CredentialManager/res/values-zh-rHK/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"其他密碼管理工具"</string> <string name="close_sheet" msgid="1393792015338908262">"閂工作表"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"返回上一頁"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"關閉"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」密鑰嗎?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資料嗎?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"已鎖定的密碼管理工具"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"輕按即可解鎖"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"沒有登入資料"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> 中沒有登入資料"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"管理登入資料"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"透過其他裝置"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"使用其他裝置"</string> diff --git a/packages/CredentialManager/res/values-zh-rTW/strings.xml b/packages/CredentialManager/res/values-zh-rTW/strings.xml index 61735849007b..885753ac4280 100644 --- a/packages/CredentialManager/res/values-zh-rTW/strings.xml +++ b/packages/CredentialManager/res/values-zh-rTW/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"其他密碼管理工具"</string> <string name="close_sheet" msgid="1393792015338908262">"關閉功能表"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"返回上一頁"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"關閉"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」密碼金鑰嗎?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資訊嗎?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"已鎖定的密碼管理工具"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"輕觸即可解鎖"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"沒有登入資訊"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> 中沒有登入資訊"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"管理登入資訊"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"透過其他裝置"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"使用其他裝置"</string> diff --git a/packages/CredentialManager/res/values-zu/strings.xml b/packages/CredentialManager/res/values-zu/strings.xml index a1c3230d9836..f077d571e8a7 100644 --- a/packages/CredentialManager/res/values-zu/strings.xml +++ b/packages/CredentialManager/res/values-zu/strings.xml @@ -47,7 +47,8 @@ <string name="other_password_manager" msgid="565790221427004141">"Abanye abaphathi bephasiwedi"</string> <string name="close_sheet" msgid="1393792015338908262">"Vala ishidi"</string> <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Buyela emuva ekhasini langaphambilini"</string> - <!-- no translation found for accessibility_close_button (1163435587545377687) --> + <string name="accessibility_close_button" msgid="1163435587545377687">"Vala"</string> + <!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) --> <skip /> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Sebenzisa ukhiye wakho wokungena olondoloziwe <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Sebenzisa ukungena kwakho ngemvume okulondoloziwe <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> @@ -60,8 +61,7 @@ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Abaphathi bephasiwedi abakhiyiwe"</string> <string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Thepha ukuze uvule"</string> <string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Alukho ulwazi lokungena ngemvume"</string> - <!-- no translation found for no_sign_in_info_in (2641118151920288356) --> - <skip /> + <string name="no_sign_in_info_in" msgid="2641118151920288356">"Alukho ulwazi lokungena ngemvume lwe-<xliff:g id="SOURCE">%1$s</xliff:g>"</string> <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Phatha ukungena ngemvume"</string> <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Kusukela kwenye idivayisi"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Sebenzisa idivayisi ehlukile"</string> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index f8ad196c41f2..0e604ce2cf3a 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -23,6 +23,7 @@ import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL import android.credentials.CredentialOption import android.credentials.GetCredentialRequest import android.credentials.ui.AuthenticationEntry +import android.credentials.ui.CancelUiRequest import android.credentials.ui.Constants import android.credentials.ui.Entry import android.credentials.ui.CreateCredentialProviderData @@ -44,6 +45,8 @@ import com.android.credentialmanager.getflow.GetCredentialUiState import androidx.credentials.CreateCredentialRequest.DisplayInfo import androidx.credentials.CreatePublicKeyCredentialRequest import androidx.credentials.CreatePasswordRequest +import androidx.credentials.GetPasswordOption +import androidx.credentials.GetPublicKeyCredentialOption import java.time.Instant @@ -70,7 +73,7 @@ class CredentialManagerRepo( requestInfo = intent.extras?.getParcelable( RequestInfo.EXTRA_REQUEST_INFO, RequestInfo::class.java - ) ?: testCreatePasswordRequestInfo() + ) ?: testCreatePasskeyRequestInfo() val originName: String? = when (requestInfo.type) { RequestInfo.TYPE_CREATE -> requestInfo.createCredentialRequest?.origin @@ -119,8 +122,7 @@ class CredentialManagerRepo( providerDisableListUiState, defaultProviderId, requestDisplayInfoUiState, - /** isOnPasskeyIntroStateAlready */ - false, + isOnPasskeyIntroStateAlready = false, isPasskeyFirstUse )!!, getCredentialUiState = null, @@ -223,6 +225,14 @@ class CredentialManagerRepo( resultReceiver.send(cancelCode, resultData) } } + + /** Return the request token whose UI should be cancelled, or null otherwise. */ + fun getCancelUiRequestToken(intent: Intent): IBinder? { + return intent.extras?.getParcelable( + CancelUiRequest.EXTRA_CANCEL_UI_REQUEST, + CancelUiRequest::class.java + )?.token + } } // TODO: below are prototype functionalities. To be removed for productionization. @@ -391,7 +401,8 @@ class CredentialManagerRepo( " \"authenticatorSelection\": {\n" + " \"residentKey\": \"required\",\n" + " \"requireResidentKey\": true\n" + - " }}" + " }}", + preferImmediatelyAvailableCredentials = true, ) val credentialData = request.credentialData return RequestInfo.newCreateRequestInfo( @@ -437,19 +448,28 @@ class CredentialManagerRepo( } private fun testGetRequestInfo(): RequestInfo { + val passwordOption = GetPasswordOption() + val passkeyOption = GetPublicKeyCredentialOption( + "json", preferImmediatelyAvailableCredentials = false) return RequestInfo.newGetRequestInfo( Binder(), GetCredentialRequest.Builder( Bundle() ).addCredentialOption( CredentialOption( - "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL", - Bundle(), - Bundle(), /*isSystemProviderRequired=*/ - false + passwordOption.type, + passwordOption.requestData, + passwordOption.candidateQueryData, + passwordOption.isSystemProviderRequired ) - ) - .build(), + ).addCredentialOption( + CredentialOption( + passkeyOption.type, + passkeyOption.requestData, + passkeyOption.candidateQueryData, + passkeyOption.isSystemProviderRequired + ) + ).build(), "com.google.android.youtube" ) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt index a3e4c81571a1..e8e39741c3bd 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt @@ -47,6 +47,12 @@ class CredentialSelectorActivity : ComponentActivity() { super.onCreate(savedInstanceState) Log.d(Constants.LOG_TAG, "Creating new CredentialSelectorActivity") try { + if (CredentialManagerRepo.getCancelUiRequestToken(intent) != null) { + Log.d( + Constants.LOG_TAG, "Received UI cancellation intent; cancelling the activity.") + this.finish() + return + } val userConfigRepo = UserConfigRepo(this) val credManRepo = CredentialManagerRepo(this, intent, userConfigRepo) setContent { @@ -67,10 +73,19 @@ class CredentialSelectorActivity : ComponentActivity() { setIntent(intent) Log.d(Constants.LOG_TAG, "Existing activity received new intent") try { - val userConfigRepo = UserConfigRepo(this) - val credManRepo = CredentialManagerRepo(this, intent, userConfigRepo) + val cancelUiRequestToken = CredentialManagerRepo.getCancelUiRequestToken(intent) val viewModel: CredentialSelectorViewModel by viewModels() - viewModel.onNewCredentialManagerRepo(credManRepo) + if (cancelUiRequestToken != null && + viewModel.shouldCancelCurrentUi(cancelUiRequestToken)) { + Log.d( + Constants.LOG_TAG, "Received UI cancellation intent; cancelling the activity.") + this.finish() + return + } else { + val userConfigRepo = UserConfigRepo(this) + val credManRepo = CredentialManagerRepo(this, intent, userConfigRepo) + viewModel.onNewCredentialManagerRepo(credManRepo) + } } catch (e: Exception) { onInitializationError(e, intent) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt index a1e0823e4f96..9b7139ccc26e 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt @@ -17,6 +17,7 @@ package com.android.credentialmanager import android.app.Activity +import android.os.IBinder import android.util.Log import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.result.ActivityResult @@ -135,6 +136,11 @@ class CredentialSelectorViewModel( uiState = uiState.copy(dialogState = DialogState.COMPLETE) } + /** Return true if the current UI's request token matches the UI cancellation request token. */ + fun shouldCancelCurrentUi(cancelRequestToken: IBinder): Boolean { + return credManRepo.requestInfo.token.equals(cancelRequestToken) + } + /**************************************************************************/ /***** Get Flow Callbacks *****/ /**************************************************************************/ diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index 9c9c2a3a1d10..a834994a5b0f 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -50,7 +50,11 @@ import com.android.credentialmanager.getflow.RemoteEntryInfo import androidx.credentials.CreateCredentialRequest import androidx.credentials.CreateCustomCredentialRequest import androidx.credentials.CreatePasswordRequest +import androidx.credentials.CredentialOption import androidx.credentials.CreatePublicKeyCredentialRequest +import androidx.credentials.CreatePublicKeyCredentialRequestPrivileged +import androidx.credentials.GetPublicKeyCredentialOption +import androidx.credentials.GetPublicKeyCredentialOptionPrivileged import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL import androidx.credentials.provider.Action import androidx.credentials.provider.AuthenticationAction @@ -172,10 +176,27 @@ class GetFlowUtils { context: Context, originName: String?, ): com.android.credentialmanager.getflow.RequestDisplayInfo? { + val getCredentialRequest = requestInfo.getCredentialRequest ?: return null + val preferImmediatelyAvailableCredentials = getCredentialRequest.credentialOptions.any { + val credentialOptionJetpack = CredentialOption.createFrom( + it.type, + it.credentialRetrievalData, + it.credentialRetrievalData, + it.isSystemProviderRequired + ) + if (credentialOptionJetpack is GetPublicKeyCredentialOption) { + credentialOptionJetpack.preferImmediatelyAvailableCredentials + } else if (credentialOptionJetpack is GetPublicKeyCredentialOptionPrivileged) { + credentialOptionJetpack.preferImmediatelyAvailableCredentials + } else { + false + } + } return com.android.credentialmanager.getflow.RequestDisplayInfo( appName = originName ?: getAppLabel(context.packageManager, requestInfo.appPackageName) - ?: return null + ?: return null, + preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials ) } @@ -415,24 +436,25 @@ class CreateFlowUtils { createCredentialRequestJetpack.password, CredentialType.PASSWORD, appLabel, - context.getDrawable(R.drawable.ic_password)!! + context.getDrawable(R.drawable.ic_password) ?: return null, + preferImmediatelyAvailableCredentials = false, ) is CreatePublicKeyCredentialRequest -> { - val requestJson = createCredentialRequestJetpack.requestJson - val json = JSONObject(requestJson) - var name = "" - var displayName = "" - if (json.has("user")) { - val user: JSONObject = json.getJSONObject("user") - name = user.getString("name") - displayName = user.getString("displayName") - } - RequestDisplayInfo( - name, - displayName, - CredentialType.PASSKEY, - appLabel, - context.getDrawable(R.drawable.ic_passkey)!! + newRequestDisplayInfoFromPasskeyJson( + requestJson = createCredentialRequestJetpack.requestJson, + appLabel = appLabel, + context = context, + preferImmediatelyAvailableCredentials = + createCredentialRequestJetpack.preferImmediatelyAvailableCredentials, + ) + } + is CreatePublicKeyCredentialRequestPrivileged -> { + newRequestDisplayInfoFromPasskeyJson( + requestJson = createCredentialRequestJetpack.requestJson, + appLabel = appLabel, + context = context, + preferImmediatelyAvailableCredentials = + createCredentialRequestJetpack.preferImmediatelyAvailableCredentials, ) } is CreateCustomCredentialRequest -> { @@ -446,7 +468,8 @@ class CreateFlowUtils { type = CredentialType.UNKNOWN, appName = appLabel, typeIcon = displayInfo.credentialTypeIcon?.loadDrawable(context) - ?: context.getDrawable(R.drawable.ic_other_sign_in)!! + ?: context.getDrawable(R.drawable.ic_other_sign_in) ?: return null, + preferImmediatelyAvailableCredentials = false, ) } else -> null @@ -613,5 +636,29 @@ class CreateFlowUtils { ) } else null } + + private fun newRequestDisplayInfoFromPasskeyJson( + requestJson: String, + appLabel: String, + context: Context, + preferImmediatelyAvailableCredentials: Boolean, + ): RequestDisplayInfo? { + val json = JSONObject(requestJson) + var name = "" + var displayName = "" + if (json.has("user")) { + val user: JSONObject = json.getJSONObject("user") + name = user.getString("name") + displayName = user.getString("displayName") + } + return RequestDisplayInfo( + name, + displayName, + CredentialType.PASSKEY, + appLabel, + context.getDrawable(R.drawable.ic_passkey) ?: return null, + preferImmediatelyAvailableCredentials, + ) + } } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt index 6d07df70e7bf..297143309d14 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt @@ -24,8 +24,6 @@ enum class DialogState { enum class ResultState { COMPLETE, - NORMAL_CANCELED, - LAUNCH_SETTING_CANCELED } data class DialogResult( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt index b94840f369e1..04a2c07da388 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt @@ -16,6 +16,7 @@ package com.android.credentialmanager.common.ui +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import com.android.credentialmanager.R import androidx.compose.material.Icon @@ -33,7 +34,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme @Composable fun ActionButton(text: String, onClick: () -> Unit) { @@ -42,12 +42,10 @@ fun ActionButton(text: String, onClick: () -> Unit) { onClick = onClick, colors = ButtonDefaults.textButtonColors( contentColor = MaterialTheme.colorScheme.primary, - ) + ), + contentPadding = PaddingValues(start = 12.dp, top = 10.dp, end = 12.dp, bottom = 10.dp), ) { - LargeLabelText( - text = text, - modifier = Modifier.padding(vertical = 10.dp, horizontal = 12.dp), - ) + LargeLabelText(text = text) } } @@ -69,7 +67,7 @@ fun ToggleVisibilityButton(modifier: Modifier = Modifier, onToggle: (Boolean) -> contentDescription = if (toggleState.value) stringResource(R.string.content_description_show_password) else stringResource(R.string.content_description_hide_password), - tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant + tint = MaterialTheme.colorScheme.onSurfaceVariant, ) } }
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt index a622e07d8bb1..cc73089a96e7 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt @@ -16,11 +16,13 @@ package com.android.credentialmanager.common.ui -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme @@ -40,7 +42,8 @@ import androidx.compose.ui.unit.dp fun SheetContainerCard( topAppBar: (@Composable () -> Unit)? = null, modifier: Modifier = Modifier, - content: @Composable ColumnScope.() -> Unit, + contentVerticalArrangement: Arrangement.Vertical = Arrangement.Top, + content: LazyListScope.() -> Unit, ) { Card( modifier = modifier.fillMaxWidth().wrapContentHeight(), @@ -54,7 +57,7 @@ fun SheetContainerCard( if (topAppBar != null) { topAppBar() } - Column( + LazyColumn( modifier = Modifier.padding( start = 24.dp, end = 24.dp, @@ -63,6 +66,7 @@ fun SheetContainerCard( ).fillMaxWidth().wrapContentHeight(), horizontalAlignment = Alignment.CenterHorizontally, content = content, + verticalArrangement = contentVerticalArrangement, ) } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt index 8f48f6bf7b23..c09a692ce9fc 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt @@ -16,6 +16,7 @@ package com.android.credentialmanager.common.ui +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.FilledTonalButton @@ -33,11 +34,9 @@ fun ConfirmButton(text: String, onClick: () -> Unit) { colors = ButtonDefaults.filledTonalButtonColors( containerColor = MaterialTheme.colorScheme.primary, contentColor = MaterialTheme.colorScheme.onPrimary, - ) + ), + contentPadding = PaddingValues(start = 24.dp, top = 10.dp, end = 24.dp, bottom = 10.dp), ) { - LargeLabelText( - text = text, - modifier = Modifier.padding(vertical = 10.dp, horizontal = 24.dp), - ) + LargeLabelText(text = text) } }
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt index 0eaaf970f37f..bffa40e83f30 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt @@ -305,7 +305,7 @@ fun MoreOptionTopAppBar( }, navigationIcon = { IconButton( - modifier = Modifier.padding(top = 8.dp, bottom = 8.dp, start = 4.dp), + modifier = Modifier.padding(top = 8.dp, bottom = 8.dp, start = 4.dp).size(48.dp), onClick = onNavigationIconClicked ) { Box( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt index 8061da79a5ae..514ff90be8d7 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt @@ -75,7 +75,7 @@ fun Snackbar( action() } IconButton(onClick = onDismiss, modifier = Modifier.padding( - top = 18.dp, bottom = 18.dp, start = 16.dp, end = 24.dp, + top = 4.dp, bottom = 4.dp, start = 2.dp, end = 10.dp, )) { Icon( Icons.Filled.Close, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt index 8f7c37efe787..8af729ecdc25 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt @@ -16,7 +16,7 @@ package com.android.credentialmanager.common.ui -import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -32,7 +32,7 @@ import androidx.compose.ui.text.style.TextAlign @Composable fun HeadlineText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.onSurface, textAlign = TextAlign.Center, @@ -46,7 +46,7 @@ fun HeadlineText(text: String, modifier: Modifier = Modifier) { @Composable fun BodyMediumText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium, @@ -59,7 +59,7 @@ fun BodyMediumText(text: String, modifier: Modifier = Modifier) { @Composable fun BodySmallText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodySmall, @@ -72,7 +72,7 @@ fun BodySmallText(text: String, modifier: Modifier = Modifier) { @Composable fun LargeTitleText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.titleLarge, @@ -85,7 +85,7 @@ fun LargeTitleText(text: String, modifier: Modifier = Modifier) { @Composable fun SmallTitleText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.titleSmall, @@ -98,7 +98,7 @@ fun SmallTitleText(text: String, modifier: Modifier = Modifier) { @Composable fun SectionHeaderText(text: String, modifier: Modifier = Modifier, color: Color) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = color, style = MaterialTheme.typography.titleSmall, @@ -111,7 +111,7 @@ fun SectionHeaderText(text: String, modifier: Modifier = Modifier, color: Color) @Composable fun SnackbarContentText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.inverseOnSurface, style = MaterialTheme.typography.bodyMedium, @@ -124,7 +124,7 @@ fun SnackbarContentText(text: String, modifier: Modifier = Modifier) { @Composable fun SnackbarActionText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.inversePrimary, style = MaterialTheme.typography.labelLarge, @@ -137,7 +137,7 @@ fun SnackbarActionText(text: String, modifier: Modifier = Modifier) { @Composable fun LargeLabelTextOnSurfaceVariant(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, textAlign = TextAlign.Center, color = MaterialTheme.colorScheme.onSurfaceVariant, @@ -151,7 +151,7 @@ fun LargeLabelTextOnSurfaceVariant(text: String, modifier: Modifier = Modifier) @Composable fun LargeLabelText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, textAlign = TextAlign.Center, style = MaterialTheme.typography.labelLarge, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index 379b3e3adf30..00b7d0057746 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -8,11 +8,12 @@ import androidx.activity.result.IntentSenderRequest import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme @@ -23,7 +24,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap @@ -43,7 +43,6 @@ import com.android.credentialmanager.common.ui.ConfirmButton import com.android.credentialmanager.common.ui.CredentialContainerCard import com.android.credentialmanager.common.ui.CtaButtonRow import com.android.credentialmanager.common.ui.Entry -import com.android.credentialmanager.common.ui.EntryListColumn import com.android.credentialmanager.common.ui.HeadlineIcon import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant import com.android.credentialmanager.common.ui.ModalBottomSheet @@ -157,53 +156,67 @@ fun PasskeyIntroCard( onLearnMore: () -> Unit, ) { SheetContainerCard { - val onboardingImageResource = remember { - mutableStateOf(R.drawable.ic_passkeys_onboarding) + item { + val onboardingImageResource = remember { + mutableStateOf(R.drawable.ic_passkeys_onboarding) + } + if (isSystemInDarkTheme()) { + onboardingImageResource.value = R.drawable.ic_passkeys_onboarding_dark + } else { + onboardingImageResource.value = R.drawable.ic_passkeys_onboarding + } + Row( + modifier = Modifier.wrapContentHeight().fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + ) { + Image( + painter = painterResource(onboardingImageResource.value), + contentDescription = null, + modifier = Modifier.size(316.dp, 168.dp) + ) + } } - if (isSystemInDarkTheme()) { - onboardingImageResource.value = R.drawable.ic_passkeys_onboarding_dark - } else { - onboardingImageResource.value = R.drawable.ic_passkeys_onboarding + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { HeadlineText(text = stringResource(R.string.passkey_creation_intro_title)) } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + PasskeyBenefitRow( + leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_password), + text = stringResource(R.string.passkey_creation_intro_body_password), + ) } - Image( - painter = painterResource(onboardingImageResource.value), - contentDescription = null, - modifier = Modifier - .align(alignment = Alignment.CenterHorizontally).size(316.dp, 168.dp) - ) - Divider(thickness = 16.dp, color = Color.Transparent) - HeadlineText(text = stringResource(R.string.passkey_creation_intro_title)) - Divider(thickness = 16.dp, color = Color.Transparent) - PasskeyBenefitRow( - leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_password), - text = stringResource(R.string.passkey_creation_intro_body_password), - ) - Divider(thickness = 16.dp, color = Color.Transparent) - PasskeyBenefitRow( - leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_fingerprint), - text = stringResource(R.string.passkey_creation_intro_body_fingerprint), - ) - Divider(thickness = 16.dp, color = Color.Transparent) - PasskeyBenefitRow( - leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_device), - text = stringResource(R.string.passkey_creation_intro_body_device), - ) - Divider(thickness = 24.dp, color = Color.Transparent) + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + PasskeyBenefitRow( + leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_fingerprint), + text = stringResource(R.string.passkey_creation_intro_body_fingerprint), + ) + } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + PasskeyBenefitRow( + leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_device), + text = stringResource(R.string.passkey_creation_intro_body_device), + ) + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } - CtaButtonRow( - leftButton = { - ActionButton( - stringResource(R.string.string_learn_more), - onClick = onLearnMore - ) - }, - rightButton = { - ConfirmButton( - stringResource(R.string.string_continue), - onClick = onConfirm - ) - }, - ) + item { + CtaButtonRow( + leftButton = { + ActionButton( + stringResource(R.string.string_learn_more), + onClick = onLearnMore + ) + }, + rightButton = { + ConfirmButton( + stringResource(R.string.string_continue), + onClick = onConfirm + ) + }, + ) + } } } @@ -218,30 +231,32 @@ fun ProviderSelectionCard( onMoreOptionsSelected: () -> Unit, ) { SheetContainerCard { - HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()) - Divider(thickness = 16.dp, color = Color.Transparent) - HeadlineText( - text = stringResource( - R.string.choose_provider_title, - when (requestDisplayInfo.type) { - CredentialType.PASSKEY -> - stringResource(R.string.passkeys) - CredentialType.PASSWORD -> - stringResource(R.string.passwords) - CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info) - } + item { HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()) } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + HeadlineText( + text = stringResource( + R.string.choose_provider_title, + when (requestDisplayInfo.type) { + CredentialType.PASSKEY -> + stringResource(R.string.passkeys) + CredentialType.PASSWORD -> + stringResource(R.string.passwords) + CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info) + } + ) ) - ) - Divider(thickness = 24.dp, color = Color.Transparent) + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } - BodyMediumText(text = stringResource(R.string.choose_provider_body)) - Divider(thickness = 16.dp, color = Color.Transparent) - CredentialContainerCard { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(2.dp) - ) { - sortedCreateOptionsPairs.forEach { entry -> - item { + item { BodyMediumText(text = stringResource(R.string.choose_provider_body)) } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + CredentialContainerCard { + Column( + verticalArrangement = Arrangement.spacedBy(2.dp) + ) { + sortedCreateOptionsPairs.forEach { entry -> MoreOptionsInfoRow( requestDisplayInfo = requestDisplayInfo, providerInfo = entry.second, @@ -256,8 +271,6 @@ fun ProviderSelectionCard( } ) } - } - item { MoreOptionsDisabledProvidersRow( disabledProviders = disabledProviderList, onDisabledProvidersSelected = onDisabledProvidersSelected, @@ -266,15 +279,17 @@ fun ProviderSelectionCard( } } if (hasRemoteEntry) { - Divider(thickness = 24.dp, color = Color.Transparent) - CtaButtonRow( - leftButton = { - ActionButton( - stringResource(R.string.string_more_options), - onMoreOptionsSelected - ) - } - ) + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + CtaButtonRow( + leftButton = { + ActionButton( + stringResource(R.string.string_more_options), + onMoreOptionsSelected + ) + } + ) + } } } } @@ -310,14 +325,14 @@ fun MoreOptionsSelectionCard( else onBackCreationSelectionButtonSelected, ) }) { - Divider(thickness = 16.dp, color = Color.Transparent) - CredentialContainerCard { - EntryListColumn { - // Only in the flows with default provider(not first time use) we can show the - // createOptions here, or they will be shown on ProviderSelectionCard - if (hasDefaultProvider) { - sortedCreateOptionsPairs.forEach { entry -> - item { + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + CredentialContainerCard { + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + // Only in the flows with default provider(not first time use) we can show the + // createOptions here, or they will be shown on ProviderSelectionCard + if (hasDefaultProvider) { + sortedCreateOptionsPairs.forEach { entry -> MoreOptionsInfoRow( requestDisplayInfo = requestDisplayInfo, providerInfo = entry.second, @@ -329,26 +344,23 @@ fun MoreOptionsSelectionCard( entry.first ) ) - }) + } + ) } - } - item { MoreOptionsDisabledProvidersRow( disabledProviders = disabledProviderList, onDisabledProvidersSelected = onDisabledProvidersSelected, ) } - } - enabledProviderList.forEach { - if (it.remoteEntry != null) { - item { + enabledProviderList.forEach { + if (it.remoteEntry != null) { RemoteEntryRow( remoteInfo = it.remoteEntry!!, onRemoteEntrySelected = onRemoteEntrySelected, ) + return@forEach } - return@forEach } } } @@ -363,30 +375,34 @@ fun MoreOptionsRowIntroCard( onUseOnceSelected: () -> Unit, ) { SheetContainerCard { - HeadlineIcon(imageVector = Icons.Outlined.NewReleases) - Divider(thickness = 24.dp, color = Color.Transparent) - HeadlineText( - text = stringResource( - R.string.use_provider_for_all_title, - providerInfo.displayName - ) - ) - Divider(thickness = 24.dp, color = Color.Transparent) - BodyMediumText(text = stringResource(R.string.use_provider_for_all_description)) - CtaButtonRow( - leftButton = { - ActionButton( - stringResource(R.string.use_once), - onClick = onUseOnceSelected + item { HeadlineIcon(imageVector = Icons.Outlined.NewReleases) } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + HeadlineText( + text = stringResource( + R.string.use_provider_for_all_title, + providerInfo.displayName ) - }, - rightButton = { - ConfirmButton( - stringResource(R.string.set_as_default), - onClick = onChangeDefaultSelected - ) - }, - ) + ) + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { BodyMediumText(text = stringResource(R.string.use_provider_for_all_description)) } + item { + CtaButtonRow( + leftButton = { + ActionButton( + stringResource(R.string.use_once), + onClick = onUseOnceSelected + ) + }, + rightButton = { + ConfirmButton( + stringResource(R.string.set_as_default), + onClick = onChangeDefaultSelected + ) + }, + ) + } } } @@ -402,38 +418,44 @@ fun CreationSelectionCard( hasDefaultProvider: Boolean, ) { SheetContainerCard { - HeadlineIcon( - bitmap = providerInfo.icon.toBitmap().asImageBitmap(), - tint = Color.Unspecified, - ) - Divider(thickness = 4.dp, color = Color.Transparent) - LargeLabelTextOnSurfaceVariant(text = providerInfo.displayName) - Divider(thickness = 16.dp, color = Color.Transparent) - HeadlineText( - text = when (requestDisplayInfo.type) { - CredentialType.PASSKEY -> stringResource( - R.string.choose_create_option_passkey_title, - requestDisplayInfo.appName - ) - CredentialType.PASSWORD -> stringResource( - R.string.choose_create_option_password_title, - requestDisplayInfo.appName - ) - CredentialType.UNKNOWN -> stringResource( - R.string.choose_create_option_sign_in_title, - requestDisplayInfo.appName + item { + HeadlineIcon( + bitmap = providerInfo.icon.toBitmap().asImageBitmap(), + tint = Color.Unspecified, + ) + } + item { Divider(thickness = 4.dp, color = Color.Transparent) } + item { LargeLabelTextOnSurfaceVariant(text = providerInfo.displayName) } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + HeadlineText( + text = when (requestDisplayInfo.type) { + CredentialType.PASSKEY -> stringResource( + R.string.choose_create_option_passkey_title, + requestDisplayInfo.appName + ) + CredentialType.PASSWORD -> stringResource( + R.string.choose_create_option_password_title, + requestDisplayInfo.appName + ) + CredentialType.UNKNOWN -> stringResource( + R.string.choose_create_option_sign_in_title, + requestDisplayInfo.appName + ) + } + ) + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + CredentialContainerCard { + PrimaryCreateOptionRow( + requestDisplayInfo = requestDisplayInfo, + entryInfo = createOptionInfo, + onOptionSelected = onOptionSelected ) } - ) - Divider(thickness = 24.dp, color = Color.Transparent) - CredentialContainerCard { - PrimaryCreateOptionRow( - requestDisplayInfo = requestDisplayInfo, - entryInfo = createOptionInfo, - onOptionSelected = onOptionSelected - ) } - Divider(thickness = 24.dp, color = Color.Transparent) + item { Divider(thickness = 24.dp, color = Color.Transparent) } var createOptionsSize = 0 var remoteEntry: RemoteInfo? = null enabledProviderList.forEach { enabledProvider -> @@ -450,29 +472,33 @@ fun CreationSelectionCard( } else { createOptionsSize > 1 || remoteEntry != null } - CtaButtonRow( - leftButton = if (shouldShowMoreOptionsButton) { - { - ActionButton( - stringResource(R.string.string_more_options), - onMoreOptionsSelected + item { + CtaButtonRow( + leftButton = if (shouldShowMoreOptionsButton) { + { + ActionButton( + stringResource(R.string.string_more_options), + onMoreOptionsSelected + ) + } + } else null, + rightButton = { + ConfirmButton( + stringResource(R.string.string_continue), + onClick = onConfirm ) - } - } else null, - rightButton = { - ConfirmButton( - stringResource(R.string.string_continue), - onClick = onConfirm - ) - }, - ) - if (createOptionInfo.footerDescription != null) { - Divider( - thickness = 1.dp, - color = MaterialTheme.colorScheme.outlineVariant, - modifier = Modifier.padding(vertical = 16.dp) + }, ) - BodySmallText(text = createOptionInfo.footerDescription) + } + if (createOptionInfo.footerDescription != null) { + item { + Divider( + thickness = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant, + modifier = Modifier.padding(vertical = 16.dp) + ) + } + item { BodySmallText(text = createOptionInfo.footerDescription) } } } } @@ -485,29 +511,30 @@ fun ExternalOnlySelectionCard( onConfirm: () -> Unit, ) { SheetContainerCard { - HeadlineIcon(painter = painterResource(R.drawable.ic_other_devices)) - Divider(thickness = 16.dp, color = Color.Transparent) - HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title)) - Divider( - thickness = 24.dp, - color = Color.Transparent - ) - CredentialContainerCard { - PrimaryCreateOptionRow( - requestDisplayInfo = requestDisplayInfo, - entryInfo = activeRemoteEntry, - onOptionSelected = onOptionSelected + item { HeadlineIcon(painter = painterResource(R.drawable.ic_other_devices)) } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title)) } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + CredentialContainerCard { + PrimaryCreateOptionRow( + requestDisplayInfo = requestDisplayInfo, + entryInfo = activeRemoteEntry, + onOptionSelected = onOptionSelected + ) + } + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + CtaButtonRow( + rightButton = { + ConfirmButton( + stringResource(R.string.string_continue), + onClick = onConfirm + ) + }, ) } - Divider(thickness = 24.dp, color = Color.Transparent) - CtaButtonRow( - rightButton = { - ConfirmButton( - stringResource(R.string.string_continue), - onClick = onConfirm - ) - }, - ) } } @@ -515,40 +542,38 @@ fun ExternalOnlySelectionCard( fun MoreAboutPasskeysIntroCard( onBackPasskeyIntroButtonSelected: () -> Unit, ) { - SheetContainerCard(topAppBar = { - MoreOptionTopAppBar( - text = stringResource(R.string.more_about_passkeys_title), - onNavigationIconClicked = onBackPasskeyIntroButtonSelected, - ) - }) { - LazyColumn( - modifier = Modifier.fillMaxWidth().wrapContentHeight(), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - item { - MoreAboutPasskeySectionHeader( - text = stringResource(R.string.passwordless_technology_title) - ) - BodyMediumText(text = stringResource(R.string.passwordless_technology_detail)) - } - item { - MoreAboutPasskeySectionHeader( - text = stringResource(R.string.public_key_cryptography_title) - ) - BodyMediumText(text = stringResource(R.string.public_key_cryptography_detail)) - } - item { - MoreAboutPasskeySectionHeader( - text = stringResource(R.string.improved_account_security_title) - ) - BodyMediumText(text = stringResource(R.string.improved_account_security_detail)) - } - item { - MoreAboutPasskeySectionHeader( - text = stringResource(R.string.seamless_transition_title) - ) - BodyMediumText(text = stringResource(R.string.seamless_transition_detail)) - } + SheetContainerCard( + topAppBar = { + MoreOptionTopAppBar( + text = stringResource(R.string.more_about_passkeys_title), + onNavigationIconClicked = onBackPasskeyIntroButtonSelected, + ) + }, + contentVerticalArrangement = Arrangement.spacedBy(8.dp) + ) { + item { + MoreAboutPasskeySectionHeader( + text = stringResource(R.string.passwordless_technology_title) + ) + BodyMediumText(text = stringResource(R.string.passwordless_technology_detail)) + } + item { + MoreAboutPasskeySectionHeader( + text = stringResource(R.string.public_key_cryptography_title) + ) + BodyMediumText(text = stringResource(R.string.public_key_cryptography_detail)) + } + item { + MoreAboutPasskeySectionHeader( + text = stringResource(R.string.improved_account_security_title) + ) + BodyMediumText(text = stringResource(R.string.improved_account_security_detail)) + } + item { + MoreAboutPasskeySectionHeader( + text = stringResource(R.string.seamless_transition_title) + ) + BodyMediumText(text = stringResource(R.string.seamless_transition_detail)) } } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt index ce4e936446ff..192fa15714c6 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt @@ -38,7 +38,9 @@ data class CreateCredentialUiState( ) internal fun hasContentToDisplay(state: CreateCredentialUiState): Boolean { - return state.sortedCreateOptionsPairs.isNotEmpty() + return state.sortedCreateOptionsPairs.isNotEmpty() || + (!state.requestDisplayInfo.preferImmediatelyAvailableCredentials && + state.remoteEntry != null) } open class ProviderInfo( @@ -104,6 +106,7 @@ data class RequestDisplayInfo( val type: CredentialType, val appName: String, val typeIcon: Drawable, + val preferImmediatelyAvailableCredentials: Boolean, ) /** diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt index 54f8e5cea798..c5028c25c5da 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt @@ -22,10 +22,11 @@ import androidx.activity.result.ActivityResult import androidx.activity.result.IntentSenderRequest import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Divider import androidx.compose.material3.TextButton @@ -139,70 +140,72 @@ fun PrimarySelectionCard( providerDisplayInfo.sortedUserNameToCredentialEntryList val authenticationEntryList = providerDisplayInfo.authenticationEntryList SheetContainerCard { - HeadlineText( - text = stringResource( - if (sortedUserNameToCredentialEntryList - .size == 1 && authenticationEntryList.isEmpty() - ) { - if (sortedUserNameToCredentialEntryList.first() - .sortedCredentialEntryList.first().credentialType - == CredentialType.PASSKEY - ) R.string.get_dialog_title_use_passkey_for - else R.string.get_dialog_title_use_sign_in_for - } else if ( - sortedUserNameToCredentialEntryList - .isEmpty() && authenticationEntryList.size == 1 - ) { - R.string.get_dialog_title_use_sign_in_for - } else R.string.get_dialog_title_choose_sign_in_for, - requestDisplayInfo.appName - ), - ) - Divider(thickness = 24.dp, color = Color.Transparent) - CredentialContainerCard { - val usernameForCredentialSize = sortedUserNameToCredentialEntryList.size - val authenticationEntrySize = authenticationEntryList.size - LazyColumn( - verticalArrangement = Arrangement.spacedBy(2.dp) - ) { - // Show max 4 entries in this primary page - if (usernameForCredentialSize + authenticationEntrySize <= 4) { - items(sortedUserNameToCredentialEntryList) { - CredentialEntryRow( - credentialEntryInfo = it.sortedCredentialEntryList.first(), - onEntrySelected = onEntrySelected, - ) - } - items(authenticationEntryList) { - AuthenticationEntryRow( - authenticationEntryInfo = it, - onEntrySelected = onEntrySelected, - ) - } - } else if (usernameForCredentialSize < 4) { - items(sortedUserNameToCredentialEntryList) { - CredentialEntryRow( - credentialEntryInfo = it.sortedCredentialEntryList.first(), - onEntrySelected = onEntrySelected, - ) - } - items(authenticationEntryList.take(4 - usernameForCredentialSize)) { - AuthenticationEntryRow( - authenticationEntryInfo = it, - onEntrySelected = onEntrySelected, - ) - } - } else { - items(sortedUserNameToCredentialEntryList.take(4)) { - CredentialEntryRow( - credentialEntryInfo = it.sortedCredentialEntryList.first(), - onEntrySelected = onEntrySelected, - ) + item { + HeadlineText( + text = stringResource( + if (sortedUserNameToCredentialEntryList + .size == 1 && authenticationEntryList.isEmpty() + ) { + if (sortedUserNameToCredentialEntryList.first() + .sortedCredentialEntryList.first().credentialType + == CredentialType.PASSKEY + ) R.string.get_dialog_title_use_passkey_for + else R.string.get_dialog_title_use_sign_in_for + } else if ( + sortedUserNameToCredentialEntryList + .isEmpty() && authenticationEntryList.size == 1 + ) { + R.string.get_dialog_title_use_sign_in_for + } else R.string.get_dialog_title_choose_sign_in_for, + requestDisplayInfo.appName + ), + ) + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + CredentialContainerCard { + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + val usernameForCredentialSize = sortedUserNameToCredentialEntryList.size + val authenticationEntrySize = authenticationEntryList.size + // Show max 4 entries in this primary page + if (usernameForCredentialSize + authenticationEntrySize <= 4) { + sortedUserNameToCredentialEntryList.forEach { + CredentialEntryRow( + credentialEntryInfo = it.sortedCredentialEntryList.first(), + onEntrySelected = onEntrySelected, + ) + } + authenticationEntryList.forEach { + AuthenticationEntryRow( + authenticationEntryInfo = it, + onEntrySelected = onEntrySelected, + ) + } + } else if (usernameForCredentialSize < 4) { + sortedUserNameToCredentialEntryList.forEach { + CredentialEntryRow( + credentialEntryInfo = it.sortedCredentialEntryList.first(), + onEntrySelected = onEntrySelected, + ) + } + authenticationEntryList.take(4 - usernameForCredentialSize).forEach { + AuthenticationEntryRow( + authenticationEntryInfo = it, + onEntrySelected = onEntrySelected, + ) + } + } else { + sortedUserNameToCredentialEntryList.take(4).forEach { + CredentialEntryRow( + credentialEntryInfo = it.sortedCredentialEntryList.first(), + onEntrySelected = onEntrySelected, + ) + } } } } } - Divider(thickness = 24.dp, color = Color.Transparent) + item { Divider(thickness = 24.dp, color = Color.Transparent) } var totalEntriesCount = sortedUserNameToCredentialEntryList .flatMap { it.sortedCredentialEntryList }.size + authenticationEntryList .size + providerInfoList.flatMap { it.actionEntryList }.size @@ -210,24 +213,26 @@ fun PrimarySelectionCard( // Row horizontalArrangement differs on only one actionButton(should place on most // left)/only one confirmButton(should place on most right)/two buttons exist the same // time(should be one on the left, one on the right) - CtaButtonRow( - leftButton = if (totalEntriesCount > 1) { - { - ActionButton( - stringResource(R.string.get_dialog_use_saved_passkey_for), - onMoreOptionSelected - ) - } - } else null, - rightButton = if (activeEntry != null) { // Only one sign-in options exist - { - ConfirmButton( - stringResource(R.string.string_continue), - onClick = onConfirm - ) - } - } else null, - ) + item { + CtaButtonRow( + leftButton = if (totalEntriesCount > 1) { + { + ActionButton( + stringResource(R.string.get_dialog_use_saved_passkey_for), + onMoreOptionSelected + ) + } + } else null, + rightButton = if (activeEntry != null) { // Only one sign-in options exist + { + ConfirmButton( + stringResource(R.string.string_continue), + onClick = onConfirm + ) + } + } else null, + ) + } } } @@ -250,48 +255,46 @@ fun AllSignInOptionCard( onNavigationIconClicked = if (isNoAccount) onCancel else onBackButtonClicked, ) }) { - LazyColumn { - // For username - items(sortedUserNameToCredentialEntryList) { item -> - PerUserNameCredentials( - perUserNameCredentialEntryList = item, - onEntrySelected = onEntrySelected, - ) - } - // Locked password manager - if (authenticationEntryList.isNotEmpty()) { - item { - LockedCredentials( - authenticationEntryList = authenticationEntryList, - onEntrySelected = onEntrySelected, - ) - } - } - // From another device - val remoteEntry = providerDisplayInfo.remoteEntry - if (remoteEntry != null) { - item { - RemoteEntryCard( - remoteEntry = remoteEntry, - onEntrySelected = onEntrySelected, - ) - } - } + // For username + items(sortedUserNameToCredentialEntryList) { item -> + PerUserNameCredentials( + perUserNameCredentialEntryList = item, + onEntrySelected = onEntrySelected, + ) + } + // Locked password manager + if (authenticationEntryList.isNotEmpty()) { item { - Divider( - thickness = 1.dp, - color = Color.LightGray, - modifier = Modifier.padding(top = 16.dp) + LockedCredentials( + authenticationEntryList = authenticationEntryList, + onEntrySelected = onEntrySelected, ) } - // Manage sign-ins (action chips) + } + // From another device + val remoteEntry = providerDisplayInfo.remoteEntry + if (remoteEntry != null) { item { - ActionChips( - providerInfoList = providerInfoList, - onEntrySelected = onEntrySelected + RemoteEntryCard( + remoteEntry = remoteEntry, + onEntrySelected = onEntrySelected, ) } } + item { + Divider( + thickness = 1.dp, + color = Color.LightGray, + modifier = Modifier.padding(top = 16.dp) + ) + } + // Manage sign-ins (action chips) + item { + ActionChips( + providerInfoList = providerInfoList, + onEntrySelected = onEntrySelected + ) + } } } @@ -454,13 +457,13 @@ fun RemoteCredentialSnackBarScreen( Snackbar( action = { TextButton( - modifier = Modifier.padding(top = 12.dp, bottom = 12.dp, start = 16.dp), + modifier = Modifier.padding(top = 4.dp, bottom = 4.dp, start = 16.dp) + .heightIn(min = 32.dp), onClick = { onClick(true) }, + contentPadding = + PaddingValues(start = 0.dp, top = 6.dp, end = 0.dp, bottom = 6.dp), ) { - SnackbarActionText( - text = stringResource(R.string.snackbar_action), - Modifier.padding(vertical = 6.dp) - ) + SnackbarActionText(text = stringResource(R.string.snackbar_action)) } }, onDismiss = onCancel, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt index d1f0f8146be3..9727d3f39c4a 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt @@ -37,7 +37,8 @@ data class GetCredentialUiState( internal fun hasContentToDisplay(state: GetCredentialUiState): Boolean { return state.providerDisplayInfo.sortedUserNameToCredentialEntryList.isNotEmpty() || state.providerDisplayInfo.authenticationEntryList.isNotEmpty() || - state.providerDisplayInfo.remoteEntry != null + (state.providerDisplayInfo.remoteEntry != null && + !state.requestDisplayInfo.preferImmediatelyAvailableCredentials) } data class ProviderInfo( @@ -146,6 +147,7 @@ class ActionEntryInfo( data class RequestDisplayInfo( val appName: String, + val preferImmediatelyAvailableCredentials: Boolean, ) /** @@ -246,7 +248,6 @@ private fun toActiveEntry( private fun toGetScreenState( providerDisplayInfo: ProviderDisplayInfo ): GetScreenState { - return if (providerDisplayInfo.sortedUserNameToCredentialEntryList.isEmpty() && providerDisplayInfo.remoteEntry == null && providerDisplayInfo.authenticationEntryList.all { it.isUnlockedAndEmpty }) diff --git a/packages/DynamicSystemInstallationService/AndroidManifest.xml b/packages/DynamicSystemInstallationService/AndroidManifest.xml index b194738c67b6..c2aaeace1af6 100644 --- a/packages/DynamicSystemInstallationService/AndroidManifest.xml +++ b/packages/DynamicSystemInstallationService/AndroidManifest.xml @@ -21,12 +21,7 @@ android:exported="true" android:permission="android.permission.INSTALL_DYNAMIC_SYSTEM" android:foregroundServiceType="systemExempted" - android:process=":dynsystem"> - <intent-filter> - <action android:name="android.os.image.action.NOTIFY_IF_IN_USE" /> - <category android:name="android.intent.category.DEFAULT" /> - </intent-filter> - </service> + android:process=":dynsystem" /> <activity android:name=".VerificationActivity" android:exported="true" diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index af20a4bd5707..750c156f95bd 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -70,7 +70,7 @@ android_library { "src/**/*.kt", ], - min_sdk_version: "29", + min_sdk_version: "30", } diff --git a/packages/SettingsLib/Spa/.idea/codeStyles/Project.xml b/packages/SettingsLib/Spa/.idea/codeStyles/Project.xml index 940f5c936e5b..78a740892f13 100644 --- a/packages/SettingsLib/Spa/.idea/codeStyles/Project.xml +++ b/packages/SettingsLib/Spa/.idea/codeStyles/Project.xml @@ -1,17 +1,12 @@ <component name="ProjectCodeStyleConfiguration"> <code_scheme name="Project" version="173"> <JetCodeStyleSettings> - <option name="PACKAGES_TO_USE_STAR_IMPORTS"> - <value /> - </option> <option name="PACKAGES_IMPORT_LAYOUT"> <value> <package name="" alias="false" withSubpackages="true" /> <package name="" alias="true" withSubpackages="true" /> </value> </option> - <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" /> - <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> </JetCodeStyleSettings> <XML> diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle index 42af9992f774..643af759347d 100644 --- a/packages/SettingsLib/Spa/build.gradle +++ b/packages/SettingsLib/Spa/build.gradle @@ -24,7 +24,7 @@ buildscript { } } plugins { - id 'com.android.application' version '7.3.1' apply false - id 'com.android.library' version '7.3.1' apply false + id 'com.android.application' version '8.0.0-beta01' apply false + id 'com.android.library' version '8.0.0-beta01' apply false id 'org.jetbrains.kotlin.android' version '1.8.0' apply false } diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties index bd7e7ff38205..53b24b0e5d75 100644 --- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties +++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties @@ -16,7 +16,7 @@ #Thu Jul 14 10:36:06 CST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-rc-1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt index 724588f794b4..3fdb1d14e667 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt @@ -77,9 +77,8 @@ data class SettingsPage( return true } - fun isEnabled(): Boolean { - return getPageProvider(sppName)?.isEnabled(arguments) ?: false - } + fun isEnabled(): Boolean = + SpaEnvironment.IS_DEBUG || getPageProvider(sppName)?.isEnabled(arguments) ?: false fun getTitle(): String { return getPageProvider(sppName)?.getTitle(arguments) ?: "" diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt index 02962a5815a2..2d956d5eddb2 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt @@ -88,4 +88,13 @@ abstract class SpaEnvironment(context: Context) { open val sliceProviderAuthorities: String? = null // TODO: add other environment setup here. + companion object { + /** + * Whether debug mode is on or off. + * + * If set to true, this will also enable all the pages under development (allows browsing + * and searching). + */ + const val IS_DEBUG = false + } } diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml index 1907b3e51d68..28f7c50a732a 100644 --- a/packages/SettingsLib/res/values-af/arrays.xml +++ b/packages/SettingsLib/res/values-af/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Het gefiltreer geaktiveer"</item> <item msgid="2779123106632690576">"Geaktiveer"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Opskrifte is gefiltreer"</item> - <item msgid="4818549483446395865">"A2DP-mediapakkette is gefiltreer"</item> - <item msgid="8207123990453243311">"RFCOMM-kanaal is gefiltreer"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Gedeaktiveer"</item> - <item msgid="5884245882825346396">"Wonder"</item> - <item msgid="6569400572915342949">"Opskrif"</item> - <item msgid="1239386221416967664">"Volledige filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (verstek)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 31f50e8fac58..3de581ea3940 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -522,7 +522,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Wekkers en onthounotas"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Laat toe dat wekkers en onthounotas gestel word"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wekkers en onthounotas"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Laat hierdie program toe om wekkers te stel en tydsensitiewe handelinge te skeduleer. Dit laat die program op die agtergrond werk, wat meer batterykrag kan gebruik.\n\nAs hierdie toestemming af is, sal bestaande wekkers en tydgegronde geleenthede wat deur hierdie program geskeduleer is, nie werk nie."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Laat hierdie app toe om wekkers te stel en tydsensitiewe handelinge te skeduleer. Dit laat die app op die agtergrond werk, wat meer batterykrag kan gebruik.\n\nAs hierdie toestemming af is, sal bestaande wekkers en tydgegronde geleenthede wat deur hierdie app geskeduleer is, nie werk nie."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"skedule, wekker, onthounota, horlosie"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Skakel aan"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Skakel Moenie steur nie aan"</string> diff --git a/packages/SettingsLib/res/values-am/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml index ad0f1edffdbb..c423d3c86ed1 100644 --- a/packages/SettingsLib/res/values-am/arrays.xml +++ b/packages/SettingsLib/res/values-am/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"ማጣሪያን አንቃ"</item> <item msgid="2779123106632690576">"ነቅቷል"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"ራስጌዎች ተጣርተዋል"</item> - <item msgid="4818549483446395865">"የA2DP ሚዲያ ፓኬቶች ተጣርተዋል"</item> - <item msgid="8207123990453243311">"የRFCOMM ሰርጥ ተጣርቷል"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"ተሰናክሏል"</item> - <item msgid="5884245882825346396">"ማጂክ"</item> - <item msgid="6569400572915342949">"የራስጌ ጽሑፍ"</item> - <item msgid="1239386221416967664">"ሙሉ ማጣሪያ"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ነባሪ)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml index cede18e807ce..cf46a8fdcc7d 100644 --- a/packages/SettingsLib/res/values-ar/arrays.xml +++ b/packages/SettingsLib/res/values-ar/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"تمّ تفعيل التصفية"</item> <item msgid="2779123106632690576">"مفعّل"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"العناوين المفلترة"</item> - <item msgid="4818549483446395865">"حُزم وسائط A2DP مفلترة"</item> - <item msgid="8207123990453243311">"قناة بروتوكول RFCOMM مفلترة"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"حُزم البيانات غير المفعّلة"</item> - <item msgid="5884245882825346396">"سِحري"</item> - <item msgid="6569400572915342949">"العنوان"</item> - <item msgid="1239386221416967664">"فلترة كاملة"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (تلقائي)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml index c52d27e20e1b..284ca122edef 100644 --- a/packages/SettingsLib/res/values-as/arrays.xml +++ b/packages/SettingsLib/res/values-as/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"সক্ষম কৰাবিলাক ফিল্টাৰ কৰা হৈছে"</item> <item msgid="2779123106632690576">"সক্ষম কৰা আছে"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"হেডাৰ ফিল্টাৰ কৰা হৈছে"</item> - <item msgid="4818549483446395865">"A2DP মিডিয়াৰ পেকেট ফিল্টাৰ কৰা হৈছে"</item> - <item msgid="8207123990453243311">"RFCOMM চেনেল ফিল্টাৰ কৰা হৈছে"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"অক্ষম কৰা আছে"</item> - <item msgid="5884245882825346396">"যাদু"</item> - <item msgid="6569400572915342949">"হেডাৰ"</item> - <item msgid="1239386221416967664">"সম্পূৰ্ণ ফিল্টাৰ"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ডিফ’ল্ট)"</item> <item msgid="1637054408779685086">"AVRCP ১.৩"</item> diff --git a/packages/SettingsLib/res/values-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml index 9605c512aea3..ff0054bfe810 100644 --- a/packages/SettingsLib/res/values-az/arrays.xml +++ b/packages/SettingsLib/res/values-az/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filtrləmə aktivdir"</item> <item msgid="2779123106632690576">"Aktivdir"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Başlıqlar Filtrlənib"</item> - <item msgid="4818549483446395865">"A2DP Media Paketləri Filtrlənib"</item> - <item msgid="8207123990453243311">"RFCOMM Kanalı Filtrlənib"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Deaktiv"</item> - <item msgid="5884245882825346396">"Sehr"</item> - <item msgid="6569400572915342949">"Başlıq"</item> - <item msgid="1239386221416967664">"Tam Filtr"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Defolt)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml index 618748b7afb9..32071e541bc7 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Omogućeno filtrirano"</item> <item msgid="2779123106632690576">"Omogućeno"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Filtrirana zaglavlja"</item> - <item msgid="4818549483446395865">"Filtrirani A2DP medijski paketi"</item> - <item msgid="8207123990453243311">"Filtrirani RFCOMM kanal"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Onemogućeno"</item> - <item msgid="5884245882825346396">"Magija"</item> - <item msgid="6569400572915342949">"Zaglavlje"</item> - <item msgid="1239386221416967664">"Potpuni filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (podrazumevano)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-be/arrays.xml b/packages/SettingsLib/res/values-be/arrays.xml index 117f0b479c0e..a60d35419cf6 100644 --- a/packages/SettingsLib/res/values-be/arrays.xml +++ b/packages/SettingsLib/res/values-be/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Уключана з фільтрацыяй"</item> <item msgid="2779123106632690576">"Уключана"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Адфільтраваны загалоўкі"</item> - <item msgid="4818549483446395865">"Адфільтраваны пакеты мультымедыя A2DP"</item> - <item msgid="8207123990453243311">"Адфільтраваны канал RFCOMM"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Адключана"</item> - <item msgid="5884245882825346396">"Спецыяльная фраза"</item> - <item msgid="6569400572915342949">"Загаловак"</item> - <item msgid="1239386221416967664">"Поўная фільтрацыя"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (стандартная)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml index cf643c815327..d778ca275d81 100644 --- a/packages/SettingsLib/res/values-bg/arrays.xml +++ b/packages/SettingsLib/res/values-bg/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Филтрирането е активирано"</item> <item msgid="2779123106632690576">"Активирано"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Заглавките са филтрирани"</item> - <item msgid="4818549483446395865">"Мултимедийните пакети A2DP са филтрирани"</item> - <item msgid="8207123990453243311">"Каналът RFCOMM е филтриран"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Деактивирано"</item> - <item msgid="5884245882825346396">"Магия"</item> - <item msgid="6569400572915342949">"Заглавка"</item> - <item msgid="1239386221416967664">"Пълен филтър"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (основно)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml index 1a7652d60d5a..dbb738c3d47e 100644 --- a/packages/SettingsLib/res/values-bn/arrays.xml +++ b/packages/SettingsLib/res/values-bn/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"ফিল্টার করা চালু আছে"</item> <item msgid="2779123106632690576">"চালু করা আছে"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"হেডার ফিল্টার করা হয়েছে"</item> - <item msgid="4818549483446395865">"A2DP মিডিয়া প্যাকেট ফিল্টার করা হয়েছে"</item> - <item msgid="8207123990453243311">"RFCOMM চ্যানেল ফিল্টার করা হয়েছে"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"বন্ধ করা আছে"</item> - <item msgid="5884245882825346396">"ম্যাজিক"</item> - <item msgid="6569400572915342949">"হেডার"</item> - <item msgid="1239386221416967664">"সম্পূর্ণ ফিল্টার"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ডিফল্ট)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml index 2ab1b79eb7db..740e70478fc3 100644 --- a/packages/SettingsLib/res/values-bs/arrays.xml +++ b/packages/SettingsLib/res/values-bs/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Omogućeno filtrirano"</item> <item msgid="2779123106632690576">"Omogućeno"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Filtrirana zaglavlja"</item> - <item msgid="4818549483446395865">"Filtrirani A2DP medijski paketi"</item> - <item msgid="8207123990453243311">"Filtrirani RFCOMM kanal"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Onemogućeno"</item> - <item msgid="5884245882825346396">"Magija"</item> - <item msgid="6569400572915342949">"Zaglavlje"</item> - <item msgid="1239386221416967664">"Potpuni filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (zadano)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index e8cabfaf7ae5..1aa8ff38b2fa 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -522,7 +522,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmi i podsjetnici"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Dozvoli postavljanje alarma i podsjetnika"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi i podsjetnici"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Dozvolite ovoj aplikaciji da postavlja alarme i zakazuje vremenski osjetljive radnje. Ovim će se omogućiti aplikaciji da radi u pozadini, čime se može povećati potrošnja baterije.\n\nAko je ovo odobrenje isključeno, postojeći alarmi i događaji zasnovani na vremenu koje je ova aplikacija zakazala neće funkcionirati."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Dozvolite ovoj aplikaciji da postavlja alarme i zakazuje vremenski osjetljive radnje. Ovim će se omogućiti aplikaciji da radi u pozadini, čime se može povećati potrošnja baterije.\n\nAko je ovo odobrenje isključeno, postojeći alarmi i događaji zasnovani na vremenu, a koje je ova aplikacija zakazala, neće funkcionirati."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"raspored, alarm, podsjetnik, sat"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Uključi"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Uključi način rada Ne ometaj"</string> diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml index 00a126cee4e4..4e437ba2d4fe 100644 --- a/packages/SettingsLib/res/values-ca/arrays.xml +++ b/packages/SettingsLib/res/values-ca/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Activat amb filtres"</item> <item msgid="2779123106632690576">"Activat"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Capçaleres filtrades"</item> - <item msgid="4818549483446395865">"Paquets multimèdia A2DP filtrats"</item> - <item msgid="8207123990453243311">"Canal RFCOMM filtrat"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Desactivat"</item> - <item msgid="5884245882825346396">"Màgia"</item> - <item msgid="6569400572915342949">"Capçalera"</item> - <item msgid="1239386221416967664">"Filtre complet"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (predeterminada)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 83b32d4d492e..f14befdcd123 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -522,7 +522,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmes i recordatoris"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Permet la configuració d\'alarmes i recordatoris"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes i recordatoris"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permet que aquesta aplicació configuri alarmes i programi accions. Això permet a l\'aplicació executar-se en segon pla i, per tant, és possible que consumeixi més bateria.\n\nSi aquest permís està desactivat, les alarmes i els esdeveniments que ja hagi programat l\'aplicació no funcionaran."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permet que aquesta aplicació configuri alarmes i programi accions a una hora determinada. Això permet a l\'aplicació executar-se en segon pla i, per tant, és possible que consumeixi més bateria.\n\nSi aquest permís està desactivat, les alarmes i els esdeveniments que ja hagi programat l\'aplicació no funcionaran."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programació, alarma, recordatori, rellotge"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activa"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activa el mode No molestis"</string> diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml index a4bd21f503d0..e1a5aefc34b5 100644 --- a/packages/SettingsLib/res/values-cs/arrays.xml +++ b/packages/SettingsLib/res/values-cs/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Povolit filtrované"</item> <item msgid="2779123106632690576">"Zapnuto"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Záhlaví filtrována"</item> - <item msgid="4818549483446395865">"Mediální balíčky A2DP filtrovány"</item> - <item msgid="8207123990453243311">"Kanál RFCOMM filtrován"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Zakázáno"</item> - <item msgid="5884245882825346396">"Komplexní hodnocení"</item> - <item msgid="6569400572915342949">"Záhlaví"</item> - <item msgid="1239386221416967664">"Úplný filtr"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (výchozí)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml index 37da365d339b..03cab2062bd5 100644 --- a/packages/SettingsLib/res/values-da/arrays.xml +++ b/packages/SettingsLib/res/values-da/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filtreret er aktiveret"</item> <item msgid="2779123106632690576">"Aktiveret"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Overskrifter filtreret"</item> - <item msgid="4818549483446395865">"A2DP-mediepakker filtreret"</item> - <item msgid="8207123990453243311">"RFCOMM-kanal filtreret"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Deaktiveret"</item> - <item msgid="5884245882825346396">"Magi"</item> - <item msgid="6569400572915342949">"Overskrift"</item> - <item msgid="1239386221416967664">"Fuldt filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (standard)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml index 2241666fb1ef..c5dcc10b0ccb 100644 --- a/packages/SettingsLib/res/values-de/arrays.xml +++ b/packages/SettingsLib/res/values-de/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filter aktiviert"</item> <item msgid="2779123106632690576">"Aktiviert"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Header gefiltert"</item> - <item msgid="4818549483446395865">"A2DP-Medienpakete gefiltert"</item> - <item msgid="8207123990453243311">"RFCOMM-Kanal gefiltert"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Deaktiviert"</item> - <item msgid="5884245882825346396">"Magie"</item> - <item msgid="6569400572915342949">"Header"</item> - <item msgid="1239386221416967664">"Vollständig gefiltert"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Standard)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-el/arrays.xml b/packages/SettingsLib/res/values-el/arrays.xml index 1a824c1b89d3..6486b3d113c2 100644 --- a/packages/SettingsLib/res/values-el/arrays.xml +++ b/packages/SettingsLib/res/values-el/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Ενεργοποιήθηκε το φιλτράρισμα"</item> <item msgid="2779123106632690576">"Ενεργοποιήθηκε"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Φιλτραρισμένες κεφαλίδες"</item> - <item msgid="4818549483446395865">"Φιλτραρισμένα πακέτα μέσων A2DP"</item> - <item msgid="8207123990453243311">"Φιλτραρισμένο κανάλι RFCOMM"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Ανενεργό"</item> - <item msgid="5884245882825346396">"Μαγεία"</item> - <item msgid="6569400572915342949">"Κεφαλίδα"</item> - <item msgid="1239386221416967664">"Πλήρες φίλτρο"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Προεπιλογή)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml index de633860626c..9a7390e5882e 100644 --- a/packages/SettingsLib/res/values-en-rAU/arrays.xml +++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Enabled Filtered"</item> <item msgid="2779123106632690576">"Enabled"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Headers filtered"</item> - <item msgid="4818549483446395865">"A2DP media packets filtered"</item> - <item msgid="8207123990453243311">"RFCOMM channel filtered"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Disabled"</item> - <item msgid="5884245882825346396">"Magic"</item> - <item msgid="6569400572915342949">"Header"</item> - <item msgid="1239386221416967664">"Full filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Default)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-en-rCA/arrays.xml b/packages/SettingsLib/res/values-en-rCA/arrays.xml index 0af8b4efc216..184d21005cc1 100644 --- a/packages/SettingsLib/res/values-en-rCA/arrays.xml +++ b/packages/SettingsLib/res/values-en-rCA/arrays.xml @@ -64,15 +64,15 @@ <item msgid="2779123106632690576">"Enabled"</item> </string-array> <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Headers Filtered"</item> - <item msgid="4818549483446395865">"A2DP Media Packets Filtered"</item> - <item msgid="8207123990453243311">"RFCOMM Channel Filtered"</item> + <item msgid="195768089203590086">"Leave only ACL headers"</item> + <item msgid="2776218217644557831">"Filter A2DP media packets"</item> + <item msgid="8163235976612675092">"Filter RFCOMM channel"</item> </string-array> <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Disabled"</item> - <item msgid="5884245882825346396">"Magic"</item> - <item msgid="6569400572915342949">"Header"</item> - <item msgid="1239386221416967664">"Full Filter"</item> + <item msgid="3961868665260627524">"Disable"</item> + <item msgid="2505973306504851132">"Fill with string of characters"</item> + <item msgid="5883011000629613855">"Leave only header"</item> + <item msgid="1051534112762023603">"Fully remove"</item> </string-array> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Default)"</item> diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml index de633860626c..9a7390e5882e 100644 --- a/packages/SettingsLib/res/values-en-rGB/arrays.xml +++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Enabled Filtered"</item> <item msgid="2779123106632690576">"Enabled"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Headers filtered"</item> - <item msgid="4818549483446395865">"A2DP media packets filtered"</item> - <item msgid="8207123990453243311">"RFCOMM channel filtered"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Disabled"</item> - <item msgid="5884245882825346396">"Magic"</item> - <item msgid="6569400572915342949">"Header"</item> - <item msgid="1239386221416967664">"Full filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Default)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml index de633860626c..9a7390e5882e 100644 --- a/packages/SettingsLib/res/values-en-rIN/arrays.xml +++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Enabled Filtered"</item> <item msgid="2779123106632690576">"Enabled"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Headers filtered"</item> - <item msgid="4818549483446395865">"A2DP media packets filtered"</item> - <item msgid="8207123990453243311">"RFCOMM channel filtered"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Disabled"</item> - <item msgid="5884245882825346396">"Magic"</item> - <item msgid="6569400572915342949">"Header"</item> - <item msgid="1239386221416967664">"Full filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Default)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-en-rXC/arrays.xml b/packages/SettingsLib/res/values-en-rXC/arrays.xml index 30c9a4968997..dec70f456d1e 100644 --- a/packages/SettingsLib/res/values-en-rXC/arrays.xml +++ b/packages/SettingsLib/res/values-en-rXC/arrays.xml @@ -64,15 +64,15 @@ <item msgid="2779123106632690576">"Enabled"</item> </string-array> <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Headers Filtered"</item> - <item msgid="4818549483446395865">"A2DP Media Packets Filtered"</item> - <item msgid="8207123990453243311">"RFCOMM Channel Filtered"</item> + <item msgid="195768089203590086">"Leave only ACL headers"</item> + <item msgid="2776218217644557831">"Filter A2DP media packets"</item> + <item msgid="8163235976612675092">"Filter RFCOMM channel"</item> </string-array> <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Disabled"</item> - <item msgid="5884245882825346396">"Magic"</item> - <item msgid="6569400572915342949">"Header"</item> - <item msgid="1239386221416967664">"Full Filter"</item> + <item msgid="3961868665260627524">"Disable"</item> + <item msgid="2505973306504851132">"Fill with string of characters"</item> + <item msgid="5883011000629613855">"Leave only header"</item> + <item msgid="1051534112762023603">"Fully remove"</item> </string-array> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Default)"</item> diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml index e2a36c6fa255..771690dc8309 100644 --- a/packages/SettingsLib/res/values-es-rUS/arrays.xml +++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filtrado habilitado"</item> <item msgid="2779123106632690576">"Habilitado"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Títulos filtrados"</item> - <item msgid="4818549483446395865">"Paquetes de medios A2DP filtrados"</item> - <item msgid="8207123990453243311">"Canal RFCOMM filtrado"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Inhabilitada"</item> - <item msgid="5884245882825346396">"Automático"</item> - <item msgid="6569400572915342949">"Título"</item> - <item msgid="1239386221416967664">"Filtro completo"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (predeterminado)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml index b5e035c9cd2f..e07f9dc372f7 100644 --- a/packages/SettingsLib/res/values-es/arrays.xml +++ b/packages/SettingsLib/res/values-es/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Habilitado con filtros"</item> <item msgid="2779123106632690576">"Habilitado"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Encabezados filtrados"</item> - <item msgid="4818549483446395865">"Paquetes de medios A2DP filtrados"</item> - <item msgid="8207123990453243311">"Canal RFCOMM filtrado"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Inhabilitado"</item> - <item msgid="5884245882825346396">"Mágico"</item> - <item msgid="6569400572915342949">"Encabezado"</item> - <item msgid="1239386221416967664">"Filtro completo"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (predeterminado)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-et/arrays.xml b/packages/SettingsLib/res/values-et/arrays.xml index d910a0ed58c9..34448a72525e 100644 --- a/packages/SettingsLib/res/values-et/arrays.xml +++ b/packages/SettingsLib/res/values-et/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Luba filtreeritud"</item> <item msgid="2779123106632690576">"Lubatud"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Filtreeritud päised"</item> - <item msgid="4818549483446395865">"Filtreeritud A2DP meediapaketid"</item> - <item msgid="8207123990453243311">"Filtreeritud RFCOMM-kanal"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Keelatud"</item> - <item msgid="5884245882825346396">"Maagiline"</item> - <item msgid="6569400572915342949">"Päis"</item> - <item msgid="1239386221416967664">"Täielik filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (vaikeseade)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml index 2d3575b0f828..eb678ffc03e3 100644 --- a/packages/SettingsLib/res/values-eu/arrays.xml +++ b/packages/SettingsLib/res/values-eu/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Gaituta baina iragazita"</item> <item msgid="2779123106632690576">"Gaituta"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Goiburuan iragazita"</item> - <item msgid="4818549483446395865">"A2DP multimedia-paketeak iragazita"</item> - <item msgid="8207123990453243311">"RFCOMM kanala iragazita"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Desgaituta"</item> - <item msgid="5884245882825346396">"Magia"</item> - <item msgid="6569400572915342949">"Goiburua"</item> - <item msgid="1239386221416967664">"Iragazki osoa"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (lehenetsia)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 82e0eea0167f..02ab1920731d 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -522,7 +522,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmak eta abisuak"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Eman alarmak eta abisuak ezartzeko baimena"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmak eta abisuak"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Eman alarmak ezartzeko eta denbora-muga duten ekintzak programatzeko baimena aplikazioari. Hala, aplikazioak atzeko planoan funtzionatuko du, eta litekeena da bateria gehiago kontsumitzea.\n\nEz baduzu ematen baimen hori, ez dute funtzionatuko aplikazio honen bidez programatutako alarmek eta denbora-muga duten ekintzek."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Eman alarmak ezartzeko eta denbora-muga duten ekintzak programatzeko baimena aplikazioari. Hala, aplikazioak atzeko planoan funtzionatuko du, eta litekeena da bateria gehiago kontsumitzea.\n\nBaimen hori ematen ez baduzu, ez dute funtzionatuko aplikazio honen bidez programatutako alarmek eta denbora-muga duten ekintzek."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programazioa, alarma, abisua, erlojua"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktibatu"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktibatu ez molestatzeko modua"</string> diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml index b983bcc51bdf..2d9be3177a16 100644 --- a/packages/SettingsLib/res/values-fa/arrays.xml +++ b/packages/SettingsLib/res/values-fa/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"فیلترشده فعال شده است"</item> <item msgid="2779123106632690576">"فعال"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"سرصفحهها فیلتر شد"</item> - <item msgid="4818549483446395865">"بستههای رسانه A2DP فیلتر شد"</item> - <item msgid="8207123990453243311">"کانال RFCOMM فیلتر شد"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"غیرفعال"</item> - <item msgid="5884245882825346396">"جادویی"</item> - <item msgid="6569400572915342949">"سرصفحه"</item> - <item msgid="1239386221416967664">"فیلتر کامل"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP نسخه ۱.۵ (پیشفرض)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml index 9e3bcd238ae9..d6f002f5b539 100644 --- a/packages/SettingsLib/res/values-fi/arrays.xml +++ b/packages/SettingsLib/res/values-fi/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Suodatus käytössä"</item> <item msgid="2779123106632690576">"Päällä"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Otsikot suodatettu"</item> - <item msgid="4818549483446395865">"A2DP-mediapaketit suodatettu"</item> - <item msgid="8207123990453243311">"RFCOMM-kanava suodatettu"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Ei käytössä"</item> - <item msgid="5884245882825346396">"Magic"</item> - <item msgid="6569400572915342949">"Otsikko"</item> - <item msgid="1239386221416967664">"Täysi suodatus"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (oletus)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml index 86066b6a6714..6657aa121159 100644 --- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml +++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filtres activés"</item> <item msgid="2779123106632690576">"Activé"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"En-têtes filtrés"</item> - <item msgid="4818549483446395865">"Paquets multimédias A2DP filtrés"</item> - <item msgid="8207123990453243311">"Canal RFCOMM filtré"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Désactivé"</item> - <item msgid="5884245882825346396">"Magie"</item> - <item msgid="6569400572915342949">"En-tête"</item> - <item msgid="1239386221416967664">"Filtre intégral"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (par défaut)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-fr/arrays.xml b/packages/SettingsLib/res/values-fr/arrays.xml index 5f31ff5e8dcd..869d88ad5599 100644 --- a/packages/SettingsLib/res/values-fr/arrays.xml +++ b/packages/SettingsLib/res/values-fr/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Activé et filtré"</item> <item msgid="2779123106632690576">"Activé"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"En-têtes filtrés"</item> - <item msgid="4818549483446395865">"Paquets multimédias A2DP filtrés"</item> - <item msgid="8207123990453243311">"Canal RFCOMM filtré"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Désactivé"</item> - <item msgid="5884245882825346396">"Magie"</item> - <item msgid="6569400572915342949">"En-tête"</item> - <item msgid="1239386221416967664">"Filtre complet"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (par défaut)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml index 665851db60e8..3cd7b4b4c4a0 100644 --- a/packages/SettingsLib/res/values-gl/arrays.xml +++ b/packages/SettingsLib/res/values-gl/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Está activado o filtrado"</item> <item msgid="2779123106632690576">"Activada"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Cabeceiras filtradas"</item> - <item msgid="4818549483446395865">"A2DP Media Packets filtrados"</item> - <item msgid="8207123990453243311">"Canle RFCOMM filtrada"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Desactivado"</item> - <item msgid="5884245882825346396">"Maxia"</item> - <item msgid="6569400572915342949">"Cabeceira"</item> - <item msgid="1239386221416967664">"Filtro completo"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (predeterminado)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml index e3739521b9d2..f559b805af4c 100644 --- a/packages/SettingsLib/res/values-gu/arrays.xml +++ b/packages/SettingsLib/res/values-gu/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"ફિલ્ટર કરેલ ચાલુ છે"</item> <item msgid="2779123106632690576">"ચાલુ છે"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"ફિલ્ટર કરેલા હેડર"</item> - <item msgid="4818549483446395865">"ફિલ્ટર કરેલા A2DP મીડિયા પૅકેટ"</item> - <item msgid="8207123990453243311">"ફિલ્ટર કરેલી RFCOMM ચૅનલ"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"બંધ કર્યું"</item> - <item msgid="5884245882825346396">"જાદુ"</item> - <item msgid="6569400572915342949">"હેડર"</item> - <item msgid="1239386221416967664">"સંપૂર્ણ ફિલ્ટર"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ડિફૉલ્ટ)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml index 356a13c3320a..be8862096de8 100644 --- a/packages/SettingsLib/res/values-hi/arrays.xml +++ b/packages/SettingsLib/res/values-hi/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"चालू और फ़िल्टर किया गया"</item> <item msgid="2779123106632690576">"चालू है"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"हेडर फ़िल्टर किए गए"</item> - <item msgid="4818549483446395865">"A2DP मीडिया पैकेट फ़िल्टर किए गए"</item> - <item msgid="8207123990453243311">"RFCOMM चैनल फ़िल्टर किए गए"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"बंद है"</item> - <item msgid="5884245882825346396">"मैजिक"</item> - <item msgid="6569400572915342949">"हेडर"</item> - <item msgid="1239386221416967664">"सभी फ़िल्टर"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (डिफ़ॉल्ट)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-hr/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml index 812f8d270d33..5c73ebba1402 100644 --- a/packages/SettingsLib/res/values-hr/arrays.xml +++ b/packages/SettingsLib/res/values-hr/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Omogućeno filtrirano"</item> <item msgid="2779123106632690576">"Omogućeno"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Filtrirana zaglavlja"</item> - <item msgid="4818549483446395865">"Filtrirani A2DP medijski paketi"</item> - <item msgid="8207123990453243311">"Filtrirani RFCOMM kanal"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Onemogućeno"</item> - <item msgid="5884245882825346396">"Čarolija"</item> - <item msgid="6569400572915342949">"Zaglavlje"</item> - <item msgid="1239386221416967664">"Potpuni filtar"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (zadano)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml index 9c842f997a10..500b9fd228f3 100644 --- a/packages/SettingsLib/res/values-hu/arrays.xml +++ b/packages/SettingsLib/res/values-hu/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Szűrtek engedélyezve"</item> <item msgid="2779123106632690576">"Engedélyezve"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Szűrt fejlécek"</item> - <item msgid="4818549483446395865">"Szűrt A2DP-médiacsomagok"</item> - <item msgid="8207123990453243311">"Szűrt RFCOMM-csatorna"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Kikapcsolva"</item> - <item msgid="5884245882825346396">"Mágia"</item> - <item msgid="6569400572915342949">"Fejléc"</item> - <item msgid="1239386221416967664">"Teljes szűrő"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (alapértelmezett)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml index 59351c4520e6..6fd6893b83e6 100644 --- a/packages/SettingsLib/res/values-hy/arrays.xml +++ b/packages/SettingsLib/res/values-hy/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Միացված է զտիչներով"</item> <item msgid="2779123106632690576">"Միացված է"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Զտված էջագլուխներ"</item> - <item msgid="4818549483446395865">"Զտված A2DP մուլտիմեդիա փաթեթներ"</item> - <item msgid="8207123990453243311">"Զտված RFCOMM կապուղի"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Անջատված է"</item> - <item msgid="5884245882825346396">"Կախարդանք"</item> - <item msgid="6569400572915342949">"Էջագլուխ"</item> - <item msgid="1239386221416967664">"Ամբողջական զտիչ"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (կանխադրված)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml index 06e24b904844..8257d0ee983a 100644 --- a/packages/SettingsLib/res/values-in/arrays.xml +++ b/packages/SettingsLib/res/values-in/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Diaktifkan Difilter"</item> <item msgid="2779123106632690576">"Diaktifkan"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Header Difilter"</item> - <item msgid="4818549483446395865">"Paket Media A2DP Difilter"</item> - <item msgid="8207123990453243311">"Saluran RFCOMM Difilter"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Nonaktif"</item> - <item msgid="5884245882825346396">"Magic"</item> - <item msgid="6569400572915342949">"Header"</item> - <item msgid="1239386221416967664">"Filter Lengkap"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Default)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml index a87e760a800e..1b114eeaff93 100644 --- a/packages/SettingsLib/res/values-is/arrays.xml +++ b/packages/SettingsLib/res/values-is/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Kveikt á síuðu"</item> <item msgid="2779123106632690576">"Kveikt"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Síaðar fyrirsagnir"</item> - <item msgid="4818549483446395865">"Síaðir A2DP-efnispakkar"</item> - <item msgid="8207123990453243311">"Síuð RFCOMM-rás"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Slökkt"</item> - <item msgid="5884245882825346396">"Töfrar"</item> - <item msgid="6569400572915342949">"Haus"</item> - <item msgid="1239386221416967664">"Heildarsía"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (sjálfgefið)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml index 18923b6899c4..50eca9304321 100644 --- a/packages/SettingsLib/res/values-it/arrays.xml +++ b/packages/SettingsLib/res/values-it/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filtro attivo"</item> <item msgid="2779123106632690576">"Attiva"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Intestazioni filtrate"</item> - <item msgid="4818549483446395865">"Pacchetti multimediali A2DP filtrati"</item> - <item msgid="8207123990453243311">"Canale RFCOMM filtrato"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Disattivato"</item> - <item msgid="5884245882825346396">"Magia"</item> - <item msgid="6569400572915342949">"Intestazione"</item> - <item msgid="1239386221416967664">"Filtro completo"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (versione predefinita)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml index 8e76f2e32e32..02b77516ed3b 100644 --- a/packages/SettingsLib/res/values-iw/arrays.xml +++ b/packages/SettingsLib/res/values-iw/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"המסננים המופעלים"</item> <item msgid="2779123106632690576">"מופעל"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"כותרות לאחר סינון"</item> - <item msgid="4818549483446395865">"מנות מדיה A2DP לאחר סינון"</item> - <item msgid="8207123990453243311">"ערוצי RFCOMM לאחר סינון"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"בהשבתה"</item> - <item msgid="5884245882825346396">"קסם"</item> - <item msgid="6569400572915342949">"כותרת"</item> - <item msgid="1239386221416967664">"סינון מלא"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ברירת המחדל)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml index 47517fef6d60..869fd9970065 100644 --- a/packages/SettingsLib/res/values-ja/arrays.xml +++ b/packages/SettingsLib/res/values-ja/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"有効(フィルタ済み)"</item> <item msgid="2779123106632690576">"有効"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"ヘッダーのフィルタ"</item> - <item msgid="4818549483446395865">"A2DP メディア パケットのフィルタ"</item> - <item msgid="8207123990453243311">"RFCOMM チャネルのフィルタ"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"無効"</item> - <item msgid="5884245882825346396">"マジック"</item> - <item msgid="6569400572915342949">"ヘッダー"</item> - <item msgid="1239386221416967664">"フルフィルタ"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5(デフォルト)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ka/arrays.xml b/packages/SettingsLib/res/values-ka/arrays.xml index eceaac76c86c..71a283c9a848 100644 --- a/packages/SettingsLib/res/values-ka/arrays.xml +++ b/packages/SettingsLib/res/values-ka/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"გაფილტრულის ჩართვა"</item> <item msgid="2779123106632690576">"ჩართულია"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"გაფილტრული სათაურები"</item> - <item msgid="4818549483446395865">"გაფილტრული A2DP მედია პაკეტები"</item> - <item msgid="8207123990453243311">"გაფილტრული RFCOMM არხი"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"გათიშული"</item> - <item msgid="5884245882825346396">"მაგია"</item> - <item msgid="6569400572915342949">"სათაური"</item> - <item msgid="1239386221416967664">"სრული ფილტრი"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ნაგულისხმევი)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml index 517b1168037f..ab5e10763b7b 100644 --- a/packages/SettingsLib/res/values-kk/arrays.xml +++ b/packages/SettingsLib/res/values-kk/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Сүзгіленгендері қосулы"</item> <item msgid="2779123106632690576">"Қосулы"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Сүзілген тақырыптар"</item> - <item msgid="4818549483446395865">"Сүзілген A2DP медиапакеттері"</item> - <item msgid="8207123990453243311">"Сүзілген RFCOMM арнасы"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Өшірулі"</item> - <item msgid="5884245882825346396">"Сиқыр"</item> - <item msgid="6569400572915342949">"Тақырып"</item> - <item msgid="1239386221416967664">"Толық сүзгі"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (әдепкі)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-km/arrays.xml b/packages/SettingsLib/res/values-km/arrays.xml index db993294a8a1..bfc983457f5a 100644 --- a/packages/SettingsLib/res/values-km/arrays.xml +++ b/packages/SettingsLib/res/values-km/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"បានបើកការត្រង"</item> <item msgid="2779123106632690576">"បានបើក"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"បានត្រងក្បាលទំព័រ"</item> - <item msgid="4818549483446395865">"បានត្រងកញ្ចប់មេឌៀ A2DP"</item> - <item msgid="8207123990453243311">"បានត្រងបណ្ដាញ RFCOMM"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"បានបិទ"</item> - <item msgid="5884245882825346396">"វេទមន្ត"</item> - <item msgid="6569400572915342949">"ក្បាលទំព័រ"</item> - <item msgid="1239386221416967664">"តម្រងពេញ"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (លំនាំដើម)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml index 2bf4ef011a8e..61e27912dda8 100644 --- a/packages/SettingsLib/res/values-kn/arrays.xml +++ b/packages/SettingsLib/res/values-kn/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"ಫಿಲ್ಟರ್ ಮಾಡುವುದನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</item> <item msgid="2779123106632690576">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"ಶಿರೋಲೇಖಗಳನ್ನು ಫಿಲ್ಟರ್ ಮಾಡಲಾಗಿದೆ"</item> - <item msgid="4818549483446395865">"A2DP ಮೀಡಿಯಾ ಪ್ಯಾಕೆಟ್ಗಳನ್ನು ಫಿಲ್ಟರ್ ಮಾಡಲಾಗಿದೆ"</item> - <item msgid="8207123990453243311">"RFCOMM ಚಾನಲ್ ಫಿಲ್ಟರ್ ಮಾಡಲಾಗಿದೆ"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</item> - <item msgid="5884245882825346396">"ಮ್ಯಾಜಿಕ್"</item> - <item msgid="6569400572915342949">"ಶಿರೋಲೇಖ"</item> - <item msgid="1239386221416967664">"ಸಂಪೂರ್ಣ ಫಿಲ್ಟರ್"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ಡೀಫಾಲ್ಟ್)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml index 1a1469f862fa..b4a035ae2751 100644 --- a/packages/SettingsLib/res/values-ko/arrays.xml +++ b/packages/SettingsLib/res/values-ko/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"필터링 사용 설정됨"</item> <item msgid="2779123106632690576">"사용 설정됨"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"헤더 필터링됨"</item> - <item msgid="4818549483446395865">"A2DP 미디어 패킷 필터링됨"</item> - <item msgid="8207123990453243311">"RFCOMM 채널 필터링됨"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"사용 안함"</item> - <item msgid="5884245882825346396">"매직"</item> - <item msgid="6569400572915342949">"헤더"</item> - <item msgid="1239386221416967664">"전체 필터"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5(기본값)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml index f59d4d4ec2f5..657e63ea33b5 100644 --- a/packages/SettingsLib/res/values-ky/arrays.xml +++ b/packages/SettingsLib/res/values-ky/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Чыпкалар иштетилди"</item> <item msgid="2779123106632690576">"Иштетилди"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Жогорку колонтитулдар чыпкаланды"</item> - <item msgid="4818549483446395865">"A2DP медиа топтомдор чыпкаланды"</item> - <item msgid="8207123990453243311">"RFCOMM каналы чыпкаланды"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Өчүрүлдү"</item> - <item msgid="5884245882825346396">"Сыйкырдуу"</item> - <item msgid="6569400572915342949">"Жогорку колонтитул"</item> - <item msgid="1239386221416967664">"Толук чыпка"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Демейки)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-lo/arrays.xml b/packages/SettingsLib/res/values-lo/arrays.xml index 4981273e7712..11688d4dfb04 100644 --- a/packages/SettingsLib/res/values-lo/arrays.xml +++ b/packages/SettingsLib/res/values-lo/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"ເປີດການກັ່ນຕອງແລ້ວ"</item> <item msgid="2779123106632690576">"ເປີດໃຊ້ແລ້ວ"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"ສ່ວນຫົວຖືກກັ່ນຕອງ"</item> - <item msgid="4818549483446395865">"ແພັກເກດສື່ A2DP ຖືກກັ່ນຕອງ"</item> - <item msgid="8207123990453243311">"ຊ່ອງ RFCOMM ຖືກກັ່ນຕອງ"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"ປິດການນຳໃຊ້ແລ້ວ"</item> - <item msgid="5884245882825346396">"ເວດມົນ"</item> - <item msgid="6569400572915342949">"ສ່ວນຫົວ"</item> - <item msgid="1239386221416967664">"ການກັ່ນຕອງແບບເຕັມ"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ຄ່າເລີ່ມຕົ້ນ)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml index 15e65e85f250..6718660be334 100644 --- a/packages/SettingsLib/res/values-lt/arrays.xml +++ b/packages/SettingsLib/res/values-lt/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Įgalinta filtruota"</item> <item msgid="2779123106632690576">"Įgalinta"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Antraštės išfiltruotos"</item> - <item msgid="4818549483446395865">"A2DP medijos paketai išfiltruoti"</item> - <item msgid="8207123990453243311">"RFCOMM kanalas išfiltruotas"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Išjungta"</item> - <item msgid="5884245882825346396">"Magiškoji eilutė"</item> - <item msgid="6569400572915342949">"Antraštė"</item> - <item msgid="1239386221416967664">"Visi filtrai"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (numatytoji)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml index 47531a00b503..3e1869abd3de 100644 --- a/packages/SettingsLib/res/values-lv/arrays.xml +++ b/packages/SettingsLib/res/values-lv/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Iespējot filtrētos"</item> <item msgid="2779123106632690576">"Iespējots"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Filtrētas galvenes"</item> - <item msgid="4818549483446395865">"Filtrētas A2DP multivides paketes"</item> - <item msgid="8207123990453243311">"Filtrēts RFCOMM kanāls"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Atspējots"</item> - <item msgid="5884245882825346396">"Pielāgotās virknes"</item> - <item msgid="6569400572915342949">"Galvene"</item> - <item msgid="1239386221416967664">"Visi filtri"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (noklusējums)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml index 9d5a5e1d3e2a..ebf387badec7 100644 --- a/packages/SettingsLib/res/values-mk/arrays.xml +++ b/packages/SettingsLib/res/values-mk/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Овозможено е филтрирано"</item> <item msgid="2779123106632690576">"Овозможено"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Насловите се филтрирани"</item> - <item msgid="4818549483446395865">"A2DP аудиовизуелните пакети се филтрирани"</item> - <item msgid="8207123990453243311">"RFCOMM-каналот е филтриран"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Оневозможено"</item> - <item msgid="5884245882825346396">"Магија"</item> - <item msgid="6569400572915342949">"Заглавие"</item> - <item msgid="1239386221416967664">"Целосен филтер"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Стандардна)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml index f28be85d4cb1..30adb57365aa 100644 --- a/packages/SettingsLib/res/values-ml/arrays.xml +++ b/packages/SettingsLib/res/values-ml/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"ഫിൽട്ടറിംഗ് പ്രവർത്തനക്ഷമമാക്കി"</item> <item msgid="2779123106632690576">"പ്രവർത്തനക്ഷമമാക്കി"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"ഹെഡ്ഡറുകൾ ഫിൽട്ടർ ചെയ്തു"</item> - <item msgid="4818549483446395865">"A2DP മീഡിയാ പാക്കറ്റകൾ ഫിൽട്ടർ ചെയ്തു"</item> - <item msgid="8207123990453243311">"RFCOMM ചാനൽ ഫിൽട്ടർ ചെയ്തു"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"പ്രവർത്തനരഹിതമാക്കി"</item> - <item msgid="5884245882825346396">"മാജിക്"</item> - <item msgid="6569400572915342949">"ഹെഡ്ഡർ"</item> - <item msgid="1239386221416967664">"പൂർണ്ണ ഫിൽട്ടർ"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ഡിഫോൾട്ട്)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml index d3c344eb8a9e..d03fcd651586 100644 --- a/packages/SettingsLib/res/values-mn/arrays.xml +++ b/packages/SettingsLib/res/values-mn/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Идэвхжүүлсэн Шүүсэн"</item> <item msgid="2779123106632690576">"Идэвхжүүлсэн"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Толгой хэсгийг шүүсэн"</item> - <item msgid="4818549483446395865">"A2DP Медиа пакетыг шүүсэн"</item> - <item msgid="8207123990453243311">"RFCOMM сувгийг шүүсэн"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Идэвхгүй болгосон"</item> - <item msgid="5884245882825346396">"Ид шид"</item> - <item msgid="6569400572915342949">"Толгой хэсэг"</item> - <item msgid="1239386221416967664">"Бүтэн шүүлтүүр"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Өгөгдмөл)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml index 093e36baa99b..68eff756451f 100644 --- a/packages/SettingsLib/res/values-mr/arrays.xml +++ b/packages/SettingsLib/res/values-mr/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"फिल्टर केलेले सुरू केले"</item> <item msgid="2779123106632690576">"सुरू केले"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"फिल्टर केलेले हेडर"</item> - <item msgid="4818549483446395865">"फिल्टर केलेली A2DP मीडिया पॅकेट"</item> - <item msgid="8207123990453243311">"फिल्टर केलेली RFCOMM चॅनल"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"बंद केले"</item> - <item msgid="5884245882825346396">"मॅजिक"</item> - <item msgid="6569400572915342949">"हेडर"</item> - <item msgid="1239386221416967664">"पूर्ण फिल्टर"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (डीफॉल्ट)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ms/arrays.xml b/packages/SettingsLib/res/values-ms/arrays.xml index 1daa57982d97..efdd879638cb 100644 --- a/packages/SettingsLib/res/values-ms/arrays.xml +++ b/packages/SettingsLib/res/values-ms/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Didayakan Ditapis"</item> <item msgid="2779123106632690576">"Didayakan"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Pengepala Ditapis"</item> - <item msgid="4818549483446395865">"Paket Media A2DP Ditapis"</item> - <item msgid="8207123990453243311">"Saluran RFCOMM Ditapis"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Dilumpuhkan"</item> - <item msgid="5884245882825346396">"Magik"</item> - <item msgid="6569400572915342949">"Pengepala"</item> - <item msgid="1239386221416967664">"Penapis Penuh"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Lalai)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-my/arrays.xml b/packages/SettingsLib/res/values-my/arrays.xml index dbca2993c781..4d0b792a8532 100644 --- a/packages/SettingsLib/res/values-my/arrays.xml +++ b/packages/SettingsLib/res/values-my/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"စစ်ထုတ်ထားသည်များကို ဖွင့်ထားသည်"</item> <item msgid="2779123106632690576">"ဖွင့်ထားသည်"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"စစ်ထုတ်ထားသော ခေါင်းစီးများ"</item> - <item msgid="4818549483446395865">"စစ်ထုတ်ထားသော A2DP မီဒီယာအတွဲများ"</item> - <item msgid="8207123990453243311">"စစ်ထုတ်ထားသော RFCOMM ချန်နယ်"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"ပိတ်ထားသည်"</item> - <item msgid="5884245882825346396">"ပဉ္စလက်"</item> - <item msgid="6569400572915342949">"ခေါင်းစီး"</item> - <item msgid="1239386221416967664">"စစ်ထုတ်မှုအပြည့်"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (မူလ)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml index 35c4cdd9fa41..9293bdadb02d 100644 --- a/packages/SettingsLib/res/values-nb/arrays.xml +++ b/packages/SettingsLib/res/values-nb/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filtrering er slått på"</item> <item msgid="2779123106632690576">"Slått på"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Filtrert topptekst"</item> - <item msgid="4818549483446395865">"Filtrerte A2DP-mediepakker"</item> - <item msgid="8207123990453243311">"Filtrert RFCOMM-kanal"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Slått av"</item> - <item msgid="5884245882825346396">"Magi"</item> - <item msgid="6569400572915342949">"Topptekst"</item> - <item msgid="1239386221416967664">"Fullt filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (standard)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml index 76652cd8ea6e..29a7c6017510 100644 --- a/packages/SettingsLib/res/values-ne/arrays.xml +++ b/packages/SettingsLib/res/values-ne/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"फिल्टर सक्षम पारियो"</item> <item msgid="2779123106632690576">"सक्षम पारिएको छ"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"हेडरहरू फिल्टर गरिए"</item> - <item msgid="4818549483446395865">"A2DP मिडिया प्याकेटहरू फिल्टर गरिए"</item> - <item msgid="8207123990453243311">"RFCOMM च्यानल फिल्टर गरियो"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"अफ गरियो"</item> - <item msgid="5884245882825346396">"म्याजिक"</item> - <item msgid="6569400572915342949">"हेडर"</item> - <item msgid="1239386221416967664">"फुल फिल्टर"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP १.५ (डिफल्ट)"</item> <item msgid="1637054408779685086">"AVRCP १.३"</item> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index ae7e8b3ec4d3..831ed2f24082 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -520,7 +520,7 @@ <string name="okay" msgid="949938843324579502">"ठिक छ"</string> <string name="done" msgid="381184316122520313">"सम्पन्न भयो"</string> <string name="alarms_and_reminders_label" msgid="6918395649731424294">"अलार्म र रिमाइन्डरहरू"</string> - <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"अलार्म तथा रिमाइन्डर सेट गर्न दिइयोस्"</string> + <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"अलार्म तथा रिमाइन्डर सेट गर्ने अनुमति दिनुहोस्"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म तथा रिमाइन्डर"</string> <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"यो एपलाई अलार्म सेट गर्ने र समयमै पूरा गर्नु पर्ने कारबाहीहरूको रुटिन बनाउने अनुमति दिनुहोस्। यो अनुमति दिइएको छ भने यो एप ब्याकग्राउन्डमा चल्छ र धेरै ब्याट्री खपत हुन्छ।\n\nयो अनुमति दिइएको छैन भने सेट गरिएका अलार्म बज्दैनन् र यो एपले तय गरेका गतिविधि चल्दैनन्।"</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"समयतालिका, अलार्म, रिमाइन्डर, घडी"</string> diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml index 2ecfbd844492..460302cfb29d 100644 --- a/packages/SettingsLib/res/values-nl/arrays.xml +++ b/packages/SettingsLib/res/values-nl/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Gefilterd staat aan"</item> <item msgid="2779123106632690576">"Aangezet"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Headers gefilterd"</item> - <item msgid="4818549483446395865">"A2DP-mediapakketten gefilterd"</item> - <item msgid="8207123990453243311">"RFCOMM-kanaal gefilterd"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Uitgezet"</item> - <item msgid="5884245882825346396">"Magie"</item> - <item msgid="6569400572915342949">"Header"</item> - <item msgid="1239386221416967664">"Volledig filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (standaard)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-or/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml index 5f483445afa0..439bd725004b 100644 --- a/packages/SettingsLib/res/values-or/arrays.xml +++ b/packages/SettingsLib/res/values-or/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"ଫିଲ୍ଟର୍କୁ ସକ୍ଷମ କରାଯାଇଛି"</item> <item msgid="2779123106632690576">"ସକ୍ଷମ କରାଯାଇଛି"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"ହେଡରଗୁଡ଼ିକୁ ଫିଲ୍ଟର କରାଯାଇଛି"</item> - <item msgid="4818549483446395865">"A2DP ମିଡିଆ ପେକେଟ ଫିଲ୍ଟର କରାଯାଇଛି"</item> - <item msgid="8207123990453243311">"RFCOMM ଚେନେଲ ଫିଲ୍ଟର କରାଯାଇଛି"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"ଅକ୍ଷମ କରାଯାଇଛି"</item> - <item msgid="5884245882825346396">"ମେଜିକ"</item> - <item msgid="6569400572915342949">"ହେଡର"</item> - <item msgid="1239386221416967664">"ସମ୍ପୂର୍ଣ୍ଣ ଫିଲ୍ଟର"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ଡିଫଲ୍ଟ)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml index d8b1c7e5937d..f4bfcd1f1861 100644 --- a/packages/SettingsLib/res/values-pa/arrays.xml +++ b/packages/SettingsLib/res/values-pa/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"ਫਿਲਟਰ ਕੀਤਿਆਂ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</item> <item msgid="2779123106632690576">"ਚਾਲੂ"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"ਸਿਰਲੇਖ ਫਿਲਟਰ ਕੀਤੇ ਗਏ"</item> - <item msgid="4818549483446395865">"A2DP ਮੀਡੀਆ ਪੈਕੇਟ ਫਿਲਟਰ ਕੀਤੇ ਗਏ"</item> - <item msgid="8207123990453243311">"RFCOMM ਚੈਨਲ ਫਿਲਟਰ ਕੀਤੇ ਗਏ"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"ਬੰਦ ਹੈ"</item> - <item msgid="5884245882825346396">"ਜਾਦੂ"</item> - <item msgid="6569400572915342949">"ਸਿਰਲੇਖ"</item> - <item msgid="1239386221416967664">"ਸਭ ਫਿਲਟਰ"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml index 9659acb0f88f..a305a65c16cd 100644 --- a/packages/SettingsLib/res/values-pl/arrays.xml +++ b/packages/SettingsLib/res/values-pl/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filtrowanie włączone"</item> <item msgid="2779123106632690576">"Włączono"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Przefiltrowane nagłówki"</item> - <item msgid="4818549483446395865">"Przefiltrowane pakiety multimediów A2DP"</item> - <item msgid="8207123990453243311">"Przefiltrowany kanał RFCOMM"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Wyłączono"</item> - <item msgid="5884245882825346396">"Magia"</item> - <item msgid="6569400572915342949">"Nagłówek"</item> - <item msgid="1239386221416967664">"Pełny filtr"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (domyślnie)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 84aa66aa3963..ddff92c78b26 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -522,7 +522,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmy i przypomnienia"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Zezwalaj na ustawianie alarmów i przypomnień"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmy i przypomnienia"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Zezwól na ustawianie alarmów i planowanie innych działań, w przypadku których czas jest istotny. Dzięki temu aplikacja będzie mogła działać w tle, co może zwiększyć wykorzystanie baterii.\n\nJeśli nie włączysz tych uprawnień, istniejące alarmy i zaplanowane wydarzenia z tej aplikacji nie będą działać."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Zezwalaj tej aplikacji na ustawianie alarmów i planowanie działań, w przypadku których czas jest istotny. Aplikacja będzie mogła działać w tle, co może zwiększyć wykorzystanie baterii.\n\nJeśli nie włączysz tego uprawnienia, istniejące alarmy i zaplanowane wydarzenia z tej aplikacji nie będą działać."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"harmonogram, alarm, przypomnienie, zegar"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Włącz"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Włącz tryb Nie przeszkadzać"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml index 0bff6c781687..eff092216c0a 100644 --- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filtro ativado"</item> <item msgid="2779123106632690576">"Ativado"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Cabeçalhos filtrados"</item> - <item msgid="4818549483446395865">"Pacotes de mídia A2DP filtrados"</item> - <item msgid="8207123990453243311">"Canal RFCOMM filtrado"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Desativado"</item> - <item msgid="5884245882825346396">"Mágica"</item> - <item msgid="6569400572915342949">"Cabeçalho"</item> - <item msgid="1239386221416967664">"Filtro completo"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (padrão)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 168207d518df..3a48c3d2b6a5 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -522,7 +522,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmes e lembretes"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Autorizar a definição de alarmes e lembretes"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes e lembretes"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permitir que o app defina alarmes e programe ações mais imediatas. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permitir que o app defina alarmes e programe ações com hora marcada. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarme, lembrete, relógio"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ativar"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ativar o Não perturbe"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml index b134be6d9ead..0553aac032e8 100644 --- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filtrado ativado"</item> <item msgid="2779123106632690576">"Ativado"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Cabeçalhos filtrados"</item> - <item msgid="4818549483446395865">"Pacotes de multimédia A2DP filtrados"</item> - <item msgid="8207123990453243311">"Canal RFCOMM filtrado"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Desativado"</item> - <item msgid="5884245882825346396">"Magia"</item> - <item msgid="6569400572915342949">"Cabeçalho"</item> - <item msgid="1239386221416967664">"Filtro completo"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (predefinição)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml index 0bff6c781687..eff092216c0a 100644 --- a/packages/SettingsLib/res/values-pt/arrays.xml +++ b/packages/SettingsLib/res/values-pt/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filtro ativado"</item> <item msgid="2779123106632690576">"Ativado"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Cabeçalhos filtrados"</item> - <item msgid="4818549483446395865">"Pacotes de mídia A2DP filtrados"</item> - <item msgid="8207123990453243311">"Canal RFCOMM filtrado"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Desativado"</item> - <item msgid="5884245882825346396">"Mágica"</item> - <item msgid="6569400572915342949">"Cabeçalho"</item> - <item msgid="1239386221416967664">"Filtro completo"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (padrão)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 168207d518df..3a48c3d2b6a5 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -522,7 +522,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmes e lembretes"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Autorizar a definição de alarmes e lembretes"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes e lembretes"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permitir que o app defina alarmes e programe ações mais imediatas. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permitir que o app defina alarmes e programe ações com hora marcada. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarme, lembrete, relógio"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ativar"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ativar o Não perturbe"</string> diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml index 8c7ffb20fc79..303d669c4eae 100644 --- a/packages/SettingsLib/res/values-ro/arrays.xml +++ b/packages/SettingsLib/res/values-ro/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Este activat Filtrat"</item> <item msgid="2779123106632690576">"Activat"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Anteturi filtrate"</item> - <item msgid="4818549483446395865">"Pachete media A2DP filtrate"</item> - <item msgid="8207123990453243311">"Canal RFCOMM filtrat"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Dezactivat"</item> - <item msgid="5884245882825346396">"Magie"</item> - <item msgid="6569400572915342949">"Antet"</item> - <item msgid="1239386221416967664">"Filtru complet"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (prestabilit)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index dba7507b090d..e9afb3da12b4 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -522,7 +522,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarme și mementouri"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Permite setarea pentru alarme și mementouri"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarme și mementouri"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite acestei aplicații să stabilească alarme și să planifice acțiuni dependente de timp. Astfel, aplicația poate să ruleze în fundal, fapt care ar putea consuma mai multă baterie.\n\nDacă permisiunea este dezactivată, alarmele și evenimentele dependente de timp planificate de aplicație nu vor funcționa."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite acestei aplicații să seteze alarme și să planifice acțiuni care trebuie realizate în timp scurt. Astfel, aplicația poate să ruleze în fundal, ceea ce ar putea crește consumul de baterie.\n\nDacă permisiunea este dezactivată, alarmele și evenimentele dependente de timp planificate de aplicație nu vor funcționa."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programare, alarmă, memento, ceas"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activează"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activează Nu deranja"</string> diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml index 1df043538bd9..4772df984ff5 100644 --- a/packages/SettingsLib/res/values-ru/arrays.xml +++ b/packages/SettingsLib/res/values-ru/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Включены фильтры"</item> <item msgid="2779123106632690576">"Включено"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Применен фильтр по заголовкам"</item> - <item msgid="4818549483446395865">"Применен фильтр по медиапакетам A2DP"</item> - <item msgid="8207123990453243311">"Применен фильтр по каналу RFCOMM"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Отключено"</item> - <item msgid="5884245882825346396">"Специальный параметр"</item> - <item msgid="6569400572915342949">"Заголовок"</item> - <item msgid="1239386221416967664">"Общий фильтр"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (по умолчанию)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 76d163cea817..16788c3c828c 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -522,7 +522,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Будильники и напоминания"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Разрешить установку будильников и напоминаний"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будильники и напоминания"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Если вы разрешите этому приложению устанавливать будильники и планировать на определенное время действия, оно будет работать в фоновом режиме. В таком случае заряд батареи может расходоваться быстрее.\n\nЕсли отключить эту настройку, текущие будильники и созданные приложением мероприятия перестанут запускаться."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Вы можете разрешить этому приложению устанавливать будильники и планировать запуск действий в определенное время. В этом случае оно будет работать в фоновом режиме и быстрее расходовать заряд батареи.\n\nЕсли отключить это разрешение, текущие будильники и созданные приложением события перестанут запускаться."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"установить, будильник, напоминание, часы"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Включить"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Включите режим \"Не беспокоить\""</string> diff --git a/packages/SettingsLib/res/values-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml index b516ce629980..8de6a71d1dd4 100644 --- a/packages/SettingsLib/res/values-si/arrays.xml +++ b/packages/SettingsLib/res/values-si/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"සබල පෙරහන් කළ"</item> <item msgid="2779123106632690576">"සබලයි"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"ශීර්ෂයන් පෙරන ලදි"</item> - <item msgid="4818549483446395865">"A2DP මාධ්ය පැකට් පෙරන ලදි"</item> - <item msgid="8207123990453243311">"RFCOMM නාලිකාව පෙරන ලදි"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"අබලයි"</item> - <item msgid="5884245882825346396">"මැජික්"</item> - <item msgid="6569400572915342949">"ශීර්ෂකය"</item> - <item msgid="1239386221416967664">"පූර්ණ පෙරහන"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (පෙරනිමි)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml index 35c4518c851c..f15dfb7629d3 100644 --- a/packages/SettingsLib/res/values-sk/arrays.xml +++ b/packages/SettingsLib/res/values-sk/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Aktivované filtrované"</item> <item msgid="2779123106632690576">"Aktivované"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Hlavičky sú filtrované"</item> - <item msgid="4818549483446395865">"Balíky A2DP Media sú filtrované"</item> - <item msgid="8207123990453243311">"Kanál RFCOMM je filtrovaný"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Deaktivované"</item> - <item msgid="5884245882825346396">"Magic"</item> - <item msgid="6569400572915342949">"Hlavička"</item> - <item msgid="1239386221416967664">"Úplný filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (predvolené)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml index 12442a451a57..9ef852c062c2 100644 --- a/packages/SettingsLib/res/values-sl/arrays.xml +++ b/packages/SettingsLib/res/values-sl/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Omogočeno filtrirano"</item> <item msgid="2779123106632690576">"Omogočeno"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Glave so filtrirane"</item> - <item msgid="4818549483446395865">"Predstavnostni paketi A2DP so filtrirani"</item> - <item msgid="8207123990453243311">"Kanal RFCOMM je filtriran"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Onemogočeno"</item> - <item msgid="5884245882825346396">"Čarovnija"</item> - <item msgid="6569400572915342949">"Glava"</item> - <item msgid="1239386221416967664">"Polni filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (privzeto)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-sq/arrays.xml b/packages/SettingsLib/res/values-sq/arrays.xml index 742a86a7fbe2..89a316dcd6e7 100644 --- a/packages/SettingsLib/res/values-sq/arrays.xml +++ b/packages/SettingsLib/res/values-sq/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Të aktivizuara të filtruara"</item> <item msgid="2779123106632690576">"Aktiv"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Titujt e filtruar"</item> - <item msgid="4818549483446395865">"Paketat e medias A2DP të filtruara"</item> - <item msgid="8207123990453243311">"Kanalet e RFCOMM të filtruara"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Joaktiv"</item> - <item msgid="5884245882825346396">"Magji"</item> - <item msgid="6569400572915342949">"Titulli"</item> - <item msgid="1239386221416967664">"Filtri i plotë"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (I parazgjedhur)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-sr/arrays.xml b/packages/SettingsLib/res/values-sr/arrays.xml index a338142a418e..e76820e3368e 100644 --- a/packages/SettingsLib/res/values-sr/arrays.xml +++ b/packages/SettingsLib/res/values-sr/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Омогућено филтрирано"</item> <item msgid="2779123106632690576">"Омогућено"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Филтрирана заглавља"</item> - <item msgid="4818549483446395865">"Филтрирани A2DP медијски пакети"</item> - <item msgid="8207123990453243311">"Филтрирани RFCOMM канал"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Онемогућено"</item> - <item msgid="5884245882825346396">"Магија"</item> - <item msgid="6569400572915342949">"Заглавље"</item> - <item msgid="1239386221416967664">"Потпуни филтер"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (подразумевано)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml index a31c155f530e..391f603721c2 100644 --- a/packages/SettingsLib/res/values-sv/arrays.xml +++ b/packages/SettingsLib/res/values-sv/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filtrering har aktiverats"</item> <item msgid="2779123106632690576">"Aktiverad"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Filtrerade rubriker"</item> - <item msgid="4818549483446395865">"Filtrerade A2DP-mediapaket"</item> - <item msgid="8207123990453243311">"Filtrerad RFCOMM-kanal"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Inaktiverad"</item> - <item msgid="5884245882825346396">"Magi"</item> - <item msgid="6569400572915342949">"Rubrik"</item> - <item msgid="1239386221416967664">"Fullständigt filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (standard)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml index 8daf0920d8c5..1a33b6e4ce7c 100644 --- a/packages/SettingsLib/res/values-sw/arrays.xml +++ b/packages/SettingsLib/res/values-sw/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Vichujio Vilivyowekwa"</item> <item msgid="2779123106632690576">"Imewashwa"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Vijajuu Vimechujwa"</item> - <item msgid="4818549483446395865">"Kifurushi cha Maelezo cha A2DP Kimechujwa"</item> - <item msgid="8207123990453243311">"Kituo cha RFCOMM Kimechujwa"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Kimezimwa"</item> - <item msgid="5884245882825346396">"Kiinimacho"</item> - <item msgid="6569400572915342949">"Kijajuu"</item> - <item msgid="1239386221416967664">"Kichujio Kamili"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Chaguomsingi)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml index 8f2fe7e2ab01..23eb2426b49f 100644 --- a/packages/SettingsLib/res/values-ta/arrays.xml +++ b/packages/SettingsLib/res/values-ta/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"இயக்கப்பட்டு வடிகட்டப்பட்டது"</item> <item msgid="2779123106632690576">"இயக்கப்பட்டது"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"வடிகட்டப்பட்ட தலைப்புப்பகுதிகள்"</item> - <item msgid="4818549483446395865">"வடிகட்டப்பட்ட A2DP மீடியா பேக்கெட்டுகள்"</item> - <item msgid="8207123990453243311">"வடிகட்டப்பட்ட RFCOMM சேனல்"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"முடக்கப்பட்டுள்ளது"</item> - <item msgid="5884245882825346396">"மேஜிக்"</item> - <item msgid="6569400572915342949">"தலைப்புப்பகுதி"</item> - <item msgid="1239386221416967664">"முழு வடிப்பான்"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (இயல்பு)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml index 4b07f37a1040..0f62c1d8d842 100644 --- a/packages/SettingsLib/res/values-te/arrays.xml +++ b/packages/SettingsLib/res/values-te/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"ప్రారంభించబడింది ఫిల్టర్ చేయబడింది"</item> <item msgid="2779123106632690576">"ప్రారంభించబడింది"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"హెడర్లు ఫిల్టర్ చేయబడ్డాయి"</item> - <item msgid="4818549483446395865">"A2DP మీడియా ప్యాకెట్లు ఫిల్టర్ చేయబడ్డాయి"</item> - <item msgid="8207123990453243311">"RFCOMM ఛానెల్ ఫిల్టర్ చేయబడింది"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"డిజేబుల్ చేయబడింది"</item> - <item msgid="5884245882825346396">"మ్యాజిక్"</item> - <item msgid="6569400572915342949">"హెడర్"</item> - <item msgid="1239386221416967664">"పూర్తి ఫిల్టర్"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.4 (ఆటోమేటిక్ సెట్టింగ్)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml index be8dd8bb7fdf..8d98cdb54fd3 100644 --- a/packages/SettingsLib/res/values-th/arrays.xml +++ b/packages/SettingsLib/res/values-th/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"เปิดใช้รายการที่กรอง"</item> <item msgid="2779123106632690576">"เปิดใช้"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"ส่วนหัวที่กรอง"</item> - <item msgid="4818549483446395865">"แพ็กเก็ตสื่อ A2DP ที่กรอง"</item> - <item msgid="8207123990453243311">"แชแนล RFCOMM ที่กรอง"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"ปิดใช้"</item> - <item msgid="5884245882825346396">"Magic"</item> - <item msgid="6569400572915342949">"ส่วนหัว"</item> - <item msgid="1239386221416967664">"ตัวกรองแบบเต็ม"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ค่าเริ่มต้น)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml index 9b4ae0ef8cfd..ed47d3294280 100644 --- a/packages/SettingsLib/res/values-tl/arrays.xml +++ b/packages/SettingsLib/res/values-tl/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Na-enable Na-filter"</item> <item msgid="2779123106632690576">"Naka-enable"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Mga Na-filter na Header"</item> - <item msgid="4818549483446395865">"Mga Na-filter na A2DP Media Packet"</item> - <item msgid="8207123990453243311">"Mga Na-filter na RFCOMM Channel"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Naka-disable"</item> - <item msgid="5884245882825346396">"Magic"</item> - <item msgid="6569400572915342949">"Header"</item> - <item msgid="1239386221416967664">"Kumpletong Filter"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Default)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml index 57c993514e2e..45580f5b4f2c 100644 --- a/packages/SettingsLib/res/values-tr/arrays.xml +++ b/packages/SettingsLib/res/values-tr/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Etkin Filtrelenmiş"</item> <item msgid="2779123106632690576">"Etkin"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Başlıklar Filtrelendi"</item> - <item msgid="4818549483446395865">"A2DP Medya Paketleri Filtrelendi"</item> - <item msgid="8207123990453243311">"RFCOMM Kanalı Filtrelendi"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Devre dışı"</item> - <item msgid="5884245882825346396">"Sihir"</item> - <item msgid="6569400572915342949">"Başlık"</item> - <item msgid="1239386221416967664">"Tam Filtre"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Varsayılan)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 86d53e7d586d..1f56170f6294 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -520,7 +520,7 @@ <string name="okay" msgid="949938843324579502">"Tamam"</string> <string name="done" msgid="381184316122520313">"Bitti"</string> <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmlar ve hatırlatıcılar"</string> - <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Alarm ve hatırlatıcı ayarlanmasına izin ver"</string> + <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Alarm ve hatırlatıcı ayarlamasına izin ver"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmlar ve hatırlatıcılar"</string> <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Bu uygulamanın alarm kurmasına ve zamana bağlı işlemler programlamasına izin verin. Bu izin, uygulamanın arka planda çalışmasına olanak sağlayarak daha fazla pil harcanmasına neden olabilir.\n\nBu izin verilmezse bu uygulama tarafından programlanmış mevcut alarmlar ve zamana bağlı etkinlikler çalışmaz."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"program, alarm, hatırlatıcı, saat"</string> diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml index 53bbb87ef7cf..f97452c159c8 100644 --- a/packages/SettingsLib/res/values-uk/arrays.xml +++ b/packages/SettingsLib/res/values-uk/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Увімкнено з фільтром"</item> <item msgid="2779123106632690576">"Увімкнено"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Фільтрування за заголовками"</item> - <item msgid="4818549483446395865">"Фільтрування за пакетами медіаданих A2DP"</item> - <item msgid="8207123990453243311">"Фільтрування за каналом RFCOMM"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Вимкнено"</item> - <item msgid="5884245882825346396">"Магія"</item> - <item msgid="6569400572915342949">"Заголовок"</item> - <item msgid="1239386221416967664">"Повний фільтр"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (за умовчанням)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml index 5f8206272306..74af32215d1b 100644 --- a/packages/SettingsLib/res/values-ur/arrays.xml +++ b/packages/SettingsLib/res/values-ur/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"فعال کردہ فلٹر کردہ"</item> <item msgid="2779123106632690576">"فعال"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"ہیڈرز فلٹر کئے گئے"</item> - <item msgid="4818549483446395865">"A2DP میڈیا پیکٹ فلٹر کیے گئے"</item> - <item msgid="8207123990453243311">"RFCOMM چینل فلٹر کیا گیا"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"غیر فعال"</item> - <item msgid="5884245882825346396">"جادو"</item> - <item msgid="6569400572915342949">"ہیڈر"</item> - <item msgid="1239386221416967664">"مکمل فلٹر"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (ڈیفالٹ)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-uz/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml index dc7e6cd539bd..3e53ae6abaf0 100644 --- a/packages/SettingsLib/res/values-uz/arrays.xml +++ b/packages/SettingsLib/res/values-uz/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Filtrlar yoniq"</item> <item msgid="2779123106632690576">"Yoniq"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Sarlavhalar filtrlandi"</item> - <item msgid="4818549483446395865">"A2DP media paketlar filtrlandi"</item> - <item msgid="8207123990453243311">"RFCOMM kanali filtrlandi"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Faolsizlantirilgan"</item> - <item msgid="5884245882825346396">"Sehr"</item> - <item msgid="6569400572915342949">"Sarlavha"</item> - <item msgid="1239386221416967664">"Toʻliq filtrlash"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (asosiy)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml index 18e6c577604d..649cb5cd7693 100644 --- a/packages/SettingsLib/res/values-vi/arrays.xml +++ b/packages/SettingsLib/res/values-vi/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Đã bật và lọc"</item> <item msgid="2779123106632690576">"Đã bật"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Đã lọc tiêu đề"</item> - <item msgid="4818549483446395865">"Đã lọc gói nội dung nghe nhìn A2DP"</item> - <item msgid="8207123990453243311">"Đã lọc kênh RFCOMM"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Đã tắt"</item> - <item msgid="5884245882825346396">"Ảo thuật"</item> - <item msgid="6569400572915342949">"Tiêu đề"</item> - <item msgid="1239386221416967664">"Bộ lọc đầy đủ"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (Mặc định)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml index 0d76e6fd7688..13941af90c8d 100644 --- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"已启用“已过滤”"</item> <item msgid="2779123106632690576">"已启用"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"已过滤标题"</item> - <item msgid="4818549483446395865">"已过滤 A2DP 媒体数据包"</item> - <item msgid="8207123990453243311">"已过滤 RFCOMM 通道"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"已停用"</item> - <item msgid="5884245882825346396">"魔术"</item> - <item msgid="6569400572915342949">"标题"</item> - <item msgid="1239386221416967664">"完整过滤器"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5(默认)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml index c30e9f700427..9b359ed4ea22 100644 --- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"已啟用篩選"</item> <item msgid="2779123106632690576">"已啟用"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"已篩選標題"</item> - <item msgid="4818549483446395865">"已篩選 A2DP 媒體封包"</item> - <item msgid="8207123990453243311">"已篩選 RFCOMM 頻道"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"已停用"</item> - <item msgid="5884245882825346396">"Magic"</item> - <item msgid="6569400572915342949">"標題"</item> - <item msgid="1239386221416967664">"完整篩選器"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (預設)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml index c6073f04b012..00362d828b91 100644 --- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"已啟用篩選結果"</item> <item msgid="2779123106632690576">"已啟用"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"已篩選標頭"</item> - <item msgid="4818549483446395865">"已篩選 A2DP 媒體封包"</item> - <item msgid="8207123990453243311">"已篩選 RFCOMM 管道"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"已停用"</item> - <item msgid="5884245882825346396">"按偏好排序"</item> - <item msgid="6569400572915342949">"標頭"</item> - <item msgid="1239386221416967664">"完整篩選器"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"AVRCP 1.5 (預設)"</item> <item msgid="1637054408779685086">"AVRCP 1.3"</item> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 8bfbd7a5cd00..588c4f4b698e 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -230,7 +230,7 @@ <string name="adb_wireless_error" msgid="721958772149779856">"錯誤"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"無線偵錯"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"如要查看並使用可用的裝置,請開啟無線偵錯功能"</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"使用 QR 圖碼配對裝置"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"使用 QR code 配對裝置"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"使用 QR code 掃描器配對新裝置"</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"使用配對碼配對裝置"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"使用六位數的配對碼配對新裝置"</string> diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml index 8b23bbbbe117..ac3cb024fef8 100644 --- a/packages/SettingsLib/res/values-zu/arrays.xml +++ b/packages/SettingsLib/res/values-zu/arrays.xml @@ -63,17 +63,13 @@ <item msgid="6336372935919715515">"Okuhlungiwe okunikwe amandla"</item> <item msgid="2779123106632690576">"Kunikwe amandla"</item> </string-array> - <string-array name="bt_hci_snoop_log_filters_entries"> - <item msgid="5118179698172834473">"Izihloko Zihlungiwe"</item> - <item msgid="4818549483446395865">"I-A2DP Media Packets Ihlungiwe"</item> - <item msgid="8207123990453243311">"Isiteshi se-RFCOMM Sihlungiwe"</item> - </string-array> - <string-array name="bt_hci_snoop_log_profile_filter_entries"> - <item msgid="6348114316510677939">"Kukhutshaziwe"</item> - <item msgid="5884245882825346396">"Umlingo"</item> - <item msgid="6569400572915342949">"Unhlokweni"</item> - <item msgid="1239386221416967664">"Isihlungi Esigcwele"</item> - </string-array> + <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) --> + <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) --> + <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) --> <string-array name="bluetooth_avrcp_versions"> <item msgid="6603880723315236832">"I-AVRCP 1.5 (Okuzenzakalelayo)"</item> <item msgid="1637054408779685086">"I-AVRCP 1.3"</item> diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java index 2614644feb20..688fc720d058 100644 --- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java +++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java @@ -55,6 +55,7 @@ public class DreamBackend { public ComponentName settingsComponentName; public CharSequence description; public Drawable previewImage; + public boolean supportsComplications = false; @Override public String toString() { @@ -175,6 +176,7 @@ public class DreamBackend { if (dreamMetadata != null) { dreamInfo.settingsComponentName = dreamMetadata.settingsActivity; dreamInfo.previewImage = dreamMetadata.previewImage; + dreamInfo.supportsComplications = dreamMetadata.showComplications; } dreamInfos.add(dreamInfo); } diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java index 6a1cee3146a2..562d20d05429 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java @@ -224,6 +224,9 @@ public class EnableZenModeDialog { mMetricsLogger.logOnConditionSelected(); updateAlarmWarningText(tag.condition); } + tag.line1.setStateDescription( + isChecked ? buttonView.getContext().getString( + com.android.internal.R.string.selected) : null); } }); diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java index 87e97b17b914..abbdaa73c18e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java @@ -196,6 +196,9 @@ public class ZenDurationDialog { if (isChecked) { tag.rb.setChecked(true); } + tag.line1.setStateDescription( + isChecked ? buttonView.getContext().getString( + com.android.internal.R.string.selected) : null); } }); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java index 59d56747ec5d..6b81c1a6f794 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java @@ -16,6 +16,8 @@ package com.android.settingslib.notification; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -78,6 +80,8 @@ public class EnableZenModeDialogTest { mController.mForeverId = Condition.newId(mContext).appendPath("forever").build(); when(mContext.getString(com.android.internal.R.string.zen_mode_forever)) .thenReturn("testSummary"); + when(mContext.getString(com.android.internal.R.string.selected)) + .thenReturn("selected"); NotificationManager.Policy alarmsEnabledPolicy = new NotificationManager.Policy( NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS, 0, 0, 0); doReturn(alarmsEnabledPolicy).when(mNotificationManager).getNotificationPolicy(); @@ -190,4 +194,25 @@ public class EnableZenModeDialogTest { // alarm warning should NOT be null assertNotNull(mController.computeAlarmWarningText(null)); } + + @Test + public void testAccessibility() { + mController.bindConditions(null); + EnableZenModeDialog.ConditionTag forever = mController.getConditionTagAt( + ZenDurationDialog.FOREVER_CONDITION_INDEX); + EnableZenModeDialog.ConditionTag countdown = mController.getConditionTagAt( + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + EnableZenModeDialog.ConditionTag alwaysAsk = mController.getConditionTagAt( + ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX); + + forever.rb.setChecked(true); + assertThat(forever.line1.getStateDescription().toString()).isEqualTo("selected"); + assertThat(countdown.line1.getStateDescription()).isNull(); + assertThat(alwaysAsk.line1.getStateDescription()).isNull(); + + alwaysAsk.rb.setChecked(true); + assertThat(forever.line1.getStateDescription()).isNull(); + assertThat(countdown.line1.getStateDescription()).isNull(); + assertThat(alwaysAsk.line1.getStateDescription().toString()).isEqualTo("selected"); + } }
\ No newline at end of file diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java index 437c0d4f4469..fc45e89a6dd5 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java @@ -16,6 +16,8 @@ package com.android.settingslib.notification; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -205,4 +207,27 @@ public class ZenDurationDialogTest { ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); assertEquals(120, tag.countdownZenDuration); } + + @Test + public void testAccessibility() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION, + Settings.Secure.ZEN_DURATION_FOREVER); + mController.setupDialog(mBuilder); + ZenDurationDialog.ConditionTag forever = mController.getConditionTagAt( + ZenDurationDialog.FOREVER_CONDITION_INDEX); + ZenDurationDialog.ConditionTag countdown = mController.getConditionTagAt( + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + ZenDurationDialog.ConditionTag alwaysAsk = mController.getConditionTagAt( + ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX); + + forever.rb.setChecked(true); + assertThat(forever.line1.getStateDescription().toString()).isEqualTo("selected"); + assertThat(countdown.line1.getStateDescription()).isNull(); + assertThat(alwaysAsk.line1.getStateDescription()).isNull(); + + alwaysAsk.rb.setChecked(true); + assertThat(forever.line1.getStateDescription()).isNull(); + assertThat(countdown.line1.getStateDescription()).isNull(); + assertThat(alwaysAsk.line1.getStateDescription().toString()).isEqualTo("selected"); + } }
\ No newline at end of file diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index 1ac20471109f..346462df004b 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -48,6 +48,7 @@ android_test { "test/**/*.java", "src/android/provider/settings/backup/*", "src/android/provider/settings/validators/*", + "src/com/android/providers/settings/GenerationRegistry.java", "src/com/android/providers/settings/SettingsBackupAgent.java", "src/com/android/providers/settings/SettingsState.java", "src/com/android/providers/settings/SettingsHelper.java", diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 210a38796b6a..93394f3872ee 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -142,6 +142,8 @@ public class SecureSettings { Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, Settings.Secure.VOLUME_HUSH_GESTURE, Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT, + Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED, + Settings.Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, Settings.Secure.HUSH_GESTURE_USED, Settings.Secure.IN_CALL_NOTIFICATION_ENABLED, Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 39cd24a3b6d4..96578626d5cc 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -219,6 +219,8 @@ public class SecureSettingsValidators { COLON_SEPARATED_PACKAGE_LIST_VALIDATOR); // legacy restore setting VALIDATORS.put(Secure.HUSH_GESTURE_USED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.MANUAL_RINGER_TOGGLE_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(Secure.LOW_POWER_WARNING_ACKNOWLEDGED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.IN_CALL_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java index 56173310a8bb..7f3b0ff8c838 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java @@ -17,19 +17,19 @@ package com.android.providers.settings; import android.os.Bundle; -import android.os.UserManager; import android.provider.Settings; +import android.util.ArrayMap; import android.util.MemoryIntArray; import android.util.Slog; -import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; /** * This class tracks changes for config/global/secure/system tables - * on a per user basis and updates a shared memory region which + * on a per user basis and updates shared memory regions which * client processes can read to determine if their local caches are * stale. */ @@ -40,138 +40,187 @@ final class GenerationRegistry { private final Object mLock; + // Key -> backingStore mapping @GuardedBy("mLock") - private final SparseIntArray mKeyToIndexMap = new SparseIntArray(); + private final ArrayMap<Integer, MemoryIntArray> mKeyToBackingStoreMap = new ArrayMap(); + // Key -> (String->Index map) mapping @GuardedBy("mLock") - private MemoryIntArray mBackingStore; + private final ArrayMap<Integer, ArrayMap<String, Integer>> mKeyToIndexMapMap = new ArrayMap<>(); + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + // Maximum number of backing stores allowed + static final int NUM_MAX_BACKING_STORE = 8; + + @GuardedBy("mLock") + private int mNumBackingStore = 0; + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + // Maximum size of an individual backing store + static final int MAX_BACKING_STORE_SIZE = MemoryIntArray.getMaxSize(); public GenerationRegistry(Object lock) { mLock = lock; } - public void incrementGeneration(int key) { + /** + * Increment the generation number if the setting is already cached in the backing stores. + * Otherwise, do nothing. + */ + public void incrementGeneration(int key, String name) { + final boolean isConfig = + (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG); + // Only store the prefix if the mutated setting is a config + final String indexMapKey = isConfig ? (name.split("/")[0] + "/") : name; synchronized (mLock) { - MemoryIntArray backingStore = getBackingStoreLocked(); - if (backingStore != null) { - try { - final int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore); - if (index >= 0) { - final int generation = backingStore.get(index) + 1; - backingStore.set(index, generation); - } - } catch (IOException e) { - Slog.e(LOG_TAG, "Error updating generation id", e); - destroyBackingStore(); + final MemoryIntArray backingStore = getBackingStoreLocked(key, + /* createIfNotExist= */ false); + if (backingStore == null) { + return; + } + try { + final int index = getKeyIndexLocked(key, indexMapKey, mKeyToIndexMapMap, + backingStore, /* createIfNotExist= */ false); + if (index < 0) { + return; + } + final int generation = backingStore.get(index) + 1; + backingStore.set(index, generation); + if (DEBUG) { + Slog.i(LOG_TAG, "Incremented generation for setting:" + indexMapKey + + " key:" + SettingsState.keyToString(key) + + " at index:" + index); } + } catch (IOException e) { + Slog.e(LOG_TAG, "Error updating generation id", e); + destroyBackingStoreLocked(key); } } } - public void addGenerationData(Bundle bundle, int key) { + /** + * Return the backing store's reference, the index and the current generation number + * of a cached setting. If it was not in the backing store, first create the entry in it before + * returning the result. + */ + public void addGenerationData(Bundle bundle, int key, String indexMapKey) { synchronized (mLock) { - MemoryIntArray backingStore = getBackingStoreLocked(); + final MemoryIntArray backingStore = getBackingStoreLocked(key, + /* createIfNotExist= */ true); + if (backingStore == null) { + // Error accessing existing backing store or no new backing store is available + return; + } try { - if (backingStore != null) { - final int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore); - if (index >= 0) { - bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, - backingStore); - bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index); - bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY, - backingStore.get(index)); - if (DEBUG) { - Slog.i(LOG_TAG, "Exported index:" + index + " for key:" - + SettingsProvider.keyToString(key)); - } - } + final int index = getKeyIndexLocked(key, indexMapKey, mKeyToIndexMapMap, + backingStore, /* createIfNotExist= */ true); + if (index < 0) { + // Should not happen unless having error accessing the backing store + return; + } + bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, + backingStore); + bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index); + bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY, + backingStore.get(index)); + if (DEBUG) { + Slog.i(LOG_TAG, "Exported index:" + index + + " for setting:" + indexMapKey + + " key:" + SettingsState.keyToString(key)); } } catch (IOException e) { Slog.e(LOG_TAG, "Error adding generation data", e); - destroyBackingStore(); + destroyBackingStoreLocked(key); } } } public void onUserRemoved(int userId) { + final int secureKey = SettingsState.makeKey( + SettingsState.SETTINGS_TYPE_SECURE, userId); + final int systemKey = SettingsState.makeKey( + SettingsState.SETTINGS_TYPE_SYSTEM, userId); synchronized (mLock) { - MemoryIntArray backingStore = getBackingStoreLocked(); - if (backingStore != null && mKeyToIndexMap.size() > 0) { - try { - final int secureKey = SettingsProvider.makeKey( - SettingsProvider.SETTINGS_TYPE_SECURE, userId); - resetSlotForKeyLocked(secureKey, mKeyToIndexMap, backingStore); - - final int systemKey = SettingsProvider.makeKey( - SettingsProvider.SETTINGS_TYPE_SYSTEM, userId); - resetSlotForKeyLocked(systemKey, mKeyToIndexMap, backingStore); - } catch (IOException e) { - Slog.e(LOG_TAG, "Error cleaning up for user", e); - destroyBackingStore(); - } + if (mKeyToIndexMapMap.containsKey(secureKey)) { + destroyBackingStoreLocked(secureKey); + mKeyToIndexMapMap.remove(secureKey); + mNumBackingStore = mNumBackingStore - 1; + } + if (mKeyToIndexMapMap.containsKey(systemKey)) { + destroyBackingStoreLocked(systemKey); + mKeyToIndexMapMap.remove(systemKey); + mNumBackingStore = mNumBackingStore - 1; } } } @GuardedBy("mLock") - private MemoryIntArray getBackingStoreLocked() { - if (mBackingStore == null) { - // One for the config table, one for the global table, two for system - // and secure tables for a managed profile (managed profile is not - // included in the max user count), ten for partially deleted users if - // users are quickly removed, and twice max user count for system and - // secure. - final int size = 1 + 1 + 2 + 10 + 2 * UserManager.getMaxSupportedUsers(); + private MemoryIntArray getBackingStoreLocked(int key, boolean createIfNotExist) { + MemoryIntArray backingStore = mKeyToBackingStoreMap.get(key); + if (!createIfNotExist) { + return backingStore; + } + if (backingStore == null) { try { - mBackingStore = new MemoryIntArray(size); + if (mNumBackingStore >= NUM_MAX_BACKING_STORE) { + Slog.e(LOG_TAG, "Error creating backing store - at capacity"); + return null; + } + backingStore = new MemoryIntArray(MAX_BACKING_STORE_SIZE); + mKeyToBackingStoreMap.put(key, backingStore); + mNumBackingStore += 1; if (DEBUG) { - Slog.e(LOG_TAG, "Created backing store " + mBackingStore); + Slog.e(LOG_TAG, "Created backing store for " + + SettingsState.keyToString(key) + " on user: " + + SettingsState.getUserIdFromKey(key)); } } catch (IOException e) { Slog.e(LOG_TAG, "Error creating generation tracker", e); } } - return mBackingStore; + return backingStore; } - private void destroyBackingStore() { - if (mBackingStore != null) { + @GuardedBy("mLock") + private void destroyBackingStoreLocked(int key) { + MemoryIntArray backingStore = mKeyToBackingStoreMap.get(key); + if (backingStore != null) { try { - mBackingStore.close(); + backingStore.close(); if (DEBUG) { - Slog.e(LOG_TAG, "Destroyed backing store " + mBackingStore); + Slog.e(LOG_TAG, "Destroyed backing store " + backingStore); } } catch (IOException e) { Slog.e(LOG_TAG, "Cannot close generation memory array", e); } - mBackingStore = null; + mKeyToBackingStoreMap.remove(key); } } - private static void resetSlotForKeyLocked(int key, SparseIntArray keyToIndexMap, - MemoryIntArray backingStore) throws IOException { - final int index = keyToIndexMap.get(key, -1); - if (index >= 0) { - keyToIndexMap.delete(key); - backingStore.set(index, 0); - if (DEBUG) { - Slog.i(LOG_TAG, "Freed index:" + index + " for key:" - + SettingsProvider.keyToString(key)); + private static int getKeyIndexLocked(int key, String indexMapKey, + ArrayMap<Integer, ArrayMap<String, Integer>> keyToIndexMapMap, + MemoryIntArray backingStore, boolean createIfNotExist) throws IOException { + ArrayMap<String, Integer> nameToIndexMap = keyToIndexMapMap.get(key); + if (nameToIndexMap == null) { + if (!createIfNotExist) { + return -1; } + nameToIndexMap = new ArrayMap<>(); + keyToIndexMapMap.put(key, nameToIndexMap); } - } - - private static int getKeyIndexLocked(int key, SparseIntArray keyToIndexMap, - MemoryIntArray backingStore) throws IOException { - int index = keyToIndexMap.get(key, -1); + int index = nameToIndexMap.getOrDefault(indexMapKey, -1); if (index < 0) { + if (!createIfNotExist) { + return -1; + } index = findNextEmptyIndex(backingStore); if (index >= 0) { backingStore.set(index, 1); - keyToIndexMap.append(key, index); + nameToIndexMap.put(indexMapKey, index); if (DEBUG) { - Slog.i(LOG_TAG, "Allocated index:" + index + " for key:" - + SettingsProvider.keyToString(key)); + Slog.i(LOG_TAG, "Allocated index:" + index + " for setting:" + indexMapKey + + " of type:" + SettingsState.keyToString(key) + + " on user:" + SettingsState.getUserIdFromKey(key)); } } else { Slog.e(LOG_TAG, "Could not allocate generation index"); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 574fd5a7ec13..11154d165109 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -182,6 +182,8 @@ public class SettingsBackupAgent extends BackupAgentHelper { "visible_pattern_enabled"; private static final String KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS = "power_button_instantly_locks"; + private static final String KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY = + "pin_enhanced_privacy"; // Name of the temporary file we use during full backup/restore. This is // stored in the full-backup tarfile as well, so should not be changed. @@ -709,6 +711,10 @@ public class SettingsBackupAgent extends BackupAgentHelper { out.writeUTF(KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS); out.writeUTF(powerButtonInstantlyLocks ? "1" : "0"); } + if (lockPatternUtils.isPinEnhancedPrivacyEverChosen(userId)) { + out.writeUTF(KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY); + out.writeUTF(lockPatternUtils.isPinEnhancedPrivacyEnabled(userId) ? "1" : "0"); + } // End marker out.writeUTF(""); out.flush(); @@ -961,6 +967,9 @@ public class SettingsBackupAgent extends BackupAgentHelper { case KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS: lockPatternUtils.setPowerButtonInstantlyLocks("1".equals(value), userId); break; + case KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY: + lockPatternUtils.setPinEnhancedPrivacyEnabled("1".equals(value), userId); + break; } } in.close(); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 683c08e3bfa4..ba275ebca168 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -35,6 +35,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OV import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.accessibility.util.AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM; import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX; +import static com.android.providers.settings.SettingsState.makeKey; import android.Manifest; import android.annotation.NonNull; @@ -375,10 +376,6 @@ public class SettingsProvider extends ContentProvider { @GuardedBy("mLock") private boolean mSyncConfigDisabledUntilReboot; - public static int makeKey(int type, int userId) { - return SettingsState.makeKey(type, userId); - } - public static int getTypeFromKey(int key) { return SettingsState.getTypeFromKey(key); } @@ -387,9 +384,6 @@ public class SettingsProvider extends ContentProvider { return SettingsState.getUserIdFromKey(key); } - public static String keyToString(int key) { - return SettingsState.keyToString(key); - } @ChangeId @EnabledSince(targetSdkVersion=android.os.Build.VERSION_CODES.S) private static final long ENFORCE_READ_PERMISSION_FOR_MULTI_SIM_DATA_CALL = 172670679L; @@ -428,22 +422,26 @@ public class SettingsProvider extends ContentProvider { switch (method) { case Settings.CALL_METHOD_GET_CONFIG: { Setting setting = getConfigSetting(name); - return packageValueForCallResult(setting, isTrackingGeneration(args)); + return packageValueForCallResult(SETTINGS_TYPE_CONFIG, name, requestingUserId, + setting, isTrackingGeneration(args)); } case Settings.CALL_METHOD_GET_GLOBAL: { Setting setting = getGlobalSetting(name); - return packageValueForCallResult(setting, isTrackingGeneration(args)); + return packageValueForCallResult(SETTINGS_TYPE_GLOBAL, name, requestingUserId, + setting, isTrackingGeneration(args)); } case Settings.CALL_METHOD_GET_SECURE: { Setting setting = getSecureSetting(name, requestingUserId); - return packageValueForCallResult(setting, isTrackingGeneration(args)); + return packageValueForCallResult(SETTINGS_TYPE_SECURE, name, requestingUserId, + setting, isTrackingGeneration(args)); } case Settings.CALL_METHOD_GET_SYSTEM: { Setting setting = getSystemSetting(name, requestingUserId); - return packageValueForCallResult(setting, isTrackingGeneration(args)); + return packageValueForCallResult(SETTINGS_TYPE_SYSTEM, name, requestingUserId, + setting, isTrackingGeneration(args)); } case Settings.CALL_METHOD_PUT_CONFIG: { @@ -553,7 +551,7 @@ public class SettingsProvider extends ContentProvider { case Settings.CALL_METHOD_LIST_CONFIG: { String prefix = getSettingPrefix(args); - Bundle result = packageValuesForCallResult(getAllConfigFlags(prefix), + Bundle result = packageValuesForCallResult(prefix, getAllConfigFlags(prefix), isTrackingGeneration(args)); reportDeviceConfigAccess(prefix); return result; @@ -1319,6 +1317,7 @@ public class SettingsProvider extends ContentProvider { return false; } + @NonNull private HashMap<String, String> getAllConfigFlags(@Nullable String prefix) { if (DEBUG) { Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix); @@ -2316,7 +2315,8 @@ public class SettingsProvider extends ContentProvider { "get/set setting for user", null); } - private Bundle packageValueForCallResult(Setting setting, boolean trackingGeneration) { + private Bundle packageValueForCallResult(int type, @NonNull String name, int userId, + @Nullable Setting setting, boolean trackingGeneration) { if (!trackingGeneration) { if (setting == null || setting.isNull()) { return NULL_SETTING_BUNDLE; @@ -2325,21 +2325,40 @@ public class SettingsProvider extends ContentProvider { } Bundle result = new Bundle(); result.putString(Settings.NameValueTable.VALUE, - !setting.isNull() ? setting.getValue() : null); + (setting != null && !setting.isNull()) ? setting.getValue() : null); - mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getKey()); + if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) { + // Don't track generation for non-existent settings unless the name is predefined + synchronized (mLock) { + mSettingsRegistry.mGenerationRegistry.addGenerationData(result, + SettingsState.makeKey(type, userId), name); + } + } return result; } - private Bundle packageValuesForCallResult(HashMap<String, String> keyValues, - boolean trackingGeneration) { + private boolean isSettingPreDefined(String name, int type) { + if (type == SETTINGS_TYPE_GLOBAL) { + return sAllGlobalSettings.contains(name); + } else if (type == SETTINGS_TYPE_SECURE) { + return sAllSecureSettings.contains(name); + } else if (type == SETTINGS_TYPE_SYSTEM) { + return sAllSystemSettings.contains(name); + } else { + return false; + } + } + + private Bundle packageValuesForCallResult(String prefix, + @NonNull HashMap<String, String> keyValues, boolean trackingGeneration) { Bundle result = new Bundle(); result.putSerializable(Settings.NameValueTable.VALUE, keyValues); if (trackingGeneration) { + // Track generation even if the namespace is empty because this is for system apps synchronized (mLock) { mSettingsRegistry.mGenerationRegistry.addGenerationData(result, - mSettingsRegistry.getSettingsLocked( - SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM).mKey); + mSettingsRegistry.getSettingsLocked(SETTINGS_TYPE_CONFIG, + UserHandle.USER_SYSTEM).mKey, prefix); } } @@ -3449,7 +3468,7 @@ public class SettingsProvider extends ContentProvider { private void notifyForSettingsChange(int key, String name) { // Increment the generation first, so observers always see the new value - mGenerationRegistry.incrementGeneration(key); + mGenerationRegistry.incrementGeneration(key, name); if (isGlobalSettingsKey(key) || isConfigSettingsKey(key)) { final long token = Binder.clearCallingIdentity(); @@ -3487,7 +3506,7 @@ public class SettingsProvider extends ContentProvider { List<String> changedSettings) { // Increment the generation first, so observers always see the new value - mGenerationRegistry.incrementGeneration(key); + mGenerationRegistry.incrementGeneration(key, prefix); StringBuilder stringBuilder = new StringBuilder(prefix); for (int i = 0; i < changedSettings.size(); ++i) { @@ -3513,7 +3532,7 @@ public class SettingsProvider extends ContentProvider { if (profileId != userId) { final int key = makeKey(type, profileId); // Increment the generation first, so observers always see the new value - mGenerationRegistry.incrementGeneration(key); + mGenerationRegistry.incrementGeneration(key, name); mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED, profileId, 0, uri).sendToTarget(); } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index c0176d57cd59..278ceb944ef6 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -322,6 +322,7 @@ public class SettingsBackupTest { Settings.Global.LOW_BATTERY_SOUND, Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, Settings.Global.LOW_POWER_MODE, + Settings.Global.EXTRA_LOW_POWER_MODE, Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, Settings.Global.LOW_POWER_MODE_STICKY, Settings.Global.LOW_POWER_MODE_SUGGESTION_PARAMS, @@ -800,7 +801,6 @@ public class SettingsBackupTest { Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL, Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING, Settings.Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, - Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED, Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE, Settings.Secure.FLASHLIGHT_AVAILABLE, diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java new file mode 100644 index 000000000000..d34fe6943153 --- /dev/null +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.providers.settings; + +import static android.provider.Settings.CALL_METHOD_GENERATION_INDEX_KEY; +import static android.provider.Settings.CALL_METHOD_GENERATION_KEY; +import static android.provider.Settings.CALL_METHOD_TRACK_GENERATION_KEY; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Bundle; +import android.util.MemoryIntArray; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; + +@RunWith(AndroidJUnit4.class) +public class GenerationRegistryTest { + @Test + public void testGenerationsWithRegularSetting() throws IOException { + final GenerationRegistry generationRegistry = new GenerationRegistry(new Object()); + final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0); + final String testSecureSetting = "test_secure_setting"; + Bundle b = new Bundle(); + // IncrementGeneration should have no effect on a non-cached setting. + generationRegistry.incrementGeneration(secureKey, testSecureSetting); + generationRegistry.incrementGeneration(secureKey, testSecureSetting); + generationRegistry.incrementGeneration(secureKey, testSecureSetting); + generationRegistry.addGenerationData(b, secureKey, testSecureSetting); + // Default index is 0 and generation is only 1 despite early calls of incrementGeneration + checkBundle(b, 0, 1, false); + + generationRegistry.incrementGeneration(secureKey, testSecureSetting); + generationRegistry.addGenerationData(b, secureKey, testSecureSetting); + // Index is still 0 and generation is now 2; also check direct array access + assertThat(getArray(b).get(0)).isEqualTo(2); + + final int systemKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SYSTEM, 0); + final String testSystemSetting = "test_system_setting"; + generationRegistry.addGenerationData(b, systemKey, testSystemSetting); + // Default index is 0 and generation is 1 for another backingStore (system) + checkBundle(b, 0, 1, false); + + final String testSystemSetting2 = "test_system_setting2"; + generationRegistry.addGenerationData(b, systemKey, testSystemSetting2); + // Second system setting index is 1 and default generation is 1 + checkBundle(b, 1, 1, false); + + generationRegistry.incrementGeneration(systemKey, testSystemSetting); + generationRegistry.incrementGeneration(systemKey, testSystemSetting); + generationRegistry.addGenerationData(b, systemKey, testSystemSetting); + // First system setting generation now incremented to 3 + checkBundle(b, 0, 3, false); + + final int systemKey2 = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SYSTEM, 10); + generationRegistry.addGenerationData(b, systemKey2, testSystemSetting); + // User 10 has a new set of backingStores + checkBundle(b, 0, 1, false); + + // Check user removal + generationRegistry.onUserRemoved(10); + generationRegistry.incrementGeneration(systemKey2, testSystemSetting); + + // Removed user should not affect existing caches + generationRegistry.addGenerationData(b, secureKey, testSecureSetting); + assertThat(getArray(b).get(0)).isEqualTo(2); + + // IncrementGeneration should have no effect for a non-cached user + b.clear(); + checkBundle(b, -1, -1, true); + // AddGeneration should create new backing store for the non-cached user + generationRegistry.addGenerationData(b, systemKey2, testSystemSetting); + checkBundle(b, 0, 1, false); + } + + @Test + public void testGenerationsWithConfigSetting() throws IOException { + final GenerationRegistry generationRegistry = new GenerationRegistry(new Object()); + final String prefix = "test_namespace/"; + final int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); + + Bundle b = new Bundle(); + generationRegistry.addGenerationData(b, configKey, prefix); + checkBundle(b, 0, 1, false); + + final String setting = "test_namespace/test_setting"; + // Check that the generation of the prefix is incremented correctly + generationRegistry.incrementGeneration(configKey, setting); + generationRegistry.addGenerationData(b, configKey, prefix); + checkBundle(b, 0, 2, false); + } + + @Test + public void testMaxNumBackingStores() throws IOException { + final GenerationRegistry generationRegistry = new GenerationRegistry(new Object()); + final String testSecureSetting = "test_secure_setting"; + Bundle b = new Bundle(); + for (int i = 0; i < GenerationRegistry.NUM_MAX_BACKING_STORE; i++) { + b.clear(); + final int key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, i); + generationRegistry.addGenerationData(b, key, testSecureSetting); + checkBundle(b, 0, 1, false); + } + b.clear(); + final int key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, + GenerationRegistry.NUM_MAX_BACKING_STORE + 1); + generationRegistry.addGenerationData(b, key, testSecureSetting); + // Should fail to add generation because the number of backing stores has reached limit + checkBundle(b, -1, -1, true); + // Remove one user should free up a backing store + generationRegistry.onUserRemoved(0); + generationRegistry.addGenerationData(b, key, testSecureSetting); + checkBundle(b, 0, 1, false); + } + + @Test + public void testMaxSizeBackingStore() throws IOException { + final GenerationRegistry generationRegistry = new GenerationRegistry(new Object()); + final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0); + final String testSecureSetting = "test_secure_setting"; + Bundle b = new Bundle(); + for (int i = 0; i < GenerationRegistry.MAX_BACKING_STORE_SIZE; i++) { + generationRegistry.addGenerationData(b, secureKey, testSecureSetting + i); + checkBundle(b, i, 1, false); + } + b.clear(); + generationRegistry.addGenerationData(b, secureKey, testSecureSetting); + // Should fail to increase index because the number of entries in the backing store has + // reached the limit + checkBundle(b, -1, -1, true); + // Shouldn't affect other cached entries + generationRegistry.addGenerationData(b, secureKey, testSecureSetting + "0"); + checkBundle(b, 0, 1, false); + } + + private void checkBundle(Bundle b, int expectedIndex, int expectedGeneration, boolean isNull) + throws IOException { + final MemoryIntArray array = getArray(b); + if (isNull) { + assertThat(array).isNull(); + } else { + assertThat(array).isNotNull(); + } + final int index = b.getInt( + CALL_METHOD_GENERATION_INDEX_KEY, -1); + assertThat(index).isEqualTo(expectedIndex); + final int generation = b.getInt(CALL_METHOD_GENERATION_KEY, -1); + assertThat(generation).isEqualTo(expectedGeneration); + if (!isNull) { + // Read into the result array with the result index should match the result generation + assertThat(array.get(index)).isEqualTo(generation); + } + } + + private MemoryIntArray getArray(Bundle b) { + return b.getParcelable( + CALL_METHOD_TRACK_GENERATION_KEY, android.util.MemoryIntArray.class); + } +} diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 9d469611479a..b236ac5af104 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -449,6 +449,7 @@ systemui_optimized_java_defaults { enabled: true, optimize: true, shrink: true, + shrink_resources: true, proguard_compatibility: false, proguard_flags_files: ["proguard.flags"], }, diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt index 40a5e9794d37..c49a487c6766 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt @@ -26,7 +26,7 @@ interface Expandable { * currently not attached or visible). * * @param cujType the CUJ type from the [com.android.internal.jank.InteractionJankMonitor] - * associated to the launch that will use this controller. + * associated to the launch that will use this controller. */ fun activityLaunchController(cujType: Int? = null): ActivityLaunchAnimator.Controller? diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt index 9668066be125..3417ffd6b83a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt @@ -75,7 +75,7 @@ class LaunchAnimator(private val timings: Timings, private val interpolators: In * - Get the associated [Context]. * - Compute whether we are expanding fully above the launch container. * - Get to overlay to which we initially put the window background layer, until the opening - * window is made visible (see [openingWindowSyncView]). + * window is made visible (see [openingWindowSyncView]). * * This container can be changed to force this [Controller] to animate the expanding view * inside a different location, for instance to ensure correct layering during the diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt index b98b92219c33..ed8e70568b48 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt @@ -24,7 +24,7 @@ interface LaunchableView { * Set whether this view should block/postpone all calls to [View.setVisibility]. This ensures * that this view: * - remains invisible during the launch animation given that it is ghosted and already drawn - * somewhere else. + * somewhere else. * - remains invisible as long as a dialog expanded from it is shown. * - restores its expected visibility once the dialog expanded from it is dismissed. * diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt index 0e2d23b04a4f..6946e6bf88a8 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt @@ -182,9 +182,9 @@ class RemoteTransitionAdapter { * Represents a TransitionInfo object as an array of old-style targets * * @param wallpapers If true, this will return wallpaper targets; otherwise it returns - * non-wallpaper targets. + * non-wallpaper targets. * @param leashMap Temporary map of change leash -> launcher leash. Is an output, so should - * be populated by this function. If null, it is ignored. + * be populated by this function. If null, it is ignored. */ fun wrapTargets( info: TransitionInfo, diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt index a96f893a8db4..b89a8b0e0272 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt @@ -6,6 +6,7 @@ object ShadeInterpolation { /** * Interpolate alpha for notification background scrim during shade expansion. + * * @param fraction Shade expansion fraction */ @JvmStatic @@ -16,6 +17,7 @@ object ShadeInterpolation { /** * Interpolate alpha for shade content during shade expansion. + * * @param fraction Shade expansion fraction */ @JvmStatic diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt index 341784e26257..468a8b10bc01 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt @@ -161,7 +161,6 @@ class TextInterpolator(layout: Layout) { * This API is useful to continue animation from the middle of the state. For example, if you * animate weight from 200 to 400, then if you want to move back to 200 at the half of the * animation, it will look like - * * <pre> <code> * ``` * val interp = TextInterpolator(layout) @@ -497,7 +496,9 @@ class TextInterpolator(layout: Layout) { count, layout.textDirectionHeuristic, paint - ) { _, _, glyphs, _ -> runs.add(glyphs) } + ) { _, _, glyphs, _ -> + runs.add(glyphs) + } out.add(runs) if (lineNo > 0) { diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt index 052888b61496..b5b6037aeae4 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt @@ -25,7 +25,6 @@ import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary /** * Shader class that renders an expanding ripple effect. The ripple contains three elements: - * * 1. an expanding filled [RippleShape] that appears in the beginning and quickly fades away * 2. an expanding ring that appears throughout the effect * 3. an expanding ring-shaped area that reveals noise over #2. @@ -317,6 +316,7 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) : * Parameters used for fade in and outs of the ripple. * * <p>Note that all the fade in/ outs are "linear" progression. + * * ``` * (opacity) * 1 @@ -331,6 +331,7 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) : * fadeIn fadeOut * Start & End Start & End * ``` + * * <p>If no fade in/ out is needed, set [fadeInStart] and [fadeInEnd] to 0; [fadeOutStart] and * [fadeOutEnd] to 1. */ diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt index 79bc2f432ded..89871fa7d875 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt @@ -30,12 +30,14 @@ data class TurbulenceNoiseAnimationConfig( * Noise move speed variables. * * Its sign determines the direction; magnitude determines the speed. <ul> + * * ``` * <li> [noiseMoveSpeedX] positive: right to left; negative: left to right. * <li> [noiseMoveSpeedY] positive: bottom to top; negative: top to bottom. * <li> [noiseMoveSpeedZ] its sign doesn't matter much, as it moves in Z direction. Use it * to add turbulence in place. * ``` + * * </ul> */ val noiseMoveSpeedX: Float = 0f, diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DumpableNotRegisteredDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DumpableNotRegisteredDetector.kt new file mode 100644 index 000000000000..30e2a2527a93 --- /dev/null +++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DumpableNotRegisteredDetector.kt @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.systemui.lint + +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Context +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Location +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.SourceCodeScanner +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.UCallExpression +import org.jetbrains.uast.UClass + +/** + * Checks if any class has implemented the `Dumpable` interface but has not registered itself with + * the `DumpManager`. + */ +@Suppress("UnstableApiUsage") +class DumpableNotRegisteredDetector : Detector(), SourceCodeScanner { + + private var isDumpable: Boolean = false + private var isCoreStartable: Boolean = false + private var hasRegisterCall: Boolean = false + private var classLocation: Location? = null + + override fun beforeCheckFile(context: Context) { + isDumpable = false + isCoreStartable = false + hasRegisterCall = false + classLocation = null + } + + override fun applicableSuperClasses(): List<String> { + return listOf(DUMPABLE_CLASS_NAME) + } + + override fun getApplicableMethodNames(): List<String> { + return listOf("registerDumpable", "registerNormalDumpable", "registerCriticalDumpable") + } + + override fun visitClass(context: JavaContext, declaration: UClass) { + if (declaration.isInterface || context.evaluator.isAbstract(declaration)) { + // Don't require interfaces or abstract classes to call `register` (assume the full + // implementations will call it). This also means that we correctly don't warn for the + // `Dumpable` interface itself. + return + } + + classLocation = context.getNameLocation(declaration) + + val superTypeClassNames = declaration.superTypes.mapNotNull { it.resolve()?.qualifiedName } + isDumpable = superTypeClassNames.contains(DUMPABLE_CLASS_NAME) + isCoreStartable = superTypeClassNames.contains(CORE_STARTABLE_CLASS_NAME) + } + + override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { + if (context.evaluator.isMemberInSubClassOf(method, DUMP_MANAGER_CLASS_NAME)) { + hasRegisterCall = true + } + } + + override fun afterCheckFile(context: Context) { + if (!isDumpable) { + return + } + if (isDumpable && isCoreStartable) { + // CoreStartables will be automatically registered, so classes that implement + // CoreStartable do not need a `register` call. + return + } + + if (!hasRegisterCall) { + context.report( + issue = ISSUE, + location = classLocation!!, + message = + "Any class implementing `Dumpable` must call " + + "`DumpManager.registerNormalDumpable` or " + + "`DumpManager.registerCriticalDumpable`", + ) + } + } + + companion object { + @JvmField + val ISSUE: Issue = + Issue.create( + id = "DumpableNotRegistered", + briefDescription = "Dumpable not registered with DumpManager.", + explanation = + """ + This class has implemented the `Dumpable` interface, but it has not registered \ + itself with the `DumpManager`. This means that the class will never actually \ + be dumped. Please call `DumpManager.registerNormalDumpable` or \ + `DumpManager.registerCriticalDumpable` in the class's constructor or \ + initialization method.""", + category = Category.CORRECTNESS, + priority = 8, + severity = Severity.WARNING, + implementation = + Implementation(DumpableNotRegisteredDetector::class.java, Scope.JAVA_FILE_SCOPE) + ) + + private const val DUMPABLE_CLASS_NAME = "com.android.systemui.Dumpable" + private const val CORE_STARTABLE_CLASS_NAME = "com.android.systemui.CoreStartable" + private const val DUMP_MANAGER_CLASS_NAME = "com.android.systemui.dump.DumpManager" + } +} diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt index 254a6fb4714f..84f70502fa45 100644 --- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt +++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt @@ -32,6 +32,7 @@ class SystemUIIssueRegistry : IssueRegistry() { BindServiceOnMainThreadDetector.ISSUE, BroadcastSentViaContextDetector.ISSUE, CleanArchitectureDependencyViolationDetector.ISSUE, + DumpableNotRegisteredDetector.ISSUE, SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY, NonInjectedMainThreadDetector.ISSUE, diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt index a4b59fd8e086..a5f832a17de4 100644 --- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt +++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt @@ -64,7 +64,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes class BadClass( private val viewModel: ViewModel, ) - """.trimIndent() + """ + .trimIndent() ) ) .issues( @@ -98,7 +99,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes class BadClass( private val repository: Repository, ) - """.trimIndent() + """ + .trimIndent() ) ) .issues( @@ -136,7 +138,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes private val interactor: Interactor, private val viewmodel: ViewModel, ) - """.trimIndent() + """ + .trimIndent() ) ) .issues( @@ -176,7 +179,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes class BadClass( private val interactor: Interactor, ) - """.trimIndent() + """ + .trimIndent() ) ) .issues( @@ -207,7 +211,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes data class Model( private val name: String, ) - """.trimIndent() + """ + .trimIndent() ) private val REPOSITORY_FILE = TestFiles.kotlin( @@ -228,7 +233,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes return models } } - """.trimIndent() + """ + .trimIndent() ) private val INTERACTOR_FILE = TestFiles.kotlin( @@ -245,7 +251,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes return repository.getModels() } } - """.trimIndent() + """ + .trimIndent() ) private val VIEW_MODEL_FILE = TestFiles.kotlin( @@ -262,7 +269,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes return interactor.getModels().map { model -> model.name } } } - """.trimIndent() + """ + .trimIndent() ) private val NON_CLEAN_ARCHITECTURE_FILE = TestFiles.kotlin( @@ -282,7 +290,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes ) } } - """.trimIndent() + """ + .trimIndent() ) private val LEGITIMATE_FILES = arrayOf( diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DumpableNotRegisteredDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DumpableNotRegisteredDetectorTest.kt new file mode 100644 index 000000000000..3d6cbc749569 --- /dev/null +++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DumpableNotRegisteredDetectorTest.kt @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.systemui.lint + +import com.android.tools.lint.checks.infrastructure.TestFiles +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Issue +import org.junit.Test + +@Suppress("UnstableApiUsage") +class DumpableNotRegisteredDetectorTest : SystemUILintDetectorTest() { + override fun getDetector(): Detector = DumpableNotRegisteredDetector() + + override fun getIssues(): List<Issue> = listOf(DumpableNotRegisteredDetector.ISSUE) + + @Test + fun classIsNotDumpable_noViolation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + class SomeClass() { + } + """.trimIndent() + ), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun classIsDumpable_andRegisterIsCalled_noViolation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + import com.android.systemui.dump.DumpManager; + + public class SomeClass implements Dumpable { + SomeClass(DumpManager dumpManager) { + dumpManager.registerDumpable(this); + } + + @Override + void dump(PrintWriter pw, String[] args) { + pw.println("testDump"); + } + } + """.trimIndent() + ), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun classIsDumpable_andRegisterNormalIsCalled_noViolation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + import com.android.systemui.dump.DumpManager; + + public class SomeClass implements Dumpable { + SomeClass(DumpManager dumpManager) { + dumpManager.registerNormalDumpable(this); + } + + @Override + void dump(PrintWriter pw, String[] args) { + pw.println("testDump"); + } + } + """.trimIndent() + ), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun classIsDumpable_andRegisterCriticalIsCalled_noViolation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + import com.android.systemui.dump.DumpManager; + + public class SomeClass implements Dumpable { + SomeClass(DumpManager dumpManager) { + dumpManager.registerCriticalDumpable(this); + } + + @Override + void dump(PrintWriter pw, String[] args) { + pw.println("testDump"); + } + } + """.trimIndent() + ), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun classIsDumpable_noRegister_violation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + + public class SomeClass implements Dumpable { + @Override + public void dump() { + } + } + """ + ) + .indented(), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expect( + (""" + src/test/pkg/SomeClass.java:5: Warning: Any class implementing Dumpable must call DumpManager.registerNormalDumpable or DumpManager.registerCriticalDumpable [DumpableNotRegistered] + public class SomeClass implements Dumpable { + ~~~~~~~~~ + 0 errors, 1 warnings + """) + .trimIndent() + ) + } + + @Test + fun classIsDumpable_usesNotDumpManagerMethod_violation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + import com.android.systemui.OtherRegistrationObject; + + public class SomeClass implements Dumpable { + public SomeClass(OtherRegistrationObject otherRegistrationObject) { + otherRegistrationObject.registerDumpable(this); + } + @Override + public void dump() { + } + } + """ + ) + .indented(), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expect( + (""" + src/test/pkg/SomeClass.java:6: Warning: Any class implementing Dumpable must call DumpManager.registerNormalDumpable or DumpManager.registerCriticalDumpable [DumpableNotRegistered] + public class SomeClass implements Dumpable { + ~~~~~~~~~ + 0 errors, 1 warnings + """) + .trimIndent() + ) + } + + @Test + fun classIsDumpableAndCoreStartable_noRegister_noViolation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + import com.android.systemui.CoreStartable; + + public class SomeClass implements Dumpable, CoreStartable { + @Override + public void start() { + } + + @Override + public void dump() { + } + } + """ + ) + .indented(), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun classIsAbstract_noRegister_noViolation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + + public abstract class SomeClass implements Dumpable { + void abstractMethodHere(); + + @Override + public void dump() { + } + } + """ + ) + .indented(), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expectClean() + } + + companion object { + private val DUMPABLE_STUB = + TestFiles.java( + """ + package com.android.systemui; + + import com.android.systemui.dump.DumpManager; + import java.io.PrintWriter; + + public interface Dumpable { + void dump(); + } + """ + ) + .indented() + + private val DUMP_MANAGER_STUB = + TestFiles.java( + """ + package com.android.systemui.dump; + + public interface DumpManager { + void registerDumpable(Dumpable module); + void registerNormalDumpable(Dumpable module); + void registerCriticalDumpable(Dumpable module); + } + """ + ) + .indented() + + private val OTHER_REGISTRATION_OBJECT_STUB = + TestFiles.java( + """ + package com.android.systemui; + + public interface OtherRegistrationObject { + void registerDumpable(Dumpable module); + } + """ + ) + .indented() + + private val CORE_STARTABLE_STUB = + TestFiles.java( + """ + package com.android.systemui; + + public interface CoreStartable { + void start(); + } + """ + ) + .indented() + + private val stubs = + arrayOf( + DUMPABLE_STUB, + DUMP_MANAGER_STUB, + OTHER_REGISTRATION_OBJECT_STUB, + CORE_STARTABLE_STUB, + ) + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt b/packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt index a02954ab4800..08ab1462b161 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt @@ -78,11 +78,10 @@ interface SystemUiController { * Set the status bar color. * * @param color The **desired** [Color] to set. This may require modification if running on an - * API level that only supports white status bar icons. + * API level that only supports white status bar icons. * @param darkIcons Whether dark status bar icons would be preferable. * @param transformColorForLightContent A lambda which will be invoked to transform [color] if - * dark icons were requested but are not available. Defaults to applying a black scrim. - * + * dark icons were requested but are not available. Defaults to applying a black scrim. * @see statusBarDarkContentEnabled */ fun setStatusBarColor( @@ -95,16 +94,15 @@ interface SystemUiController { * Set the navigation bar color. * * @param color The **desired** [Color] to set. This may require modification if running on an - * API level that only supports white navigation bar icons. Additionally this will be ignored - * and [Color.Transparent] will be used on API 29+ where gesture navigation is preferred or the - * system UI automatically applies background protection in other navigation modes. + * API level that only supports white navigation bar icons. Additionally this will be ignored + * and [Color.Transparent] will be used on API 29+ where gesture navigation is preferred or + * the system UI automatically applies background protection in other navigation modes. * @param darkIcons Whether dark navigation bar icons would be preferable. * @param navigationBarContrastEnforced Whether the system should ensure that the navigation bar - * has enough contrast when a fully transparent background is requested. Only supported on API - * 29+. + * has enough contrast when a fully transparent background is requested. Only supported on API + * 29+. * @param transformColorForLightContent A lambda which will be invoked to transform [color] if - * dark icons were requested but are not available. Defaults to applying a black scrim. - * + * dark icons were requested but are not available. Defaults to applying a black scrim. * @see navigationBarDarkContentEnabled * @see navigationBarContrastEnforced */ diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt index cfc38df08b0a..d4a81f9c765d 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt @@ -255,7 +255,9 @@ fun Expandable( .onGloballyPositioned { controller.boundsInComposeViewRoot.value = it.boundsInRoot() } - ) { wrappedContent(controller.expandable) } + ) { + wrappedContent(controller.expandable) + } } else -> { val clickModifier = diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt index edb10c7d392f..767756e17747 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt @@ -156,9 +156,9 @@ internal class ExpandableControllerImpl( * Create a [LaunchAnimator.Controller] that is going to be used to drive an activity or dialog * animation. This controller will: * 1. Compute the start/end animation state using [boundsInComposeViewRoot] and the location of - * composeViewRoot on the screen. + * composeViewRoot on the screen. * 2. Update [animatorState] with the current animation state if we are animating, or null - * otherwise. + * otherwise. */ private fun launchController(): LaunchAnimator.Controller { return object : LaunchAnimator.Controller { diff --git a/packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt b/packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt index eb9d62506faa..a80a1f934dab 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt @@ -86,21 +86,20 @@ object PagerDefaults { /** * A horizontally scrolling layout that allows users to flip between items to the left and right. * - * @sample com.google.accompanist.sample.pager.HorizontalPagerSample - * * @param count the number of pages. * @param modifier the modifier to apply to this layout. * @param state the state object to be used to control or observe the pager's state. * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be - * composed from the end to the start and [PagerState.currentPage] == 0 will mean the first item is - * located at the end. + * composed from the end to the start and [PagerState.currentPage] == 0 will mean the first item + * is located at the end. * @param itemSpacing horizontal spacing to add between items. * @param flingBehavior logic describing fling behavior. * @param key the scroll position will be maintained based on the key, which means if you add/remove - * items before the current visible item the item with the given key will be kept as the first - * visible one. + * items before the current visible item the item with the given key will be kept as the first + * visible one. * @param content a block which describes the content. Inside this block you can reference - * [PagerScope.currentPage] and other properties in [PagerScope]. + * [PagerScope.currentPage] and other properties in [PagerScope]. + * @sample com.google.accompanist.sample.pager.HorizontalPagerSample */ @ExperimentalPagerApi @Composable @@ -134,21 +133,20 @@ fun HorizontalPager( /** * A vertically scrolling layout that allows users to flip between items to the top and bottom. * - * @sample com.google.accompanist.sample.pager.VerticalPagerSample - * * @param count the number of pages. * @param modifier the modifier to apply to this layout. * @param state the state object to be used to control or observe the pager's state. * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be - * composed from the bottom to the top and [PagerState.currentPage] == 0 will mean the first item is - * located at the bottom. + * composed from the bottom to the top and [PagerState.currentPage] == 0 will mean the first item + * is located at the bottom. * @param itemSpacing vertical spacing to add between items. * @param flingBehavior logic describing fling behavior. * @param key the scroll position will be maintained based on the key, which means if you add/remove - * items before the current visible item the item with the given key will be kept as the first - * visible one. + * items before the current visible item the item with the given key will be kept as the first + * visible one. * @param content a block which describes the content. Inside this block you can reference - * [PagerScope.currentPage] and other properties in [PagerScope]. + * [PagerScope.currentPage] and other properties in [PagerScope]. + * @sample com.google.accompanist.sample.pager.VerticalPagerSample */ @ExperimentalPagerApi @Composable @@ -246,7 +244,9 @@ internal fun Pager( // Constraint the content to be <= than the size of the pager. .fillParentMaxHeight() .wrapContentSize() - ) { pagerScope.content(page) } + ) { + pagerScope.content(page) + } } } } else { @@ -272,7 +272,9 @@ internal fun Pager( // Constraint the content to be <= than the size of the pager. .fillParentMaxWidth() .wrapContentSize() - ) { pagerScope.content(page) } + ) { + pagerScope.content(page) + } } } } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt b/packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt index 2e6ae78b43c7..1822a68f1e77 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt @@ -198,7 +198,7 @@ class PagerState( * * @param page the page to animate to. Must be between 0 and [pageCount] (inclusive). * @param pageOffset the percentage of the page width to offset, from the start of [page]. Must - * be in the range 0f..1f. + * be in the range 0f..1f. */ suspend fun animateScrollToPage( @IntRange(from = 0) page: Int, diff --git a/packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt b/packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt index 23122de56758..98140295306a 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt @@ -44,11 +44,11 @@ internal object SnappingFlingBehaviorDefaults { /** * Create and remember a snapping [FlingBehavior] to be used with [LazyListState]. * - * TODO: move this to a new module and make it public - * * @param lazyListState The [LazyListState] to update. * @param decayAnimationSpec The decay animation spec to use for decayed flings. * @param snapAnimationSpec The animation spec to use when snapping. + * + * TODO: move this to a new module and make it public */ @Composable internal fun rememberSnappingFlingBehavior( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt index 3eeadae5385f..a74e56b6e2f2 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt @@ -60,7 +60,7 @@ import com.android.systemui.people.ui.viewmodel.PeopleViewModel * * @param viewModel the [PeopleViewModel] that should be composed. * @param onResult the callback called with the result of this screen. Callers should usually finish - * the Activity/Fragment/View hosting this Composable once a result is available. + * the Activity/Fragment/View hosting this Composable once a result is available. */ @Composable fun PeopleScreen( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt index 3f590df697cb..0484ff475cdf 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt @@ -79,7 +79,9 @@ internal fun PeopleScreenEmpty( containerColor = androidColors.colorAccentPrimary, contentColor = androidColors.textColorOnAccent, ) - ) { Text(stringResource(R.string.got_it)) } + ) { + Text(stringResource(R.string.got_it)) + } } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index ab36d5899739..00c0a0b3e7b3 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.launch private val TAG = ClockRegistry::class.simpleName!! private const val DEBUG = true +private val KEY_TIMESTAMP = "appliedTimestamp" /** ClockRegistry aggregates providers and plugins */ open class ClockRegistry( @@ -134,9 +135,9 @@ open class ClockRegistry( assertNotMainThread() try { - value?._applied_timestamp = System.currentTimeMillis() - val json = ClockSettings.serialize(value) + value?.metadata?.put(KEY_TIMESTAMP, System.currentTimeMillis()) + val json = ClockSettings.serialize(value) if (handleAllUsers) { Settings.Secure.putStringForUser( context.contentResolver, @@ -172,7 +173,7 @@ open class ClockRegistry( clockChangeListeners.forEach(func) } - private fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) { + public fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) { scope.launch(bgDispatcher) { applySettings(mutator(settings ?: ClockSettings())) } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index 2a40f5c70420..4df7a44d3e1d 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt @@ -95,7 +95,7 @@ class DefaultClockController( open inner class DefaultClockFaceController( override val view: AnimatableClockView, - val seedColor: Int?, + var seedColor: Int?, ) : ClockFaceController { // MAGENTA is a placeholder, and will be assigned correctly in initialize @@ -111,9 +111,9 @@ class DefaultClockController( init { if (seedColor != null) { - currentColor = seedColor + currentColor = seedColor!! } - view.setColors(currentColor, currentColor) + view.setColors(DOZE_COLOR, currentColor) } override val events = @@ -141,7 +141,7 @@ class DefaultClockController( fun updateColor() { val color = if (seedColor != null) { - seedColor + seedColor!! } else if (isRegionDark) { resources.getColor(android.R.color.system_accent1_100) } else { @@ -194,6 +194,14 @@ class DefaultClockController( smallClock.updateColor() } + override fun onSeedColorChanged(seedColor: Int?) { + largeClock.seedColor = seedColor + smallClock.seedColor = seedColor + + largeClock.updateColor() + smallClock.updateColor() + } + override fun onLocaleChanged(locale: Locale) { val nf = NumberFormat.getInstance(locale) if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) { diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt index c120876a7a63..0d880759bd09 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt @@ -51,7 +51,7 @@ object CustomizationProviderContract { * * Supported operations: * - Query - to know which slots are available, query the [SlotTable.URI] [Uri]. The result - * set will contain rows with the [SlotTable.Columns] columns. + * set will contain rows with the [SlotTable.Columns] columns. */ object SlotTable { const val TABLE_NAME = "slots" @@ -74,8 +74,8 @@ object CustomizationProviderContract { * * Supported operations: * - Query - to know about all the affordances that are available on the device, regardless - * of which ones are currently selected, query the [AffordanceTable.URI] [Uri]. The result - * set will contain rows, each with the columns specified in [AffordanceTable.Columns]. + * of which ones are currently selected, query the [AffordanceTable.URI] [Uri]. The result + * set will contain rows, each with the columns specified in [AffordanceTable.Columns]. */ object AffordanceTable { const val TABLE_NAME = "affordances" @@ -128,14 +128,14 @@ object CustomizationProviderContract { * * Supported operations: * - Insert - to insert an affordance and place it in a slot, insert values for the columns - * into the [SelectionTable.URI] [Uri]. The maximum capacity rule is enforced by the system. - * Selecting a new affordance for a slot that is already full will automatically remove the - * oldest affordance from the slot. + * into the [SelectionTable.URI] [Uri]. The maximum capacity rule is enforced by the + * system. Selecting a new affordance for a slot that is already full will automatically + * remove the oldest affordance from the slot. * - Query - to know which affordances are set on which slots, query the - * [SelectionTable.URI] [Uri]. The result set will contain rows, each of which with the - * columns from [SelectionTable.Columns]. + * [SelectionTable.URI] [Uri]. The result set will contain rows, each of which with the + * columns from [SelectionTable.Columns]. * - Delete - to unselect an affordance, removing it from a slot, delete from the - * [SelectionTable.URI] [Uri], passing in values for each column. + * [SelectionTable.URI] [Uri], passing in values for each column. */ object SelectionTable { const val TABLE_NAME = "selections" @@ -160,7 +160,7 @@ object CustomizationProviderContract { * * Supported operations: * - Query - to know the values of flags, query the [FlagsTable.URI] [Uri]. The result set will - * contain rows, each of which with the columns from [FlagsTable.Columns]. + * contain rows, each of which with the columns from [FlagsTable.Columns]. */ object FlagsTable { const val TABLE_NAME = "flags" diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt index ec860b58bb4a..18753fd9c0c7 100644 --- a/packages/SystemUI/ktfmt_includes.txt +++ b/packages/SystemUI/ktfmt_includes.txt @@ -1,28 +1,20 @@ +packages/SystemUI -packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt -packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt --packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt -packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt --packages/SystemUI/checks/src/com/android/internal/systemui/lint/GetMainLooperViaContextDetector.kt -packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterReceiverViaContextDetector.kt -packages/SystemUI/checks/src/com/android/internal/systemui/lint/SoftwareBitmapDetector.kt --packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt --packages/SystemUI/checks/tests/com/android/systemui/lint/BindServiceViaContextDetectorTest.kt --packages/SystemUI/checks/tests/com/android/systemui/lint/BroadcastSentViaContextDetectorTest.kt --packages/SystemUI/checks/tests/com/android/systemui/lint/GetMainLooperViaContextDetectorTest.kt --packages/SystemUI/checks/tests/com/android/systemui/lint/RegisterReceiverViaContextDetectorTest.kt --packages/SystemUI/checks/tests/com/android/systemui/lint/SoftwareBitmapDetectorTest.kt +-packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt +-packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt -packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt --packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt +-packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/View.kt -packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt -packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt -packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt -packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt -packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt -packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt --packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt --packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt --packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +-packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt -packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonPositionCalculator.kt -packages/SystemUI/shared/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerManager.kt -packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt @@ -35,8 +27,6 @@ -packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt -packages/SystemUI/src/com/android/keyguard/ClockEventController.kt -packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt --packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt --packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt -packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt -packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt -packages/SystemUI/src/com/android/keyguard/clock/ClockPalette.kt @@ -65,12 +55,10 @@ -packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt -packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt -packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt --packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt -packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt -packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt -packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt -packages/SystemUI/src/com/android/systemui/biometrics/DwellRippleShader.kt --packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt @@ -80,7 +68,6 @@ -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt -packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt @@ -93,8 +80,6 @@ -packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt -packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt -packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt --packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt --packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt -packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt -packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt -packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt @@ -102,7 +87,6 @@ -packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt -packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt -packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt --packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt -packages/SystemUI/src/com/android/systemui/controls/controller/ControlInfo.kt -packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt -packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt @@ -132,6 +116,7 @@ -packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt -packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt -packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt +-packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt -packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt -packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt -packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt @@ -162,7 +147,6 @@ -packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt -packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt -packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt --packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt -packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt -packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt -packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt @@ -172,20 +156,16 @@ -packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt -packages/SystemUI/src/com/android/systemui/dump/LogBufferFreezer.kt -packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt +-packages/SystemUI/src/com/android/systemui/flags/Flags.kt -packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt -packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt -packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt --packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt +-packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt +-packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt +-packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt +-packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt -packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt --packages/SystemUI/src/com/android/systemui/log/LogLevel.kt --packages/SystemUI/src/com/android/systemui/log/LogMessage.kt --packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt --packages/SystemUI/src/com/android/systemui/log/LogcatEchoTracker.kt --packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt --packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerProd.kt --packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt -packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt --packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt -packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt -packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt -packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt @@ -202,9 +182,15 @@ -packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt -packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt -packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt +-packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt +-packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt +-packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt +-packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt +-packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt -packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt -packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt -packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt +-packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt -packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt -packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt -packages/SystemUI/src/com/android/systemui/privacy/MediaProjectionPrivacyItemMonitor.kt @@ -220,8 +206,6 @@ -packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt -packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt -packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt --packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt --packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt -packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt -packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt -packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt @@ -237,7 +221,6 @@ -packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt -packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialogEventLogger.kt -packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt --packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt -packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt -packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt -packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -245,10 +228,6 @@ -packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt -packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt -packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt --packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt --packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt --packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt --packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt -packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt -packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt -packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt @@ -259,23 +238,21 @@ -packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt -packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt -packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt --packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt -packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt -packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt -packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt -packages/SystemUI/src/com/android/systemui/settings/brightness/MirroredBrightnessController.kt -packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManager.kt -packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt --packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt -packages/SystemUI/src/com/android/systemui/shade/NPVCDownEventState.kt --packages/SystemUI/src/com/android/systemui/shade/NotifPanelEvents.kt -packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt -packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt -packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt +-packages/SystemUI/src/com/android/systemui/shade/ShadeHeightLogger.kt -packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt +-packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt -packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt -packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt --packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt -packages/SystemUI/src/com/android/systemui/smartspace/SmartspacePrecondition.kt -packages/SystemUI/src/com/android/systemui/smartspace/SmartspaceTargetFilter.kt -packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt @@ -284,9 +261,7 @@ -packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt -packages/SystemUI/src/com/android/systemui/statusbar/AbstractLockscreenShadeTransitionController.kt -packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarWifiView.kt -packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt --packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt -packages/SystemUI/src/com/android/systemui/statusbar/LockScreenShadeOverScroller.kt -packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt -packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt @@ -311,10 +286,12 @@ -packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt -packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt -packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +-packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt -packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt -packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt -packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt -packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt +-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt -packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt -packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt -packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt @@ -325,7 +302,6 @@ -packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt @@ -403,6 +379,7 @@ -packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt +-packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalker.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractor.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt @@ -444,13 +421,10 @@ -packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt -packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt -packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/ShadeStateListener.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt -packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt -packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt +-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/ConnectivitySlots.kt +-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt -packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt -packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt -packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt @@ -470,19 +444,17 @@ -packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt -packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt -packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt --packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt -packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt +-packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt -packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarRootView.kt -packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt -packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt -packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt +-packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt -packages/SystemUI/src/com/android/systemui/unfold/FoldStateLogger.kt -packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt --packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt -packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt -packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt --packages/SystemUI/src/com/android/systemui/user/UserCreator.kt --packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt -packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt -packages/SystemUI/src/com/android/systemui/user/UserSwitcherRootView.kt -packages/SystemUI/src/com/android/systemui/util/AsyncActivityLauncher.kt @@ -509,7 +481,6 @@ -packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt -packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt -packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt --packages/SystemUI/src/com/android/systemui/util/collection/RingBuffer.kt -packages/SystemUI/src/com/android/systemui/util/concurrency/Execution.kt -packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt -packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt @@ -517,6 +488,7 @@ -packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt -packages/SystemUI/src/com/android/systemui/util/kotlin/IpcSerializer.kt -packages/SystemUI/src/com/android/systemui/util/kotlin/nullability.kt +-packages/SystemUI/src/com/android/systemui/util/recycler/HorizontalSpacerItemDecoration.kt -packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt -packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt -packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialogReceiver.kt @@ -525,7 +497,6 @@ -packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt -packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt -packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt --packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt -packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt -packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt -packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt @@ -549,8 +520,6 @@ -packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt -packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt -packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt --packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt -packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt -packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt @@ -569,7 +538,6 @@ -packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt @@ -579,7 +547,6 @@ -packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt -packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt -packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt @@ -596,13 +563,17 @@ -packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt -packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt -packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt +-packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt +-packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt +-packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt -packages/SystemUI/tests/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt --packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt -packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt -packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt @@ -610,7 +581,6 @@ -packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt -packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt @@ -641,35 +611,28 @@ -packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt -packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt -packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt +-packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt -packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt -packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt --packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt -packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt --packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt --packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt --packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/shade/transition/SplitShadeOverScrollerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +-packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt -packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt -packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt -packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt +-packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -683,6 +646,7 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableStateTrackerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt @@ -719,7 +683,6 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt @@ -733,8 +696,7 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/ShadeExpansionStateManagerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt +-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt @@ -744,7 +706,6 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt @@ -753,40 +714,34 @@ -packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt --packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt --packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt -packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt -packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/ListenerSetTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt --packages/SystemUI/tests/src/com/android/systemui/util/collection/RingBufferTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt -packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt -packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt --packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt -packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt -packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt -packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt +-packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt index 314c736e2e44..db88b593e432 100644 --- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt +++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt @@ -171,7 +171,7 @@ enum class Style(internal val coreSpec: CoreSpec) { a1 = TonalSpec(HueSource(), ChromaConstant(36.0)), a2 = TonalSpec(HueSource(), ChromaConstant(16.0)), a3 = TonalSpec(HueAdd(60.0), ChromaConstant(24.0)), - n1 = TonalSpec(HueSource(), ChromaConstant(4.0)), + n1 = TonalSpec(HueSource(), ChromaConstant(6.0)), n2 = TonalSpec(HueSource(), ChromaConstant(8.0)) )), VIBRANT(CoreSpec( diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt index ab4aca569bd4..babe5700a01c 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt @@ -112,6 +112,9 @@ interface ClockEvents { /** Call whenever the color palette should update */ fun onColorPaletteChanged(resources: Resources) {} + /** Call if the seed color has changed and should be updated */ + fun onSeedColorChanged(seedColor: Int?) {} + /** Call whenever the weather data should update */ fun onWeatherDataChanged(data: WeatherData) {} } @@ -189,12 +192,13 @@ data class ClockSettings( val clockId: ClockId? = null, val seedColor: Int? = null, ) { - var _applied_timestamp: Long? = null + // Exclude metadata from equality checks + var metadata: JSONObject = JSONObject() companion object { private val KEY_CLOCK_ID = "clockId" private val KEY_SEED_COLOR = "seedColor" - private val KEY_TIMESTAMP = "_applied_timestamp" + private val KEY_METADATA = "metadata" fun serialize(setting: ClockSettings?): String { if (setting == null) { @@ -204,7 +208,7 @@ data class ClockSettings( return JSONObject() .put(KEY_CLOCK_ID, setting.clockId) .put(KEY_SEED_COLOR, setting.seedColor) - .put(KEY_TIMESTAMP, setting._applied_timestamp) + .put(KEY_METADATA, setting.metadata) .toString() } @@ -216,11 +220,11 @@ data class ClockSettings( val json = JSONObject(jsonStr) val result = ClockSettings( - json.getString(KEY_CLOCK_ID), + if (!json.isNull(KEY_CLOCK_ID)) json.getString(KEY_CLOCK_ID) else null, if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null ) - if (!json.isNull(KEY_TIMESTAMP)) { - result._applied_timestamp = json.getLong(KEY_TIMESTAMP) + if (!json.isNull(KEY_METADATA)) { + result.metadata = json.getJSONObject(KEY_METADATA) } return result } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt index e99b2149bc1d..3e34885a6d9c 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt @@ -35,7 +35,6 @@ import kotlin.math.max * as the result of taking a bug report). * * You can dump the entire buffer at any time by running: - * * ``` * $ adb shell dumpsys activity service com.android.systemui/.SystemUIService <bufferName> * ``` @@ -46,13 +45,11 @@ import kotlin.math.max * locally (usually for debugging purposes). * * To enable logcat echoing for an entire buffer: - * * ``` * $ adb shell settings put global systemui/buffer/<bufferName> <level> * ``` * * To enable logcat echoing for a specific tag: - * * ``` * $ adb shell settings put global systemui/tag/<tag> <level> * ``` @@ -64,10 +61,10 @@ import kotlin.math.max * LogBufferFactory. * * @param name The name of this buffer, printed when the buffer is dumped and in some other - * situations. + * situations. * @param maxSize The maximum number of messages to keep in memory at any one time. Buffers start - * out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches the - * maximum, it behaves like a ring buffer. + * out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches + * the maximum, it behaves like a ring buffer. */ class LogBuffer @JvmOverloads @@ -116,22 +113,22 @@ constructor( * initializer stored and converts it to a human-readable log message. * * @param tag A string of at most 23 characters, used for grouping logs into categories or - * subjects. If this message is echoed to logcat, this will be the tag that is used. + * subjects. If this message is echoed to logcat, this will be the tag that is used. * @param level Which level to log the message at, both to the buffer and to logcat if it's - * echoed. In general, a module should split most of its logs into either INFO or DEBUG level. - * INFO level should be reserved for information that other parts of the system might care - * about, leaving the specifics of code's day-to-day operations to DEBUG. + * echoed. In general, a module should split most of its logs into either INFO or DEBUG level. + * INFO level should be reserved for information that other parts of the system might care + * about, leaving the specifics of code's day-to-day operations to DEBUG. * @param messageInitializer A function that will be called immediately to store relevant data - * on the log message. The value of `this` will be the LogMessage to be initialized. + * on the log message. The value of `this` will be the LogMessage to be initialized. * @param messagePrinter A function that will be called if and when the message needs to be - * dumped to logcat or a bug report. It should read the data stored by the initializer and - * convert it to a human-readable string. The value of `this` will be the LogMessage to be - * printed. **IMPORTANT:** The printer should ONLY ever reference fields on the LogMessage and - * NEVER any variables in its enclosing scope. Otherwise, the runtime will need to allocate a - * new instance of the printer for each call, thwarting our attempts at avoiding any sort of - * allocation. + * dumped to logcat or a bug report. It should read the data stored by the initializer and + * convert it to a human-readable string. The value of `this` will be the LogMessage to be + * printed. **IMPORTANT:** The printer should ONLY ever reference fields on the LogMessage and + * NEVER any variables in its enclosing scope. Otherwise, the runtime will need to allocate a + * new instance of the printer for each call, thwarting our attempts at avoiding any sort of + * allocation. * @param exception Provide any exception that need to be logged. This is saved as - * [LogMessage.exception] + * [LogMessage.exception] */ @JvmOverloads inline fun log( diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt index faf1b78c598d..7a125ac14ea1 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt @@ -28,7 +28,6 @@ import android.provider.Settings * Version of [LogcatEchoTracker] for debuggable builds * * The log level of individual buffers or tags can be controlled via global settings: - * * ``` * # Echo any message to <bufferName> of <level> or higher * $ adb shell settings put global systemui/buffer/<bufferName> <level> diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/util/RingBuffer.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/util/RingBuffer.kt index 68d78907f028..4773f54a079e 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/util/RingBuffer.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/util/RingBuffer.kt @@ -30,7 +30,7 @@ import kotlin.math.max * * @param maxSize The maximum size the buffer can grow to before it begins functioning as a ring. * @param factory A function that creates a fresh instance of T. Used by the buffer while it's - * growing to [maxSize]. + * growing to [maxSize]. */ class RingBuffer<T>(private val maxSize: Int, private val factory: () -> T) : Iterable<T> { diff --git a/packages/SystemUI/res-keyguard/drawable-mdpi/ic_lockscreen_sim.png b/packages/SystemUI/res-keyguard/drawable-mdpi/ic_lockscreen_sim.png Binary files differdeleted file mode 100644 index 2e259c3e17c1..000000000000 --- a/packages/SystemUI/res-keyguard/drawable-mdpi/ic_lockscreen_sim.png +++ /dev/null diff --git a/packages/SystemUI/res-keyguard/drawable-xhdpi/ic_lockscreen_sim.png b/packages/SystemUI/res-keyguard/drawable-xhdpi/ic_lockscreen_sim.png Binary files differdeleted file mode 100644 index f4de96adae30..000000000000 --- a/packages/SystemUI/res-keyguard/drawable-xhdpi/ic_lockscreen_sim.png +++ /dev/null diff --git a/packages/SystemUI/res-keyguard/drawable-hdpi/ic_lockscreen_sim.png b/packages/SystemUI/res-keyguard/drawable/ic_lockscreen_sim.png Binary files differindex 7cf9e3699ceb..7cf9e3699ceb 100644 --- a/packages/SystemUI/res-keyguard/drawable-hdpi/ic_lockscreen_sim.png +++ b/packages/SystemUI/res-keyguard/drawable/ic_lockscreen_sim.png diff --git a/packages/SystemUI/res-keyguard/layout/fsi_chrome_view.xml b/packages/SystemUI/res-keyguard/layout/fsi_chrome_view.xml deleted file mode 100644 index 4ff2967b5ddf..000000000000 --- a/packages/SystemUI/res-keyguard/layout/fsi_chrome_view.xml +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<com.android.systemui.statusbar.notification.fsi.FsiChromeView android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_margin="50dp" - android:orientation="vertical" - xmlns:android="http://schemas.android.com/apk/res/android"> - - <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:id="@+id/fsi_chrome" - android:layout_height="50dp" - android:orientation="horizontal"> - - <ImageView - android:id="@+id/fsi_app_icon" - android:layout_width="50dp" - android:layout_height="match_parent" - android:contentDescription="@null" /> - - <TextView - android:id="@+id/fsi_app_name" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:padding="10dp" - android:textSize="22dp" - android:gravity="center" - android:textColor="#FFFFFF" - android:text="AppName" /> - - <Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1" /> - - <Button - android:id="@+id/fsi_fullscreen_button" - android:layout_width="100dp" - android:layout_height="match_parent" - android:text="fullscreen" /> - - <Button - android:id="@+id/fsi_dismiss_button" - android:layout_width="100dp" - android:layout_height="match_parent" - android:text="dismiss" /> - - </LinearLayout> - -</com.android.systemui.statusbar.notification.fsi.FsiChromeView>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml b/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml index 411fea5dd22d..48769fdebd99 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml @@ -14,10 +14,12 @@ ~ limitations under the License --> -<merge xmlns:android="http://schemas.android.com/apk/res/android"> +<merge xmlns:android="http://schemas.android.com/apk/res/android" > <TextView android:id="@+id/digit_text" style="@style/Widget.TextView.NumPadKey.Digit" + android:autoSizeMaxTextSize="32sp" + android:autoSizeTextType="uniform" android:layout_width="wrap_content" android:layout_height="wrap_content" /> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml index 7db0fe908ec0..728d861ab693 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?><!-- ** ** Copyright 2012, The Android Open Source Project ** @@ -17,185 +16,185 @@ */ --> <!-- This is the SIM PIN view that allows the user to enter a SIM PIN to unlock the device. --> -<com.android.keyguard.KeyguardSimPinView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/res-auto" - android:id="@+id/keyguard_sim_pin_view" - android:orientation="vertical" +<com.android.keyguard.KeyguardSimPinView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/res-auto" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/keyguard_sim_pin_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + androidprv:layout_maxWidth="@dimen/keyguard_security_width" + android:layout_gravity="center_horizontal|bottom"> + <include layout="@layout/keyguard_bouncer_message_area"/> + + <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" - android:layout_height="match_parent" - androidprv:layout_maxWidth="@dimen/keyguard_security_width" - android:layout_gravity="center_horizontal|bottom"> - <include layout="@layout/keyguard_bouncer_message_area" /> - <Space - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_weight="1" /> - <ImageView - android:id="@+id/keyguard_sim" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:tint="@color/background_protected" - android:src="@drawable/ic_lockscreen_sim"/> - <LinearLayout + android:layout_height="0dp" + android:layout_weight="1" + android:layoutDirection="ltr"> + <LinearLayout + android:id="@+id/pin_area" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:gravity="center" - android:layoutDirection="ltr" - > - <include layout="@layout/keyguard_esim_area" - android:id="@+id/keyguard_esim_area" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - <RelativeLayout - android:id="@+id/row0" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingBottom="4dp" - > + android:gravity="center_horizontal" + android:paddingTop="@dimen/num_pad_entry_row_margin_bottom" + android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom" + androidprv:layout_constraintBottom_toTopOf="@+id/flow1" + androidprv:layout_constraintEnd_toEndOf="parent" + androidprv:layout_constraintStart_toStartOf="parent" + androidprv:layout_constraintTop_toTopOf="parent"> + + <ImageView + android:id="@+id/keyguard_sim" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_gravity="center_horizontal" + android:src="@drawable/ic_lockscreen_sim" + app:tint="@color/background_protected" /> + + <include + android:id="@+id/keyguard_esim_area" + layout="@layout/keyguard_esim_area" + android:layout_gravity="center_horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + <com.android.keyguard.PasswordTextView android:id="@+id/simPinEntry" style="@style/Widget.TextView.Password" android:layout_width="@dimen/keyguard_security_width" android:layout_height="@dimen/keyguard_password_height" - android:layout_centerHorizontal="true" - android:layout_marginRight="72dp" android:contentDescription="@string/keyguard_accessibility_sim_pin_area" - android:gravity="center" - androidprv:scaledTextSize="@integer/scaled_password_text_size" /> - </RelativeLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" android:layout_gravity="center_horizontal" - android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" - > - <com.android.keyguard.NumPadKey - android:id="@+id/key1" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="1" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key2" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="2" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key3" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="3" - /> - </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_gravity="center_horizontal" - android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" - > - <com.android.keyguard.NumPadKey - android:id="@+id/key4" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="4" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key5" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="5" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key6" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="6" - /> - </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_gravity="center_horizontal" - android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" - > - <com.android.keyguard.NumPadKey - android:id="@+id/key7" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="7" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key8" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="8" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key9" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="9" - /> - </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_gravity="center_horizontal" - > - <com.android.keyguard.NumPadButton - android:id="@+id/delete_button" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - android:contentDescription="@string/keyboardview_keycode_delete" - style="@style/NumPadKey.Delete" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key0" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="0" - /> - <com.android.keyguard.NumPadButton - android:id="@+id/key_enter" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - style="@style/NumPadKey.Enter" - android:contentDescription="@string/keyboardview_keycode_enter" - /> + androidprv:scaledTextSize="@integer/scaled_password_text_size" /> </LinearLayout> - </LinearLayout> - <include layout="@layout/keyguard_eca" - android:id="@+id/keyguard_selector_fade_container" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_gravity="bottom|center_horizontal" - android:layout_marginTop="@dimen/keyguard_eca_top_margin" - android:layout_marginBottom="2dp" - android:gravity="center_horizontal"/> + + <androidx.constraintlayout.helper.widget.Flow + android:id="@+id/flow1" + android:layout_width="0dp" + android:layout_height="0dp" + android:clipChildren="false" + android:clipToPadding="false" + android:orientation="horizontal" + androidprv:constraint_referenced_ids="key1,key2,key3,key4,key5,key6,key7,key8,key9,delete_button,key0,key_enter" + androidprv:flow_horizontalGap="@dimen/num_pad_key_margin_end" + androidprv:flow_horizontalStyle="packed" + androidprv:flow_maxElementsWrap="3" + androidprv:flow_verticalBias="1.0" + androidprv:flow_verticalGap="@dimen/num_pad_entry_row_margin_bottom" + androidprv:flow_verticalStyle="packed" + androidprv:flow_wrapMode="aligned" + androidprv:layout_constraintBottom_toBottomOf="parent" + androidprv:layout_constraintEnd_toEndOf="parent" + androidprv:layout_constraintStart_toStartOf="parent" + androidprv:layout_constraintTop_toBottomOf="@id/pin_area" /> + + <com.android.keyguard.NumPadButton + android:id="@+id/delete_button" + style="@style/NumPadKey.Delete" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key0" + android:contentDescription="@string/keyboardview_keycode_delete" /> + + <com.android.keyguard.NumPadButton + android:id="@+id/key_enter" + style="@style/NumPadKey.Enter" + android:layout_width="0dp" + android:layout_height="0dp" + android:contentDescription="@string/keyboardview_keycode_enter" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key1" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key2" + androidprv:digit="1" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key2" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key3" + androidprv:digit="2" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key3" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key4" + androidprv:digit="3" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key4" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key5" + androidprv:digit="4" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key5" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key6" + androidprv:digit="5" + androidprv:textView="@+id/simPinEntry" /> + + + <com.android.keyguard.NumPadKey + android:id="@+id/key6" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key7" + androidprv:digit="6" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key7" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key8" + androidprv:digit="7" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key8" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key9" + androidprv:digit="8" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key9" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/delete_button" + androidprv:digit="9" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key0" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key_enter" + androidprv:digit="0" + androidprv:textView="@+id/simPinEntry" /> + </androidx.constraintlayout.widget.ConstraintLayout> + + <include + android:id="@+id/keyguard_selector_fade_container" + layout="@layout/keyguard_eca" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom|center_horizontal" + android:layout_marginBottom="2dp" + android:layout_marginTop="@dimen/keyguard_eca_top_margin" + android:gravity="center_horizontal" + android:orientation="vertical" /> </com.android.keyguard.KeyguardSimPinView> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml index 422bd4c12e8e..7e24d1231aee 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml @@ -21,6 +21,7 @@ <com.android.keyguard.KeyguardSimPukView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/res-auto" + xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/keyguard_sim_puk_view" android:orientation="vertical" android:layout_width="match_parent" @@ -29,173 +30,165 @@ android:layout_gravity="center_horizontal|bottom"> <include layout="@layout/keyguard_bouncer_message_area"/> - <Space + <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="0dp" - android:layout_weight="1" /> - - <ImageView - android:id="@+id/keyguard_sim" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:tint="@color/background_protected" - android:src="@drawable/ic_lockscreen_sim"/> - - <LinearLayout + android:layout_weight="1" + android:layoutDirection="ltr"> + <LinearLayout + android:id="@+id/pin_area" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:gravity="center" - android:layoutDirection="ltr" - > - <include layout="@layout/keyguard_esim_area" - android:id="@+id/keyguard_esim_area" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - - <RelativeLayout - android:id="@+id/row0" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingBottom="4dp" - > + android:gravity="center_horizontal" + android:paddingTop="@dimen/num_pad_entry_row_margin_bottom" + android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom" + androidprv:layout_constraintBottom_toTopOf="@+id/flow1" + androidprv:layout_constraintEnd_toEndOf="parent" + androidprv:layout_constraintStart_toStartOf="parent" + androidprv:layout_constraintTop_toTopOf="parent"> + + <ImageView + android:id="@+id/keyguard_sim" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_gravity="center_horizontal" + android:src="@drawable/ic_lockscreen_sim" + app:tint="@color/background_protected" /> + + <include + android:id="@+id/keyguard_esim_area" + layout="@layout/keyguard_esim_area" + android:layout_gravity="center_horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> <com.android.keyguard.PasswordTextView android:id="@+id/pukEntry" style="@style/Widget.TextView.Password" android:layout_width="@dimen/keyguard_security_width" android:layout_height="@dimen/keyguard_password_height" - android:layout_centerHorizontal="true" - android:layout_marginRight="72dp" - android:contentDescription="@string/keyguard_accessibility_sim_puk_area" - android:gravity="center" - androidprv:scaledTextSize="@integer/scaled_password_text_size" /> - </RelativeLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_gravity="center_horizontal" - android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" - > - <com.android.keyguard.NumPadKey - android:id="@+id/key1" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="1" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key2" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="2" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key3" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - androidprv:textView="@+id/pukEntry" - androidprv:digit="3" - /> - </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_gravity="center_horizontal" - android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" - - > - <com.android.keyguard.NumPadKey - android:id="@+id/key4" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="4" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key5" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="5" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key6" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - androidprv:textView="@+id/pukEntry" - androidprv:digit="6" - /> - </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_gravity="center_horizontal" - android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" - > - <com.android.keyguard.NumPadKey - android:id="@+id/key7" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="7" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key8" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="8" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key9" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - androidprv:textView="@+id/pukEntry" - androidprv:digit="9" - /> - </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" + android:contentDescription="@string/keyguard_accessibility_sim_pin_area" android:layout_gravity="center_horizontal" - > - <com.android.keyguard.NumPadButton - android:id="@+id/delete_button" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - android:contentDescription="@string/keyboardview_keycode_delete" - style="@style/NumPadKey.Delete" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key0" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="0" - /> - <com.android.keyguard.NumPadButton - android:id="@+id/key_enter" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - style="@style/NumPadKey.Enter" - android:contentDescription="@string/keyboardview_keycode_enter" - /> + androidprv:scaledTextSize="@integer/scaled_password_text_size" /> </LinearLayout> - </LinearLayout> + + <androidx.constraintlayout.helper.widget.Flow + android:id="@+id/flow1" + android:layout_width="0dp" + android:layout_height="0dp" + android:clipChildren="false" + android:clipToPadding="false" + android:orientation="horizontal" + androidprv:constraint_referenced_ids="key1,key2,key3,key4,key5,key6,key7,key8,key9,delete_button,key0,key_enter" + androidprv:flow_horizontalGap="@dimen/num_pad_key_margin_end" + androidprv:flow_horizontalStyle="packed" + androidprv:flow_maxElementsWrap="3" + androidprv:flow_verticalBias="1.0" + androidprv:flow_verticalGap="@dimen/num_pad_entry_row_margin_bottom" + androidprv:flow_verticalStyle="packed" + androidprv:flow_wrapMode="aligned" + androidprv:layout_constraintBottom_toBottomOf="parent" + androidprv:layout_constraintEnd_toEndOf="parent" + androidprv:layout_constraintStart_toStartOf="parent" + androidprv:layout_constraintTop_toBottomOf="@id/pin_area" /> + + <com.android.keyguard.NumPadButton + android:id="@+id/delete_button" + style="@style/NumPadKey.Delete" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key0" + android:contentDescription="@string/keyboardview_keycode_delete" /> + + <com.android.keyguard.NumPadButton + android:id="@+id/key_enter" + style="@style/NumPadKey.Enter" + android:layout_width="0dp" + android:layout_height="0dp" + android:contentDescription="@string/keyboardview_keycode_enter" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key1" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key2" + androidprv:digit="1" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key2" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key3" + androidprv:digit="2" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key3" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key4" + androidprv:digit="3" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key4" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key5" + androidprv:digit="4" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key5" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key6" + androidprv:digit="5" + androidprv:textView="@+id/pukEntry" /> + + + <com.android.keyguard.NumPadKey + android:id="@+id/key6" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key7" + androidprv:digit="6" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key7" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key8" + androidprv:digit="7" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key8" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key9" + androidprv:digit="8" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key9" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/delete_button" + androidprv:digit="9" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key0" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key_enter" + androidprv:digit="0" + androidprv:textView="@+id/pukEntry" /> + </androidx.constraintlayout.widget.ConstraintLayout> <include layout="@layout/keyguard_eca" android:id="@+id/keyguard_selector_fade_container" diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml index ce88d5b2f60f..6a18b3dcfd82 100644 --- a/packages/SystemUI/res-keyguard/values-eu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml @@ -31,8 +31,7 @@ <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bizkor kargatzen"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mantso kargatzen"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kargatzea optimizatu da bateria ez kaltetzeko"</string> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (5712938022567388098) --> - <skip /> + <string name="keyguard_plugged_in_incompatible_charger" msgid="5712938022567388098">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kargagailua ez da bateragarria"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Desblokeatzeko, sakatu Menua."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sarea blokeatuta dago"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ez dago SIMik"</string> diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml index 80bd0bfaec42..2f295f745549 100644 --- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml @@ -31,8 +31,7 @@ <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"En recharge rapide : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"En recharge lente : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge optimisée pour protéger la pile"</string> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (5712938022567388098) --> - <skip /> + <string name="keyguard_plugged_in_incompatible_charger" msgid="5712938022567388098">"<xliff:g id="PERCENTAGE">%s</xliff:g> • chargeur incompatible"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur la touche Menu pour déverrouiller l\'appareil."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Aucune carte SIM"</string> diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml index b2058a182344..9dd5c5d24ee7 100644 --- a/packages/SystemUI/res-keyguard/values-ko/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml @@ -31,8 +31,7 @@ <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 고속 충전 중"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 저속 충전 중"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 배터리 보호를 위해 충전 최적화됨"</string> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (5712938022567388098) --> - <skip /> + <string name="keyguard_plugged_in_incompatible_charger" msgid="5712938022567388098">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 호환되지 않는 충전기"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"잠금 해제하려면 메뉴를 누르세요."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"네트워크 잠김"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM 없음"</string> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index b6a78f56ec5f..caf32331b81f 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -82,7 +82,7 @@ <!-- The vertical margin between the date and the owner info. --> <!-- The translation for disappearing security views after having solved them. --> - <dimen name="disappear_y_translation">-32dp</dimen> + <dimen name="disappear_y_translation">-50dp</dimen> <!-- Dimens for animation for the Bouncer PIN view --> <dimen name="pin_view_trans_y_entry">120dp</dimen> diff --git a/packages/SystemUI/res/drawable/controls_panel_background.xml b/packages/SystemUI/res/drawable/controls_panel_background.xml index 9092877fc6fa..fc108a5e1e06 100644 --- a/packages/SystemUI/res/drawable/controls_panel_background.xml +++ b/packages/SystemUI/res/drawable/controls_panel_background.xml @@ -18,5 +18,5 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#1F1F1F" /> - <corners android:radius="@dimen/notification_corner_radius" /> + <corners android:radius="@dimen/controls_panel_corner_radius" /> </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_footer_edit_circle.xml b/packages/SystemUI/res/drawable/qs_footer_edit_circle.xml new file mode 100644 index 000000000000..2e29cae0ca4f --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_footer_edit_circle.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<inset xmlns:android="http://schemas.android.com/apk/res/android" + android:inset="@dimen/qs_footer_action_inset"> + <ripple + android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask"> + <!-- We make this shape a rounded rectangle instead of a oval so that it can animate --> + <!-- properly into an app/dialog. --> + <shape android:shape="rectangle"> + <solid android:color="@android:color/white"/> + <corners android:radius="@dimen/qs_footer_action_corner_radius"/> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/qs_footer_action_corner_radius"/> + </shape> + </item> + + </ripple> +</inset>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/controls_fullscreen.xml b/packages/SystemUI/res/layout/controls_fullscreen.xml index e08e63b39e59..fa703038cc7e 100644 --- a/packages/SystemUI/res/layout/controls_fullscreen.xml +++ b/packages/SystemUI/res/layout/controls_fullscreen.xml @@ -15,19 +15,11 @@ limitations under the License. --> -<FrameLayout +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/control_detail_root" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> - - <LinearLayout - android:id="@+id/global_actions_controls" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:paddingHorizontal="@dimen/controls_padding_horizontal" /> - -</FrameLayout> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml index aa211bf8cfdc..71561c07ebd3 100644 --- a/packages/SystemUI/res/layout/controls_with_favorites.xml +++ b/packages/SystemUI/res/layout/controls_with_favorites.xml @@ -13,82 +13,94 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<merge - xmlns:android="http://schemas.android.com/apk/res/android"> +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:orientation="vertical" + tools:parentTag="android.widget.LinearLayout"> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginBottom="@dimen/controls_header_bottom_margin"> - - <!-- make sure the header stays centered in the layout by adding a spacer --> - <Space - android:id="@+id/controls_spacer" - android:layout_width="@dimen/controls_header_menu_size" - android:layout_height="1dp" - android:visibility="gone" /> - - <ImageView - android:id="@+id/controls_close" - android:contentDescription="@string/accessibility_desc_close" - android:src="@drawable/ic_close" - android:background="?android:attr/selectableItemBackgroundBorderless" - android:tint="@color/control_primary_text" - android:layout_width="@dimen/controls_header_menu_size" - android:layout_height="@dimen/controls_header_menu_size" - android:padding="12dp" - android:visibility="gone" /> - <!-- need to keep this outer view in order to have a correctly sized anchor - for the dropdown menu, as well as dropdown background in the right place --> <LinearLayout - android:id="@+id/controls_header" - android:orientation="horizontal" - android:layout_width="0dp" - android:layout_weight="1" - android:minHeight="48dp" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center" - android:gravity="center"> - <TextView - style="@style/Control.Spinner.Header" - android:clickable="false" - android:id="@+id/app_or_structure_spinner" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" /> + android:paddingHorizontal="@dimen/controls_header_horizontal_padding" + android:layout_marginBottom="@dimen/controls_header_bottom_margin" + android:orientation="horizontal"> + + <!-- make sure the header stays centered in the layout by adding a spacer --> + <Space + android:id="@+id/controls_spacer" + android:layout_width="@dimen/controls_header_menu_button_size" + android:layout_height="1dp" + android:visibility="gone" /> + + <ImageView + android:id="@+id/controls_close" + android:layout_width="@dimen/controls_header_menu_button_size" + android:layout_height="@dimen/controls_header_menu_button_size" + android:layout_gravity="center_vertical" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/accessibility_desc_close" + android:padding="12dp" + android:src="@drawable/ic_close" + android:tint="@color/control_primary_text" + android:visibility="gone" + tools:visibility="visible" /> + + <!-- need to keep this outer view in order to have a correctly sized anchor + for the dropdown menu, as well as dropdown background in the right place --> + <LinearLayout + android:id="@+id/controls_header" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_weight="1" + android:gravity="center" + android:minHeight="48dp" + android:orientation="horizontal"> + + <TextView + android:id="@+id/app_or_structure_spinner" + style="@style/Control.Spinner.Header" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:clickable="false" + tools:text="Test app" /> + </LinearLayout> + + <ImageView + android:id="@+id/controls_more" + android:layout_width="@dimen/controls_header_menu_button_size" + android:layout_height="@dimen/controls_header_menu_button_size" + android:layout_gravity="center_vertical" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/accessibility_menu" + android:padding="12dp" + android:src="@drawable/ic_more_vert" + android:tint="@color/control_more_vert" /> </LinearLayout> - <ImageView - android:id="@+id/controls_more" - android:src="@drawable/ic_more_vert" - android:layout_width="@dimen/controls_header_menu_size" - android:layout_height="@dimen/controls_header_menu_size" - android:padding="12dp" - android:tint="@color/control_more_vert" - android:layout_gravity="center" - android:contentDescription="@string/accessibility_menu" - android:background="?android:attr/selectableItemBackgroundBorderless" /> - </LinearLayout> - <ScrollView + <ScrollView android:id="@+id/controls_scroll_view" android:layout_width="match_parent" android:layout_height="0dp" + android:layout_marginHorizontal="@dimen/controls_content_margin_horizontal" android:layout_weight="1" - android:orientation="vertical" android:clipChildren="true" + android:orientation="vertical" android:paddingHorizontal="16dp" android:scrollbars="none"> - <include layout="@layout/global_actions_controls_list_view" /> - </ScrollView> + <include layout="@layout/global_actions_controls_list_view" /> + + </ScrollView> - <FrameLayout - android:id="@+id/controls_panel" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_weight="1" - android:background="@drawable/controls_panel_background" - android:visibility="gone" - /> + <FrameLayout + android:id="@+id/controls_panel" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_marginHorizontal="@dimen/controls_content_margin_horizontal" + android:layout_weight="1" + android:background="@drawable/controls_panel_background" + android:visibility="gone" + tools:visibility="visible" /> </merge> diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index b1d3ed05333b..745cfc6c1655 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -64,7 +64,7 @@ android:layout_width="@dimen/qs_footer_action_button_size" android:layout_height="@dimen/qs_footer_action_button_size" android:layout_gravity="center_vertical|end" - android:background="?android:attr/selectableItemBackground" + android:background="@drawable/qs_footer_edit_circle" android:clickable="true" android:contentDescription="@string/accessibility_quick_settings_edit" android:focusable="true" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index be9ef9debc82..f26542690047 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Kennisgewingskerm."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Vinnige instellings."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Kitsinstellings en kennisgewingskerm."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Sluitskerm."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Werksluitskerm"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Maak toe"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 03925f72a8fb..4ec413e3a9a5 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"مركز الإشعارات."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"الإعدادات السريعة."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"\"الإعدادات السريعة\" و\"مركز الإشعارات\""</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"شاشة القفل."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"شاشة قفل بيانات العمل"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"إغلاق"</string> @@ -695,7 +694,7 @@ <string name="right_keycode" msgid="2480715509844798438">"رمز مفتاح اليمين"</string> <string name="left_icon" msgid="5036278531966897006">"رمز اليسار"</string> <string name="right_icon" msgid="1103955040645237425">"رمز اليمين"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"اضغط باستمرار مع السحب لإضافة الميزات."</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"اضغط باستمرار مع السحب لإضافة المربّعات"</string> <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"اضغط باستمرار مع السحب لإعادة ترتيب الميزات."</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"اسحب هنا للإزالة"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"الحدّ الأدنى من عدد المربعات الذي تحتاج إليه هو <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"التبديل إلى الملف الشخصي للعمل"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"إغلاق"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"إعدادات شاشة القفل"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"لا يتوفّر اتصال Wi-Fi."</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"استخدام الكاميرا محظور."</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"استخدام الكاميرا والميكروفون محظور."</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"استخدام الميكروفون محظور."</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"وضع الأولوية مفعّل."</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"ميزة لفت انتباه \"مساعد Google\" مفعّلة."</string> </resources> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 9507cdaf7f4f..ae2f1e2286b3 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"জাননী পেনেল।"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ক্ষিপ্ৰ ছেটিং।"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"ক্ষিপ্ৰ ছেটিং জাননী পেনেল।"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"বন্ধ স্ক্ৰীন।"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"কৰ্মস্থানৰ প্ৰ\'ফাইলৰ লক স্ক্ৰীন"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"বন্ধ কৰক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index fcee2e14cd9e..33496748d751 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Bildiriş kölgəsi."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Tez ayarlar."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Sürətli ayarlar və Bildiriş göstərişi."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Kilid ekranı."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ekran kilidi"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Qapadın"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 12ce25951383..dbfb012319b8 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Цень апавяшчэння.."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Хуткія налады."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Хуткія налады і шчыток апавяшчэнняў."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Экран блакіроўкі."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Экран блакіроўкі дзейнасці"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Закрыць"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Пераключыцца на працоўны профіль"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Закрыць"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Налады экрана блакіроўкі"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Сетка Wi-Fi недаступная"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камера заблакіравана"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Камера і мікрафон заблакіраваны"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Мікрафон заблакіраваны"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Прыярытэтны рэжым уключаны"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Памочнік гатовы выконваць каманды"</string> </resources> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 9ba041eae4d2..e87b628ae8b3 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Падащ панел с известия."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Бързи настройки."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Падащ панел с бързи настройки и известия."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Заключване на екрана."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Заключен екран на служебния профил"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Затваряне"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index dbc3a52b8151..2a84e59006ed 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"বিজ্ঞপ্তি শেড৷"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"দ্রুত সেটিংস৷"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"দ্রুত সেটিংস এবং বিজ্ঞপ্তি শেড।"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"লক স্ক্রিন।"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"কর্মস্থলের স্ক্রিন লক"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"বন্ধ করুন"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"অফিস প্রোফাইলে পাল্টে নিন"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"বন্ধ করুন"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"লক স্ক্রিন সেটিংস"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"ওয়াই-ফাই উপলভ্য নয়"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"ক্যামেরার অ্যাক্সেস ব্লক করা আছে"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"ক্যামেরা এবং মাইক্রোফোনের অ্যাক্সেস ব্লক করা আছে"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"মাইক্রোফোনের অ্যাক্সেস ব্লক করা আছে"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"\'প্রায়োরিটি\' মোড চালু করা আছে"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"অ্যাসিস্ট্যান্ট আপনার কথা শোনার জন্য চালু করা আছে"</string> </resources> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 8d4a0dd9b42b..e73eab959822 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -198,7 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Obavještenja sa sjenčenjem."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Brze postavke."</string> - <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Brze postavke i zaslon obavijesti."</string> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Brze postavke i lokacija za obavještenja."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Zaključan ekran."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Zaključan ekran radnog profila"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Zatvori"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index cc878950c0d3..66d20e4889d0 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Àrea de notificacions"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Configuració ràpida"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Configuració ràpida i àrea de notificacions."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Pantalla de bloqueig"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Pantalla de bloqueig per a la feina"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Tanca"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Canvia al perfil de treball"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Tanca"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Configuració pantalla de bloqueig"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"No hi ha cap Wi‑Fi disponible"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"La càmera està bloquejada"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"La càmera i el micròfon estan bloquejats"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"El micròfon està bloquejat"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"El mode Prioritat està activat"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"L\'Assistent està activat"</string> </resources> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index e07a89215df3..e61a4d0ae672 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Panel oznámení."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Rychlé nastavení."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Rychlé nastavení a panel oznámení"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Obrazovka uzamčení"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Obrazovka uzamčení pracovního profilu"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Zavřít"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 4b10ddfade91..aa6a480373e2 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Notifikationspanel."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Kvikmenu."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Kvikmenu og notifikationspanel."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Låseskærm."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Låseskærm til arbejde"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Luk"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Skift til arbejdsprofil"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Luk"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Indstillinger for låseskærm"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi er ikke tilgængeligt"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kameraet er blokeret"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Der er blokeret for kameraet og mikrofonen"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofonen er blokeret"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Prioritetstilstand er aktiveret"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Assistent lytter"</string> </resources> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index f8c02e3c496a..621788aa993c 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Benachrichtigungsleiste"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Schnelleinstellungen"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Schnelleinstellungen und Benachrichtigungsleiste."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Sperrbildschirm"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Sperrbildschirm für Arbeitsprofil"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Schließen"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index f2fb5b89955b..d81cedb2e400 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Πλαίσιο σκίασης ειδοποιήσεων."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Γρήγορες ρυθμίσεις."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Γρήγορες ρυθμίσεις και πλαίσιο σκίασης ειδοποιήσεων."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Οθόνη κλειδώματος"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Οθόνη κλειδωμένης εργασίας"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Κλείσιμο"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 81682b583904..44b56de971f3 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Pantalla de notificaciones"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Configuración rápida"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Configuración rápida y panel de notificaciones."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Pantalla de bloqueo"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Pantalla bloqueada del perfil de trabajo"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Cerrar"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Cambiar al perfil de trabajo"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Cerrar"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Config. de pantalla de bloqueo"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi no disponible"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"La cámara está bloqueada"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"La cámara y el micrófono están bloqueados"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"El micrófono está bloqueado"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"El modo de prioridad está activado"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Asistente está prestando atención"</string> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 526743ae0ed8..9080c5869c53 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Pantalla de notificaciones"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Ajustes rápidos"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Ajustes rápidos y pantalla de notificaciones."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Pantalla de bloqueo."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Pantalla de bloqueo para el perfil de trabajo"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Cerrar"</string> @@ -1019,7 +1018,7 @@ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string> <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string> <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string> - <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectada temporalmente"</string> + <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectado temporalmente"</string> <string name="mobile_data_poor_connection" msgid="819617772268371434">"Conexión inestable"</string> <string name="mobile_data_off_summary" msgid="3663995422004150567">"Los datos móviles no se conectarán automáticamente"</string> <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sin conexión"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Cambiar al perfil de trabajo"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Cerrar"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Ajustes de pantalla de bloqueo"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Red Wi-Fi no disponible"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Cámara bloqueada"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Cámara y micrófono bloqueados"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Micrófono bloqueado"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Modo Prioridad activado"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"El Asistente está activado"</string> </resources> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 9388a14295c9..9ce7dbf74354 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Märguande vari."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Kiirseaded."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Kiirseaded ja märguandeala."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Kuva lukustamine."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Töö lukustuskuva"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Sulgemine"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Lülitu tööprofiilile"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Sule"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Lukustuskuva seaded"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"WiFi pole saadaval"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kaamera on blokeeritud"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kaamera ja mikrofon on blokeeritud"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon on blokeeritud"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Prioriteetne režiim on sisse lülitatud"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Assistent on aktiveeritud"</string> </resources> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 84f2d0cf394c..d37afbabb58c 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -616,84 +616,48 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Jakinarazpenak"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Lasterbideak"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Aldatu tekl. diseinua"</string> - <!-- no translation found for keyboard_shortcut_clear_text (4679927133259287577) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_list_title (1156178106617830429) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_list_hint (5982623262974326746) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_list_no_result (6819302191660875501) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_category_system (1151182120757052669) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_category_input (5440558509904296233) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_category_open_apps (1450959949739257562) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_category_current_app (2011953559133734491) --> - <skip /> - <!-- no translation found for group_system_access_notification_shade (7116898151485382275) --> - <skip /> - <!-- no translation found for group_system_full_screenshot (7389040853798023211) --> - <skip /> - <!-- no translation found for group_system_access_system_app_shortcuts (4421497579210445641) --> - <skip /> - <!-- no translation found for group_system_go_back (8838454003680364227) --> - <skip /> - <!-- no translation found for group_system_access_home_screen (1857344316928441909) --> - <skip /> - <!-- no translation found for group_system_overview_open_apps (6897128761003265350) --> - <skip /> - <!-- no translation found for group_system_cycle_forward (9202444850838205990) --> - <skip /> - <!-- no translation found for group_system_cycle_back (5163464503638229131) --> - <skip /> - <!-- no translation found for group_system_access_all_apps_search (488070738028991753) --> - <skip /> - <!-- no translation found for group_system_hide_reshow_taskbar (3809304065624351131) --> - <skip /> - <!-- no translation found for group_system_access_system_settings (7961639365383008053) --> - <skip /> - <!-- no translation found for group_system_access_google_assistant (1186152943161483864) --> - <skip /> - <!-- no translation found for group_system_lock_screen (7391191300363416543) --> - <skip /> - <!-- no translation found for group_system_quick_memo (2914234890158583919) --> - <skip /> - <!-- no translation found for keyboard_shortcut_group_system_multitasking (1065232949510862593) --> - <skip /> - <!-- no translation found for system_multitasking_rhs (6593269428880305699) --> - <skip /> - <!-- no translation found for system_multitasking_lhs (8839380725557952846) --> - <skip /> - <!-- no translation found for system_multitasking_full_screen (1962084334200006297) --> - <skip /> - <!-- no translation found for system_multitasking_replace (844285282472557186) --> - <skip /> - <!-- no translation found for keyboard_shortcut_group_input (6888282716546625610) --> - <skip /> - <!-- no translation found for input_switch_input_language_next (3394291576873633793) --> - <skip /> - <!-- no translation found for input_switch_input_language_previous (8823659252918609216) --> - <skip /> - <!-- no translation found for input_access_emoji (8105642858900406351) --> - <skip /> - <!-- no translation found for input_access_voice_typing (7291201476395326141) --> - <skip /> + <string name="keyboard_shortcut_clear_text" msgid="4679927133259287577">"Garbitu testua"</string> + <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Lasterbideak"</string> + <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Bilatu lasterbideak"</string> + <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ez da aurkitu lasterbiderik"</string> + <string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string> + <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Sarrera"</string> + <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Irekitako aplikazioak"</string> + <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Uneko aplikazioa"</string> + <string name="group_system_access_notification_shade" msgid="7116898151485382275">"Atzitu jakinarazpenen panela"</string> + <string name="group_system_full_screenshot" msgid="7389040853798023211">"Atera pantaila osoaren argazki bat"</string> + <string name="group_system_access_system_app_shortcuts" msgid="4421497579210445641">"Atzitu sistemaren edo aplikazioetarako lasterbideen zerrenda"</string> + <string name="group_system_go_back" msgid="8838454003680364227">"Atzera: itzuli aurreko egoerara (atzera egiteko botoia)"</string> + <string name="group_system_access_home_screen" msgid="1857344316928441909">"Atzitu hasierako pantaila"</string> + <string name="group_system_overview_open_apps" msgid="6897128761003265350">"Ikusi irekitako aplikazioen ikuspegi orokorra"</string> + <string name="group_system_cycle_forward" msgid="9202444850838205990">"Joan azken aplikazioetako batetik bestera (aurrera)"</string> + <string name="group_system_cycle_back" msgid="5163464503638229131">"Joan azken aplikazioetako batetik bestera (atzera)"</string> + <string name="group_system_access_all_apps_search" msgid="488070738028991753">"Atzitu aplikazio guztien zerrenda eta bilatu (adibidez, bilatzeko aukeraren edo Exekutatzeko tresna aplikazioaren bidez)"</string> + <string name="group_system_hide_reshow_taskbar" msgid="3809304065624351131">"Ezkutatu eta erakutsi (berriro) zereginen barra"</string> + <string name="group_system_access_system_settings" msgid="7961639365383008053">"Atzitu sistemaren ezarpenak"</string> + <string name="group_system_access_google_assistant" msgid="1186152943161483864">"Atzitu Google-ren Laguntzailea"</string> + <string name="group_system_lock_screen" msgid="7391191300363416543">"Blokeatu pantaila"</string> + <string name="group_system_quick_memo" msgid="2914234890158583919">"Ireki Oharrak aplikazioa oharrak bizkor idazteko"</string> + <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Zereginen aldibereko sistemaren exekuzioa"</string> + <string name="system_multitasking_rhs" msgid="6593269428880305699">"Sartu pantaila zatituaren eskuineko aldean uneko aplikazioarekin"</string> + <string name="system_multitasking_lhs" msgid="8839380725557952846">"Sartu pantaila zatituaren ezkerreko aldean uneko aplikazioarekin"</string> + <string name="system_multitasking_full_screen" msgid="1962084334200006297">"Aldatu pantaila zatitutik pantaila osora"</string> + <string name="system_multitasking_replace" msgid="844285282472557186">"Pantaila zatituan zaudela: ordeztu aplikazio bat beste batekin"</string> + <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Sarrera"</string> + <string name="input_switch_input_language_next" msgid="3394291576873633793">"Aldatu idazteko hizkuntza (hurrengo hizkuntza)"</string> + <string name="input_switch_input_language_previous" msgid="8823659252918609216">"Aldatu idazteko hizkuntza (aurreko hizkuntza)"</string> + <string name="input_access_emoji" msgid="8105642858900406351">"Atzitu emojiak"</string> + <string name="input_access_voice_typing" msgid="7291201476395326141">"Atzitu ahozko idazketa"</string> <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Aplikazioak"</string> <string name="keyboard_shortcut_group_applications_assist" msgid="771606231466098742">"Laguntzailea"</string> - <!-- no translation found for keyboard_shortcut_group_applications_browser (7328131901589876868) --> - <skip /> + <string name="keyboard_shortcut_group_applications_browser" msgid="7328131901589876868">"Ireki arakatzailea (Chrome, modu lehenetsian)"</string> <string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"Kontaktuak"</string> - <!-- no translation found for keyboard_shortcut_group_applications_email (7480359963463803511) --> - <skip /> + <string name="keyboard_shortcut_group_applications_email" msgid="7480359963463803511">"Ireki posta elektronikoa (Gmail, modu lehenetsian)"</string> <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMSak"</string> <string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"Musika"</string> <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string> - <!-- no translation found for keyboard_shortcut_group_applications_calculator (6316043911946540137) --> - <skip /> - <!-- no translation found for keyboard_shortcut_group_applications_maps (7312554713993114342) --> - <skip /> + <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Ireki Kalkulagailua"</string> + <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Ireki Maps"</string> <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ez molestatzeko modua"</string> <string name="volume_dnd_silent" msgid="4154597281458298093">"Bolumen-botoietarako lasterbidea"</string> <string name="battery" msgid="769686279459897127">"Bateria"</string> @@ -856,12 +820,9 @@ <string name="privacy_type_media_projection" msgid="8136723828804251547">"pantaila-grabaketa"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Ez du izenik"</string> <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Egonean"</string> - <!-- no translation found for font_scaling_dialog_title (6273107303850248375) --> - <skip /> - <!-- no translation found for font_scaling_smaller (1012032217622008232) --> - <skip /> - <!-- no translation found for font_scaling_larger (5476242157436806760) --> - <skip /> + <string name="font_scaling_dialog_title" msgid="6273107303850248375">"Letraren tamaina"</string> + <string name="font_scaling_smaller" msgid="1012032217622008232">"Txikitu"</string> + <string name="font_scaling_larger" msgid="5476242157436806760">"Handitu"</string> <string name="magnification_window_title" msgid="4863914360847258333">"Lupa-leihoa"</string> <string name="magnification_controls_title" msgid="8421106606708891519">"Lupa-leihoaren aukerak"</string> <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Handitu"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index bafee640bcff..9bc513e80856 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"مجموعه اعلان."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"تنظیمات سریع."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"تنظیمات فوری و کشوی اعلانات."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"صفحه قفل."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"صفحه قفل کاری"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"بستن"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 4097e76699c6..4c3962e249bc 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Ilmoitusalue."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Pika-asetukset."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Pika-asetukset ja ilmoitusalue"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Lukitse näyttö."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Työlukitusnäyttö"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Sulje"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 47974b254362..05dec17d3b3b 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -616,84 +616,48 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notifications"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Raccourcis clavier"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Changer la disposition du clavier"</string> - <!-- no translation found for keyboard_shortcut_clear_text (4679927133259287577) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_list_title (1156178106617830429) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_list_hint (5982623262974326746) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_list_no_result (6819302191660875501) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_category_system (1151182120757052669) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_category_input (5440558509904296233) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_category_open_apps (1450959949739257562) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_category_current_app (2011953559133734491) --> - <skip /> - <!-- no translation found for group_system_access_notification_shade (7116898151485382275) --> - <skip /> - <!-- no translation found for group_system_full_screenshot (7389040853798023211) --> - <skip /> - <!-- no translation found for group_system_access_system_app_shortcuts (4421497579210445641) --> - <skip /> - <!-- no translation found for group_system_go_back (8838454003680364227) --> - <skip /> - <!-- no translation found for group_system_access_home_screen (1857344316928441909) --> - <skip /> - <!-- no translation found for group_system_overview_open_apps (6897128761003265350) --> - <skip /> - <!-- no translation found for group_system_cycle_forward (9202444850838205990) --> - <skip /> - <!-- no translation found for group_system_cycle_back (5163464503638229131) --> - <skip /> - <!-- no translation found for group_system_access_all_apps_search (488070738028991753) --> - <skip /> - <!-- no translation found for group_system_hide_reshow_taskbar (3809304065624351131) --> - <skip /> - <!-- no translation found for group_system_access_system_settings (7961639365383008053) --> - <skip /> - <!-- no translation found for group_system_access_google_assistant (1186152943161483864) --> - <skip /> - <!-- no translation found for group_system_lock_screen (7391191300363416543) --> - <skip /> - <!-- no translation found for group_system_quick_memo (2914234890158583919) --> - <skip /> - <!-- no translation found for keyboard_shortcut_group_system_multitasking (1065232949510862593) --> - <skip /> - <!-- no translation found for system_multitasking_rhs (6593269428880305699) --> - <skip /> - <!-- no translation found for system_multitasking_lhs (8839380725557952846) --> - <skip /> - <!-- no translation found for system_multitasking_full_screen (1962084334200006297) --> - <skip /> - <!-- no translation found for system_multitasking_replace (844285282472557186) --> - <skip /> - <!-- no translation found for keyboard_shortcut_group_input (6888282716546625610) --> - <skip /> - <!-- no translation found for input_switch_input_language_next (3394291576873633793) --> - <skip /> - <!-- no translation found for input_switch_input_language_previous (8823659252918609216) --> - <skip /> - <!-- no translation found for input_access_emoji (8105642858900406351) --> - <skip /> - <!-- no translation found for input_access_voice_typing (7291201476395326141) --> - <skip /> + <string name="keyboard_shortcut_clear_text" msgid="4679927133259287577">"Effacer le texte"</string> + <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Raccourcis"</string> + <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Recherchez des raccourcis"</string> + <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Aucun raccourci trouvé"</string> + <string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Système"</string> + <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Entrée"</string> + <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Ouvrir applis"</string> + <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Appli actuelle"</string> + <string name="group_system_access_notification_shade" msgid="7116898151485382275">"Accéder au volet de notification"</string> + <string name="group_system_full_screenshot" msgid="7389040853798023211">"Prendre une capture d\'écran complète"</string> + <string name="group_system_access_system_app_shortcuts" msgid="4421497579210445641">"Accéder à la liste des raccourcis du système/des applications"</string> + <string name="group_system_go_back" msgid="8838454003680364227">"Retour : retour à l\'état précédent (bouton précédent)"</string> + <string name="group_system_access_home_screen" msgid="1857344316928441909">"Accéder à l\'écran d\'accueil"</string> + <string name="group_system_overview_open_apps" msgid="6897128761003265350">"Aperçu des applications ouvertes"</string> + <string name="group_system_cycle_forward" msgid="9202444850838205990">"Parcourir les applications récentes (avancer)"</string> + <string name="group_system_cycle_back" msgid="5163464503638229131">"Parcourir les applications récentes (retour)"</string> + <string name="group_system_access_all_apps_search" msgid="488070738028991753">"Accéder à la liste des applis et à la recherche (recherche/lanceur)"</string> + <string name="group_system_hide_reshow_taskbar" msgid="3809304065624351131">"Masquer et (ré)afficher la barre des tâches"</string> + <string name="group_system_access_system_settings" msgid="7961639365383008053">"Accéder aux paramètres système"</string> + <string name="group_system_access_google_assistant" msgid="1186152943161483864">"Accéder à l\'Assistant Google"</string> + <string name="group_system_lock_screen" msgid="7391191300363416543">"Écran de verrouillage"</string> + <string name="group_system_quick_memo" msgid="2914234890158583919">"Ouvrir l\'application de prise de notes pour prendre des notes rapides"</string> + <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitâche du système"</string> + <string name="system_multitasking_rhs" msgid="6593269428880305699">"Passer à l\'écran partagé avec l\'application actuelle à droite"</string> + <string name="system_multitasking_lhs" msgid="8839380725557952846">"Passer à l\'écran partagé avec l\'application actuelle à gauche"</string> + <string name="system_multitasking_full_screen" msgid="1962084334200006297">"Passer de l\'écran partagé au plein écran"</string> + <string name="system_multitasking_replace" msgid="844285282472557186">"En mode d\'écran partagé : remplacer une application par une autre"</string> + <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrée"</string> + <string name="input_switch_input_language_next" msgid="3394291576873633793">"Changer la langue d\'entrée (langue suivante)"</string> + <string name="input_switch_input_language_previous" msgid="8823659252918609216">"Changer la langue d\'entrée (langue précédente)"</string> + <string name="input_access_emoji" msgid="8105642858900406351">"Accéder aux émojis"</string> + <string name="input_access_voice_typing" msgid="7291201476395326141">"Accéder à l\'entrée vocale"</string> <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Applications"</string> <string name="keyboard_shortcut_group_applications_assist" msgid="771606231466098742">"Assistance"</string> - <!-- no translation found for keyboard_shortcut_group_applications_browser (7328131901589876868) --> - <skip /> + <string name="keyboard_shortcut_group_applications_browser" msgid="7328131901589876868">"Navigateur (Chrome par défaut)"</string> <string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"Contacts"</string> - <!-- no translation found for keyboard_shortcut_group_applications_email (7480359963463803511) --> - <skip /> + <string name="keyboard_shortcut_group_applications_email" msgid="7480359963463803511">"Courriel (Gmail par défaut)"</string> <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"Messages texte"</string> <string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"Musique"</string> <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Agenda"</string> - <!-- no translation found for keyboard_shortcut_group_applications_calculator (6316043911946540137) --> - <skip /> - <!-- no translation found for keyboard_shortcut_group_applications_maps (7312554713993114342) --> - <skip /> + <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculatrice"</string> + <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string> <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ne pas déranger"</string> <string name="volume_dnd_silent" msgid="4154597281458298093">"Raccourci des boutons de volume"</string> <string name="battery" msgid="769686279459897127">"Pile"</string> @@ -856,12 +820,9 @@ <string name="privacy_type_media_projection" msgid="8136723828804251547">"enregistrement d\'écran"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Sans titre"</string> <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Veille"</string> - <!-- no translation found for font_scaling_dialog_title (6273107303850248375) --> - <skip /> - <!-- no translation found for font_scaling_smaller (1012032217622008232) --> - <skip /> - <!-- no translation found for font_scaling_larger (5476242157436806760) --> - <skip /> + <string name="font_scaling_dialog_title" msgid="6273107303850248375">"Taille de police"</string> + <string name="font_scaling_smaller" msgid="1012032217622008232">"Rapetisser"</string> + <string name="font_scaling_larger" msgid="5476242157436806760">"Agrandir"</string> <string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string> <string name="magnification_controls_title" msgid="8421106606708891519">"Commandes pour la fenêtre d\'agrandissement"</string> <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Effectuer un zoom avant"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 7e2badad012d..0e089c893eaf 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Volet des notifications"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Réglages rapides"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Réglages rapides et volet des notifications."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Écran de verrouillage"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Écran de verrouillage du profil professionnel"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Fermer"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Passer au profil professionnel"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Fermer"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Paramètres écran de verrouillage"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi non disponible"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Caméra bloquée"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Caméra et micro bloqués"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Micro bloqué"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Mode Prioritaire activé"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Assistant à l\'écoute"</string> </resources> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 8ff9b06349af..ea532f5f82af 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Panel despregable"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Configuración rápida"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Configuración rápida e panel despregable."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Pantalla de bloqueo."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Pantalla de bloqueo do perfil de traballo"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Pechar"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Cambiar ao perfil de traballo"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Pechar"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Configuración pantalla bloqueo"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wifi non dispoñible"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"A cámara está bloqueada"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"A cámara e o micrófono están bloqueados"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"O micrófono está bloqueado"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"O modo de prioridade está activado"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"A atención do Asistente está activada"</string> </resources> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index d1422ba1874d..3f2b494bc4b2 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"નોટિફિકેશન શેડ."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ઝડપી સેટિંગ."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"ઝડપી સેટિંગ અને નોટિફિકેશન શેડ."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"લૉક સ્ક્રીન."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"કાર્ય લૉક સ્ક્રીન"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"બંધ કરો"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"ઑફિસની પ્રોફાઇલ પર સ્વિચ કરો"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"બંધ કરો"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"લૉક સ્ક્રીનના સેટિંગ"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"વાઇ-ફાઇ ઉપલબ્ધ નથી"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"કૅમેરા બ્લૉક કરેલો છે"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"કૅમેરા અને માઇક્રોફોન બ્લૉક કરેલા છે"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"માઇક્રોફોન બ્લૉક કરેલો છે"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"પ્રાધાન્યતા મોડ ચાલુ છે"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Assistant સક્રિય છે"</string> </resources> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 0d08dcd0a7e2..c49861bf4f70 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"सूचना शेड."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"त्वरित सेटिंग."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"क्विक सेटिंग और नोटिफ़िकेशन शेड."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"लॉक स्क्रीन."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"वर्क लॉक स्क्रीन"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"बंद करें"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index fadc88d1cf8f..b9c606724ccf 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Értesítési felület."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Gyorsbeállítások."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Gyorsbeállítások és értesítési terület"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Lezárási képernyő."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Munka lezárási képernyővel"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Bezárás"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Váltás munkaprofilra"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Bezárás"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Lezárási képernyő beállításai"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Nem áll rendelkezésre Wi-Fi"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera letiltva"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera és mikrofon letiltva"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon letiltva"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Prioritás mód bekapcsolva"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"A Segéd figyel"</string> </resources> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 22877cf4c4c4..018120a7f28a 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Ծանուցումների վահանակ:"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Արագ կարգավորումներ:"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Արագ կարգավորումներ և ծանուցումների վահանակ։"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Էկրանի կողպում:"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Աշխատանքային պրոֆիլի կողպէկրան"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Փակել"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Անցնել աշխատանքային պրոֆիլ"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Փակել"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Կողպէկրանի կարգավորումներ"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi ցանց հասանելի չէ"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Տեսախցիկն արգելափակված է"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Տեսախցիկն ու խոսափողը արգելափակված են"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Խոսափողն արգելափակված է"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Առաջնահերթության ռեժիմը միացված է"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Օգնականը լսում է"</string> </resources> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 262d0e1bc17f..d76d2ffcfc0d 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Bayangan pemberitahuan."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Setelan cepat."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Setelan cepat dan Menu notifikasi."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Layar kunci."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Layar kunci kantor"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Tutup"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Beralih ke profil kerja"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Tutup"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Setelan layar kunci"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi tidak tersedia"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera diblokir"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera dan mikrofon diblokir"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon diblokir"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Mode prioritas diaktifkan"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Asisten sedang memerhatikan"</string> </resources> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 7bdc66c9bc21..228052440538 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Tilkynningasvæði."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Flýtistillingar."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Flýtistillingar og tilkynningagluggi."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Lásskjár."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Vinnulásskjár"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Loka"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Skipta yfir í vinnusnið"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Loka"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Stillingar fyrir lásskjá"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi er ekki til staðar"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Lokað fyrir myndavél"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Lokað fyrir myndavél og hljóðnema"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Lokað fyrir hljóðnema"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Kveikt er á forgangsstillingu"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Hjálparinn er að hlusta"</string> </resources> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index bc983ffb07fe..873138209b9d 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -1110,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Passa a profilo di lavoro"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Chiudi"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Impostazioni schermata di blocco"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi non disponibile"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Videocamera bloccata"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Videocamera e microfono bloccati"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Microfono bloccato"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Modalità priorità attivata"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"L\'assistente è attivo"</string> </resources> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 859435f1b92a..8946db2bea31 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"לוח התראות."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"הגדרות מהירות."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"הגדרות מהירות ולוח ההתראות."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"מסך נעילה."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"מסך נעילה של עבודה"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"סגירה"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index ca29664f9c5e..d5b96d5f5fea 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"通知シェード"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"クイック設定"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"クイック設定と通知シェード。"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ロック画面"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"仕事用プロファイルのロック画面"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"閉じる"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 4e40a737c359..54101b13de95 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"შეტყობინებების ფარდა"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"სწრაფი პარამეტრები"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"სწრაფი პარამეტრები და შეტყობინებების ფარდა"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ეკრანის დაბლოკვა."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"სამსახურის ჩაკეტილი ეკრანი"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"დახურვა"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 459237bbf514..dce96820cbd2 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Хабарландыру тақтасы"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Жылдам параметрлер."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Жылдам параметрлер мен хабарландыру тақтасы."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Бекіту экраны."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Әрекетті құлыптау экраны"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Жабу"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Жұмыс профиліне ауысу"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Жабу"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Экран құлпының параметрлері"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi қолжетімсіз."</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камера бөгелген."</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Камера мен микрофон бөгелген."</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Микрофон бөгелген."</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"\"Маңызды\" режимі қосулы."</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Assistant қосулы."</string> </resources> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index f3452837d934..ef20217fee53 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"ពណ៌ការជូនដំណឹង"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ការកំណត់រហ័ស។"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"ការកំណត់រហ័ស និងផ្ទាំងជូនដំណឹង។"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ចាក់សោអេក្រង់។"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"អេក្រង់ចាក់សោលក្ខណៈការងារ"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"បិទ"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 6e22f3659eb8..c830e847923a 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"ಅಧಿಸೂಚನೆಯ ಛಾಯೆ."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳು."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಅಧಿಸೂಚನೆಯ ಪರದೆ."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ಲಾಕ್ ಸ್ಕ್ರೀನ್."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ಕೆಲಸದ ಲಾಕ್ ಪರದೆ"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"ಮುಚ್ಚು"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index f7a6dfbf2191..b33168f9e0ea 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"알림 세부정보"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"빠른 설정"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"빠른 설정 및 알림 창입니다."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"화면을 잠급니다."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"업무용 잠금 화면"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"닫기"</string> @@ -616,84 +615,48 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"알림"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"단축키"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"키보드 레이아웃 전환"</string> - <!-- no translation found for keyboard_shortcut_clear_text (4679927133259287577) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_list_title (1156178106617830429) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_list_hint (5982623262974326746) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_list_no_result (6819302191660875501) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_category_system (1151182120757052669) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_category_input (5440558509904296233) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_category_open_apps (1450959949739257562) --> - <skip /> - <!-- no translation found for keyboard_shortcut_search_category_current_app (2011953559133734491) --> - <skip /> - <!-- no translation found for group_system_access_notification_shade (7116898151485382275) --> - <skip /> - <!-- no translation found for group_system_full_screenshot (7389040853798023211) --> - <skip /> - <!-- no translation found for group_system_access_system_app_shortcuts (4421497579210445641) --> - <skip /> - <!-- no translation found for group_system_go_back (8838454003680364227) --> - <skip /> - <!-- no translation found for group_system_access_home_screen (1857344316928441909) --> - <skip /> - <!-- no translation found for group_system_overview_open_apps (6897128761003265350) --> - <skip /> - <!-- no translation found for group_system_cycle_forward (9202444850838205990) --> - <skip /> - <!-- no translation found for group_system_cycle_back (5163464503638229131) --> - <skip /> - <!-- no translation found for group_system_access_all_apps_search (488070738028991753) --> - <skip /> - <!-- no translation found for group_system_hide_reshow_taskbar (3809304065624351131) --> - <skip /> - <!-- no translation found for group_system_access_system_settings (7961639365383008053) --> - <skip /> - <!-- no translation found for group_system_access_google_assistant (1186152943161483864) --> - <skip /> - <!-- no translation found for group_system_lock_screen (7391191300363416543) --> - <skip /> - <!-- no translation found for group_system_quick_memo (2914234890158583919) --> - <skip /> - <!-- no translation found for keyboard_shortcut_group_system_multitasking (1065232949510862593) --> - <skip /> - <!-- no translation found for system_multitasking_rhs (6593269428880305699) --> - <skip /> - <!-- no translation found for system_multitasking_lhs (8839380725557952846) --> - <skip /> - <!-- no translation found for system_multitasking_full_screen (1962084334200006297) --> - <skip /> - <!-- no translation found for system_multitasking_replace (844285282472557186) --> - <skip /> - <!-- no translation found for keyboard_shortcut_group_input (6888282716546625610) --> - <skip /> - <!-- no translation found for input_switch_input_language_next (3394291576873633793) --> - <skip /> - <!-- no translation found for input_switch_input_language_previous (8823659252918609216) --> - <skip /> - <!-- no translation found for input_access_emoji (8105642858900406351) --> - <skip /> - <!-- no translation found for input_access_voice_typing (7291201476395326141) --> - <skip /> + <string name="keyboard_shortcut_clear_text" msgid="4679927133259287577">"텍스트 삭제"</string> + <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"단축키"</string> + <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"단축키 검색"</string> + <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"단축키 없음"</string> + <string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"시스템"</string> + <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"입력"</string> + <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"열린 앱"</string> + <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"현재 앱"</string> + <string name="group_system_access_notification_shade" msgid="7116898151485382275">"알림 창에 액세스"</string> + <string name="group_system_full_screenshot" msgid="7389040853798023211">"전체 스크린샷 촬영"</string> + <string name="group_system_access_system_app_shortcuts" msgid="4421497579210445641">"시스템/앱 단축키 목록에 액세스"</string> + <string name="group_system_go_back" msgid="8838454003680364227">"뒤로: 이전 상태로 되돌아가기(뒤로 버튼)"</string> + <string name="group_system_access_home_screen" msgid="1857344316928441909">"홈 화면에 액세스"</string> + <string name="group_system_overview_open_apps" msgid="6897128761003265350">"열린 앱 개요"</string> + <string name="group_system_cycle_forward" msgid="9202444850838205990">"최근 앱 간 순환(앞으로)"</string> + <string name="group_system_cycle_back" msgid="5163464503638229131">"최근 앱 간 순환(뒤로)"</string> + <string name="group_system_access_all_apps_search" msgid="488070738028991753">"모든 앱 및 검색 목록(예: 검색/런처)에 액세스"</string> + <string name="group_system_hide_reshow_taskbar" msgid="3809304065624351131">"태스크 바 숨김 및 다시 표시"</string> + <string name="group_system_access_system_settings" msgid="7961639365383008053">"시스템 설정에 액세스"</string> + <string name="group_system_access_google_assistant" msgid="1186152943161483864">"Google 어시스턴트에 액세스"</string> + <string name="group_system_lock_screen" msgid="7391191300363416543">"잠금 화면"</string> + <string name="group_system_quick_memo" msgid="2914234890158583919">"빠른 메모를 위해 노트 앱 불러오기"</string> + <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"시스템 멀티태스킹"</string> + <string name="system_multitasking_rhs" msgid="6593269428880305699">"현재 앱을 오른쪽으로 보내는 화면 분할 입력"</string> + <string name="system_multitasking_lhs" msgid="8839380725557952846">"현재 앱을 왼쪽으로 보내는 화면 분할 입력"</string> + <string name="system_multitasking_full_screen" msgid="1962084334200006297">"화면 분할에서 전체 화면으로 전환"</string> + <string name="system_multitasking_replace" msgid="844285282472557186">"화면 분할 중: 다른 앱으로 바꾸기"</string> + <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"입력"</string> + <string name="input_switch_input_language_next" msgid="3394291576873633793">"입력 언어 전환(다음 언어)"</string> + <string name="input_switch_input_language_previous" msgid="8823659252918609216">"입력 언어 전환(이전 언어)"</string> + <string name="input_access_emoji" msgid="8105642858900406351">"이모티콘에 액세스"</string> + <string name="input_access_voice_typing" msgid="7291201476395326141">"음성 입력에 액세스"</string> <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"애플리케이션"</string> <string name="keyboard_shortcut_group_applications_assist" msgid="771606231466098742">"지원"</string> - <!-- no translation found for keyboard_shortcut_group_applications_browser (7328131901589876868) --> - <skip /> + <string name="keyboard_shortcut_group_applications_browser" msgid="7328131901589876868">"브라우저(Chrome을 기본값으로 설정)"</string> <string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"연락처"</string> - <!-- no translation found for keyboard_shortcut_group_applications_email (7480359963463803511) --> - <skip /> + <string name="keyboard_shortcut_group_applications_email" msgid="7480359963463803511">"이메일(Gmail을 기본값으로 설정)"</string> <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS"</string> <string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"음악"</string> <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"캘린더"</string> - <!-- no translation found for keyboard_shortcut_group_applications_calculator (6316043911946540137) --> - <skip /> - <!-- no translation found for keyboard_shortcut_group_applications_maps (7312554713993114342) --> - <skip /> + <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"계산기"</string> + <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"지도"</string> <string name="volume_and_do_not_disturb" msgid="502044092739382832">"방해 금지 모드"</string> <string name="volume_dnd_silent" msgid="4154597281458298093">"볼륨 버튼 단축키"</string> <string name="battery" msgid="769686279459897127">"배터리"</string> @@ -856,12 +819,9 @@ <string name="privacy_type_media_projection" msgid="8136723828804251547">"화면 녹화"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"제목 없음"</string> <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"대기"</string> - <!-- no translation found for font_scaling_dialog_title (6273107303850248375) --> - <skip /> - <!-- no translation found for font_scaling_smaller (1012032217622008232) --> - <skip /> - <!-- no translation found for font_scaling_larger (5476242157436806760) --> - <skip /> + <string name="font_scaling_dialog_title" msgid="6273107303850248375">"글꼴 크기"</string> + <string name="font_scaling_smaller" msgid="1012032217622008232">"축소"</string> + <string name="font_scaling_larger" msgid="5476242157436806760">"확대"</string> <string name="magnification_window_title" msgid="4863914360847258333">"확대 창"</string> <string name="magnification_controls_title" msgid="8421106606708891519">"확대 창 컨트롤"</string> <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"확대"</string> @@ -1150,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"직장 프로필로 전환"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"닫기"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"잠금 화면 설정"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi를 사용할 수 없음"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"카메라 차단됨"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"카메라 및 마이크 차단됨"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"마이크 차단됨"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"우선순위 모드 설정됨"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"어시스턴트가 대기 중임"</string> </resources> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 3d6a94954113..d50e19743e05 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Билдирмелер тактасы."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Тез тууралоолор."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Ыкчам параметрлер жана билдирмелер тактасы."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Кулпуланган экран."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Жумуштун кулпуланган экраны"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Жабуу"</string> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index bc63c9f46a5b..4f38e6058723 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -64,5 +64,6 @@ <dimen name="qs_panel_padding_top">@dimen/qqs_layout_margin_top</dimen> - <dimen name="controls_padding_horizontal">16dp</dimen> + <dimen name="controls_header_horizontal_padding">12dp</dimen> + <dimen name="controls_content_margin_horizontal">16dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 0b5b5de89bfd..41610493d10e 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"ໜ້າຈໍແຈ້ງເຕືອນ."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ການຕັ້ງຄ່າດ່ວນ."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"ການຕັ້ງຄ່າດ່ວນ ແລະ ເງົາການແຈ້ງເຕືອນ."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ລັອກໜ້າຈໍ."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ໜ້າຈໍລັອກວຽກ"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"ປິດ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index d607b178b1e0..b37478943665 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Pranešimų gaubtas."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Spartieji nustatymai."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Spartieji nustatymai ir pranešimų skydelis."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Užrakinimo ekranas."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Darbo profilio užrakinimo ekranas"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Uždaryti"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 30ffd4bd19ca..f82bcd64b22e 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Paziņojumu panelis"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Ātrie iestatījumi"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Ātrie iestatījumi un paziņojumu panelis."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Bloķēšanas ekrāns."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Darba profila bloķēšanas ekrāns"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Aizvērt"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Pārslēgties uz darba profilu"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Aizvērt"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Bloķēšanas ekrāna iestatījumi"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi nav pieejams"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera ir bloķēta"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kameras un mikrofona lietošana ir bloķēta"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofons ir bloķēts"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Prioritātes režīms ir ieslēgts"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Asistents klausās"</string> </resources> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index cda4a856e176..64631c867c74 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Панел за известување"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Брзи поставки."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"„Брзи поставки“ и „Панел со известувања“."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Заклучи екран."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Работен заклучен екран"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Затвори"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Префрли се на работен профил"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Затвори"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Поставки за заклучен екран"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi не е достапно"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камерата е блокирана"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Камерата и микрофонот се блокирани"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Микрофонот е блокиран"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Приоритетниот режим е вклучен"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Вниманието на „Помошникот“ е вклучено"</string> </resources> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 580241036d01..75633a284c9c 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"അറിയിപ്പ് ഷെയ്ഡ്."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ദ്രുത ക്രമീകരണങ്ങൾ."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"അറിയിപ്പ് ഷെയ്ഡിനുള്ള ദ്രുത ക്രമീകരണം."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ലോക്ക് സ്ക്രീൻ."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ഔദ്യോഗിക ലോക്ക് സ്ക്രീൻ"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"അവസാനിപ്പിക്കുക"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 3e6405ce6678..c1be9f782916 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Мэдэгдлийн хураангуй самбар"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Шуурхай тохиргоо."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Шуурхай тохиргоо болон мэдэгдлийн хураангуй самбар."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Дэлгэц түгжих."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ажлын түгжигдсэн дэлгэц"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Хаах"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index f582a9fae40a..b729efc167b5 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"सूचना शेड."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"क्विक सेटिंग्ज."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"क्विक सेटिंग्ज आणि सूचना शेड."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"लॉक स्क्रीन."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"कार्य लॉक स्क्रीन"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"बंद करा"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"कार्य प्रोफाइलवर स्विच करा"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"बंद करा"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"लॉक स्क्रीन सेटिंग्ज"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"वाय-फाय उपलब्ध नाही"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"कॅमेरा ब्लॉक केला"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"कॅमेरा आणि मायक्रोफोन ब्लॉक केले आहेत"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"मायक्रोफोन ब्लॉक केला"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"प्राधान्य मोड सुरू आहे"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Assistant चे लक्ष हे आता अॅक्टिव्ह आहे"</string> </resources> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 4624a4ba1aae..9ed06362c359 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Bidai pemberitahuan."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Tetapan pantas."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Tetapan pantas dan Bidai pemberitahuan."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Kunci skrin."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Skrin kunci kerja"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Tutup"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 646348044639..88165a83589a 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"အကြောင်းကြားစာအကွက်"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"အမြန်လုပ် အပြင်အဆင်"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"‘အမြန်ဆက်တင်များ’ နှင့် ‘အကြောင်းကြားစာအကွက်’။"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"မျက်နှာပြင် သော့ပိတ်ရန်"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"အလုပ်သုံး လော့ခ်မျက်နှာပြင်"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"ပိတ်ရန်"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 94ebf2b9857a..33d87ead34df 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Varselskygge."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Hurtiginnstillinger."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Hurtiginnstillinger og varselpanelet"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Låseskjerm."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Låseskjerm for arbeid"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Lukk"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index d16e91f68813..48e134bb27e4 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"सूचना कक्ष।"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"द्रुत सेटिङहरू"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"द्रुत सेटिङ तथा सूचना कक्ष।"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"स्क्रीन बन्द गर्नुहोस्।"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"कार्य प्रोफाइलको लक स्क्रिन"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"बन्द गर्नुहोस्"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 995abb04ca9f..2f86231a4827 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Meldingenpaneel."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Snelle instellingen."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Snelle instellingen en meldingenpaneel."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Vergrendelscherm."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Vergrendelscherm voor werk"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Sluiten"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index f1b69b4a9695..4ac1f8486768 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"ବିଜ୍ଞପ୍ତି ଶେଡ୍।"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"କ୍ୱିକ୍ ସେଟିଂସ୍।"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"କୁଇକ ସେଟିଂସ ଏବଂ ବିଜ୍ଞପ୍ତି ସେଡ।"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ଲକ୍ ସ୍କ୍ରୀନ୍।"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ୱର୍କ ଲକ୍ ସ୍କ୍ରୀନ୍"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"ବନ୍ଦ କରନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 0c7708fe2e05..236093010eea 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"ਸੂਚਨਾ ਸ਼ੇਡ।"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ।"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸੂਚਨਾ ਸ਼ੇਡ।"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">" ਲਾਕ ਸਕ੍ਰੀਨ।"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ਕਾਰਜ-ਸਥਾਨ ਲਾਕ ਸਕ੍ਰੀਨ"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"ਬੰਦ ਕਰੋ"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਜਾਓ"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"ਬੰਦ ਕਰੋ"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"ਲਾਕ ਸਕ੍ਰੀਨ ਸੈਟਿੰਗਾਂ"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"ਵਾਈ-ਫਾਈ ਉਪਲਬਧ ਨਹੀਂ"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"ਕੈਮਰਾ ਬਲਾਕ ਕੀਤਾ ਗਿਆ"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"ਕੈਮਰਾ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਬਲਾਕ ਕੀਤੇ ਗਏ"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਬਲਾਕ ਕੀਤਾ ਗਿਆ"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"ਤਰਜੀਹ ਮੋਡ ਚਾਲੂ ਹੈ"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Assistant ਧਿਆਨ ਸੁਵਿਧਾ ਨੂੰ ਚਾਲੂ ਹੈ"</string> </resources> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 611e996b806d..18450ab36f63 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Obszar powiadomień."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Szybkie ustawienia."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Szybkie ustawienia i obszar powiadomień."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Ekran blokady."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ekran blokady wyświetlany podczas działania"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Zamknij"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Przełącz na profil służbowy"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Zamknij"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Ustawienia ekranu blokady"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Sieć Wi-Fi jest niedostępna"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera jest zablokowana"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera i mikrofon są zablokowane"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon jest zablokowany"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Tryb priorytetowy jest włączony"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Asystent jest aktywny"</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 105031c22503..8add565ce87d 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Aba de notificações."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Configurações rápidas."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Configurações rápidas e aba de notificações."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Tela de bloqueio."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Tela de bloqueio de trabalho"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Fechar"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 8145483c2933..f8a684c626e2 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Painel de notificações."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Definições rápidas."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Definições rápidas e painel de notificações."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Ecrã de bloqueio."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ecrã de bloqueio de trabalho"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Fechar"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 105031c22503..8add565ce87d 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Aba de notificações."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Configurações rápidas."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Configurações rápidas e aba de notificações."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Tela de bloqueio."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Tela de bloqueio de trabalho"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Fechar"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 0303e646c237..a4698d600582 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Fereastră pentru notificări."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Setări rapide."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Fereastră de Setări rapide și notificări."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Ecranul de blocare."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ecran de blocare pentru serviciu"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Închide"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 9a71eea4bd4c..32a784c94b8c 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Панель уведомлений"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Быстрые настройки"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Быстрые настройки и панель уведомлений."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Экран блокировки."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Заблокировано"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Закрыть"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index ba04fabf1878..8bb334ee1d43 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"දැනුම්දීම් ආවරණය."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ක්ෂණික සැකසීම්."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"ඉක්මන් සැකසීම් සහ දැනුම්දීම් ඡායිතය."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"අගුළු තිරය."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"කාර්යාල අගුලු තිරය"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"වසන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 36058de155d9..f4181540e7fb 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Panel upozornení."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Rýchle nastavenia."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Rýchle nastavenia a panel upozornení"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Uzamknutá obrazovka"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Uzamknutá obrazovka pracovného profilu"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Zavrieť"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index f4ae067e50dc..3bc835165fde 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Zaslon z obvestili."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Hitre nastavitve."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Hitre nastavitve in zaslon z obvestili"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Zaklenjen zaslon"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Zaklenjen zaslon delovnega profila"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Zapri"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index f69221a8f3ca..6a35e38ee9b3 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Streha e njoftimeve."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Cilësimet e shpejta."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"\"Cilësimet e shpejta\" dhe \"Streha e njoftimeve\"."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Ekrani i kyçjes."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ekrani i kyçjes së punës"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Mbylle"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 19e59523611d..8dcf0cd3a6bc 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Meddelandepanel."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Snabbinställningar."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Snabbinställningar och meddelandepanel."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Låsskärm."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Låsskärm för arbete"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Stäng"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 1cfa514f1b91..49d4733c8b3c 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Kivuli cha arifa."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Mipangilio ya haraka."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Mipangilio ya haraka na Sehemu ya arifa."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Skrini iliyofungwa."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Skrini iliyofungwa ya kazini"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Funga"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Tumia wasifu wa kazini"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Funga"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Mipangilio ya skrini iliyofungwa"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi haipatikani"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera imezuiwa"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera na maikrofoni zimezuiwa"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Maikrofoni imezuiwa"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Hali ya kipaumbele imewashwa"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Programu ya Mratibu imewashwa"</string> </resources> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 7cd147099e9c..59becc69506c 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -87,4 +87,7 @@ <!-- Biometric Auth pattern view size, better to align keyguard_security_width --> <dimen name="biometric_auth_pattern_view_size">348dp</dimen> + + <dimen name="controls_header_horizontal_padding">12dp</dimen> + <dimen name="controls_content_margin_horizontal">24dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml index 9ed936050aa2..8583f0549960 100644 --- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml @@ -37,6 +37,8 @@ <dimen name="qs_media_rec_album_size">112dp</dimen> <dimen name="qs_media_rec_album_side_margin">16dp</dimen> + <dimen name="controls_panel_corner_radius">40dp</dimen> + <dimen name="lockscreen_shade_max_over_scroll_amount">42dp</dimen> <!-- Roughly the same distance as media on LS to media on QS. We will translate by this value diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml index 8b41a44b9ba3..9248d585bba7 100644 --- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml @@ -33,5 +33,7 @@ side --> <dimen name="qs_tiles_page_horizontal_margin">60dp</dimen> + <dimen name="controls_panel_corner_radius">46dp</dimen> + <dimen name="notification_section_divider_height">16dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml index 8f59df655c3a..20864591ae5a 100644 --- a/packages/SystemUI/res/values-sw720dp/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp/dimens.xml @@ -19,7 +19,8 @@ <!-- gap on either side of status bar notification icons --> <dimen name="status_bar_icon_padding">1dp</dimen> - <dimen name="controls_padding_horizontal">40dp</dimen> + <dimen name="controls_header_horizontal_padding">28dp</dimen> + <dimen name="controls_content_margin_horizontal">40dp</dimen> <dimen name="large_screen_shade_header_height">56dp</dimen> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index a29df5914a3a..4a0cb46cb52d 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"నోటిఫికేషన్ షేడ్."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"శీఘ్ర సెట్టింగ్లు."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"క్విక్ సెట్టింగ్లు, నోటిఫికేషన్ తెర."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"లాక్ స్క్రీన్."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"కార్యాలయ లాక్ స్క్రీన్"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"మూసివేస్తుంది"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index f0a2123a546a..64a528066931 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -1110,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"สลับไปใช้โปรไฟล์งาน"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"ปิด"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"การตั้งค่าหน้าจอล็อก"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi ไม่พร้อมใช้งาน"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"กล้องถูกบล็อกอยู่"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"กล้องและไมโครโฟนถูกบล็อกอยู่"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"ไมโครโฟนถูกบล็อกอยู่"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"โหมดลำดับความสำคัญเปิดอยู่"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"การเรียกใช้งาน Assistant เปิดอยู่"</string> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 05f4628e57c1..d4d583f105e0 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Notification shade."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Mga mabilisang setting."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Mga mabilisang setting at Notification shade."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Lock screen."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Lock screen sa trabaho"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Isara"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 81f84860a3bb..4df50fc99c6d 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Bildirim gölgesi."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Hızlı ayarlar."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Hızlı ayarlar ve Bildirim gölgesi."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Kilit ekranı"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"İş profili kilit ekranı"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Kapat"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 3ba43d571902..c24f858e1154 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Панель сповіщень."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Швидке налаштування."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Швидкі налаштування й панель сповіщень."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Заблокований екран."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Екран блокування завдання"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Закрити"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Перейти в робочий профіль"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Закрити"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Параметри заблокованого екрана"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Мережа Wi-Fi недоступна"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камеру заблоковано"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Камеру й мікрофон заблоковано"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Мікрофон заблоковано"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Режим пріоритету ввімкнено"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Асистента активовано"</string> </resources> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 5388be5f72fa..17ff36b09f63 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"اطلاعاتی شیڈ۔"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"فوری ترتیبات۔"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"فوری ترتیبات اور اطلاعاتی شیڈ۔"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"مقفل اسکرین۔"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"دفتری مقفل اسکرین"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"بند کریں"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index d97be0684e57..0b488f8fb0cd 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Bóng thông báo."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Cài đặt nhanh."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Cài đặt nhanh và ngăn thông báo."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Màn hình khóa."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Màn hình khóa công việc"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Đóng"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Chuyển sang hồ sơ công việc"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Đóng"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Cài đặt màn hình khoá"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Không có Wi-Fi"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Máy ảnh bị chặn"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Máy ảnh và micrô bị chặn"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Micrô bị chặn"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Chế độ ưu tiên đang bật"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Trợ lý đang bật"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index ea1c94ad96d6..b5d0e01d1c91 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"通知栏。"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"快捷设置。"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"快捷设置和通知栏。"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"锁定屏幕。"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"工作锁定屏幕"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"关闭"</string> @@ -1019,7 +1018,7 @@ <string name="mobile_data_settings_title" msgid="3955246641380064901">"移动数据网络"</string> <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string> <string name="mobile_data_connection_active" msgid="944490013299018227">"已连接"</string> - <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"暂时已连接"</string> + <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"已暂时连接"</string> <string name="mobile_data_poor_connection" msgid="819617772268371434">"连接状况不佳"</string> <string name="mobile_data_off_summary" msgid="3663995422004150567">"系统将不会自动连接到移动数据网络"</string> <string name="mobile_data_no_connection" msgid="1713872434869947377">"无网络连接"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index cfd1cc8470a6..cacf20530642 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"通知欄。"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"快速設定。"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"快速設定和通知欄。"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"上鎖畫面。"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"工作螢幕鎖定"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"關閉"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 450a3cca66c2..3fbdd487534d 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"通知欄。"</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"快捷設定。"</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"快速設定和通知欄。"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"螢幕鎖定。"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Work 螢幕鎖定"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"關閉"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 85854c55d696..8039ca14ad3a 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -198,8 +198,7 @@ <skip /> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Umthunzi wesaziso."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Izilingiselelo ezisheshayo."</string> - <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) --> - <skip /> + <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Amasethingi asheshayo Nomthunzi wezaziso."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Khiya isikrini."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ukukhiya isikrini somsebenzi"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Vala"</string> @@ -1111,16 +1110,10 @@ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Shintshela kuphrofayela yomsebenzi"</string> <string name="call_from_work_profile_close" msgid="7927067108901068098">"Vala"</string> <string name="lock_screen_settings" msgid="9197175446592718435">"Amasethingi okukhiya isikrini"</string> - <!-- no translation found for wifi_unavailable_dream_overlay_content_description (2024166212194640100) --> - <skip /> - <!-- no translation found for camera_blocked_dream_overlay_content_description (4074759493559418130) --> - <skip /> - <!-- no translation found for camera_and_microphone_blocked_dream_overlay_content_description (7891078093416249764) --> - <skip /> - <!-- no translation found for microphone_blocked_dream_overlay_content_description (5466897982130007033) --> - <skip /> - <!-- no translation found for priority_mode_dream_overlay_content_description (6044561000253314632) --> - <skip /> - <!-- no translation found for assistant_attention_content_description (6830215897604642875) --> - <skip /> + <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"I-Wi-Fi ayitholakali"</string> + <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Ikhamera ivinjiwe"</string> + <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Ikhamera nemakrofoni zivinjiwe"</string> + <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Imakrofoni ivinjiwe"</string> + <string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Imodi ebalulekile ivuliwe"</string> + <string name="assistant_attention_content_description" msgid="6830215897604642875">"Ukunaka kwe-Assistant kuvuliwe"</string> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ac07d5647a3e..897271588655 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -257,9 +257,6 @@ <!-- Radius for notifications corners with adjacent notifications --> <dimen name="notification_corner_radius_small">4dp</dimen> - <!-- Vertical padding of the FSI container --> - <dimen name="fsi_chrome_vertical_padding">80dp</dimen> - <!-- the padding of the shelf icon container --> <dimen name="shelf_icon_container_padding">13dp</dimen> @@ -1195,11 +1192,13 @@ <!-- Home Controls --> <dimen name="controls_header_menu_size">48dp</dimen> + <dimen name="controls_header_menu_button_size">48dp</dimen> <dimen name="controls_header_bottom_margin">16dp</dimen> + <dimen name="controls_header_horizontal_padding">12dp</dimen> <dimen name="controls_header_app_icon_size">24dp</dimen> <dimen name="controls_top_margin">48dp</dimen> - <dimen name="controls_padding_horizontal">0dp</dimen> - <dimen name="control_header_text_size">20sp</dimen> + <dimen name="controls_content_margin_horizontal">0dp</dimen> + <dimen name="control_header_text_size">24sp</dimen> <dimen name="control_item_text_size">16sp</dimen> <dimen name="control_menu_item_text_size">16sp</dimen> <dimen name="control_menu_item_min_height">56dp</dimen> @@ -1230,6 +1229,8 @@ <item name="controls_task_view_width_percentage" translatable="false" format="float" type="dimen">1.0</item> <dimen name="controls_task_view_right_margin">0dp</dimen> + <dimen name="controls_panel_corner_radius">42dp</dimen> + <!-- Home Controls activity view detail panel--> <dimen name="controls_activity_view_corner_radius">@*android:dimen/config_bottomDialogCornerRadius</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 7b3caffea1e1..a988813a96d3 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -407,8 +407,12 @@ <!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication --> <string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string> - <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> - <string name="accessibility_fingerprint_dialog_fingerprint_icon">Fingerprint icon</string> + <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, to locate the sensor (tablet) for accessibility (not shown on the screen). [CHAR LIMIT=NONE]--> + <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet">The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the tablet.\n\nPressing the power button turns off the screen.</string> + <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, to locate the sensor (device) for accessibility (not shown on the screen). [CHAR LIMIT=NONE]--> + <string name="security_settings_sfps_enroll_find_sensor_message" product="device">The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the device.\n\nPressing the power button turns off the screen.</string> + <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, to locate the sensor (default) for accessibility (not shown on the screen). [CHAR LIMIT=NONE]--> + <string name="security_settings_sfps_enroll_find_sensor_message" product="default">The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the phone.\n\nPressing the power button turns off the screen.</string> <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] --> <string name="fingerprint_dialog_use_fingerprint_instead">Can\u2019t recognize face. Use fingerprint instead.</string> <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] --> @@ -2431,6 +2435,10 @@ panel (embedded activity) instead of controls rendered by SystemUI [CHAR LIMIT=NONE] --> <string name="controls_panel_authorization">When you add <xliff:g id="appName" example="My app">%s</xliff:g>, it can add controls and content to this panel. In some apps, you can choose which controls show up here.</string> + <!-- Shows in a dialog presented to the user to authorize this app removal from a Device + controls panel [CHAR LIMIT=NONE] --> + <string name="controls_panel_remove_app_authorization">Remove controls for <xliff:g example="My app" id="appName">%s</xliff:g>?</string> + <!-- a11y state description for a control that is currently favorited [CHAR LIMIT=NONE] --> <string name="accessibility_control_favorite">Favorited</string> <!-- a11y state description for a control that is currently favorited with its position [CHAR LIMIT=NONE] --> @@ -2471,6 +2479,8 @@ <string name="controls_dialog_title">Add to device controls</string> <!-- Controls dialog add to favorites [CHAR LIMIT=40] --> <string name="controls_dialog_ok">Add</string> + <!-- Controls dialog remove app from a panel [CHAR LIMIT=40] --> + <string name="controls_dialog_remove">Remove</string> <!-- Controls dialog message. Indicates app that suggested this control [CHAR LIMIT=NONE] --> <string name="controls_dialog_message">Suggested by <xliff:g id="app" example="System UI">%s</xliff:g></string> <!-- Controls tile secondary label when device is locked and user does not want access to controls from lockscreen [CHAR LIMIT=20] --> @@ -2588,6 +2598,8 @@ <string name="controls_menu_edit">Edit controls</string> <!-- Controls menu, add another app [CHAR LIMIT=30] --> <string name="controls_menu_add_another_app">Add app</string> + <!-- Controls menu, remove app [CHAR_LIMIT=30] --> + <string name="controls_menu_remove">Remove app</string> <!-- Title for the media output dialog with media related devices [CHAR LIMIT=50] --> <string name="media_output_dialog_add_output">Add outputs</string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt index 12e0b9ab835a..c5979cc50c3c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt @@ -65,35 +65,40 @@ class UnfoldConstantTranslateAnimator( } else { 1 } - viewsToTranslate.forEach { (view, direction, shouldBeAnimated) -> - if (shouldBeAnimated()) { - view.get()?.translationX = xTrans * direction.multiplier * rtlMultiplier - } + viewsToTranslate.forEach { (view, direction) -> + view.get()?.translationX = xTrans * direction.multiplier * rtlMultiplier } } /** Finds in [parent] all views specified by [ids] and register them for the animation. */ private fun registerViewsForAnimation(parent: ViewGroup, ids: Set<ViewIdToTranslate>) { viewsToTranslate = - ids.mapNotNull { (id, dir, pred) -> - parent.findViewById<View>(id)?.let { view -> - ViewToTranslate(WeakReference(view), dir, pred) + ids.asSequence() + .filter { it.shouldBeAnimated() } + .mapNotNull { + parent.findViewById<View>(it.viewId)?.let { view -> + ViewToTranslate(WeakReference(view), it.direction) + } } - } + .toList() } - /** Represents a view to animate. [rootView] should contain a view with [viewId] inside. */ + /** + * Represents a view to animate. [rootView] should contain a view with [viewId] inside. + * [shouldBeAnimated] is only evaluated when the viewsToTranslate is registered in + * [registerViewsForAnimation]. + */ data class ViewIdToTranslate( val viewId: Int, val direction: Direction, val shouldBeAnimated: () -> Boolean = { true } ) - private data class ViewToTranslate( - val view: WeakReference<View>, - val direction: Direction, - val shouldBeAnimated: () -> Boolean - ) + /** + * Represents a view whose animation process is in-progress. It should be immutable because the + * started animation should be completed. + */ + private data class ViewToTranslate(val view: WeakReference<View>, val direction: Direction) /** Direction of the animation. */ enum class Direction(val multiplier: Float) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt index 23742c503ed3..454294f36d2a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt @@ -87,7 +87,7 @@ internal object Evaluator { * Helper for evaluating 3-valued logical AND/OR. * * @param returnValueIfAnyMatches AND returns false if any value is false. OR returns true if - * any value is true. + * any value is true. */ private fun threeValuedAndOrOr( conditions: Collection<Condition>, diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 7d39c4aaacbf..dd60647021ff 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -23,6 +23,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import android.annotation.IntDef; import android.content.Context; import android.content.res.Resources; +import android.os.SystemProperties; import android.view.ViewConfiguration; import android.view.WindowManagerPolicyConstants; @@ -115,6 +116,9 @@ public class QuickStepContract { public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26; // Device dreaming state public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27; + // Whether the back gesture is allowed (or ignored) by the Shade + public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean( + "persist.wm.debug.shade_allow_back_gesture", false); @Retention(RetentionPolicy.SOURCE) @IntDef({SYSUI_STATE_SCREEN_PINNING, @@ -243,9 +247,14 @@ public class QuickStepContract { sysuiStateFlags &= ~SYSUI_STATE_NAV_BAR_HIDDEN; } // Disable when in immersive, or the notifications are interactive - int disableFlags = SYSUI_STATE_NAV_BAR_HIDDEN - | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED - | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; + int disableFlags = SYSUI_STATE_NAV_BAR_HIDDEN | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; + + // EdgeBackGestureHandler ignores Back gesture when SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED. + // To allow Shade to respond to Back, we're bypassing this check (behind a flag). + if (!ALLOW_BACK_GESTURE_IN_SHADE) { + disableFlags |= SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; + } + return (sysuiStateFlags & disableFlags) != 0; } diff --git a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt index e0cf7b6a2bc4..d8085b9f9f2e 100644 --- a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt +++ b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt @@ -132,6 +132,7 @@ private object InternalFaceAuthReasons { /** * UiEvents that are logged to identify why face auth is being triggered. + * * @param extraInfo is logged as the position. See [UiEventLogger#logWithInstanceIdAndPosition] */ enum class FaceAuthUiEvent diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java index baaef1983e9c..f8cb38d7488b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java @@ -44,7 +44,7 @@ import java.util.Map; public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView> extends KeyguardInputViewController<T> { private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final LockPatternUtils mLockPatternUtils; + protected final LockPatternUtils mLockPatternUtils; private final LatencyTracker mLatencyTracker; private final FalsingCollector mFalsingCollector; private final EmergencyButtonController mEmergencyButtonController; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java index d221e22a4fcd..a010c9a16517 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java @@ -26,6 +26,7 @@ import android.text.method.TextKeyListener; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup.MarginLayoutParams; +import android.view.WindowInsets; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; @@ -156,6 +157,15 @@ public class KeyguardPasswordViewController // TODO: Remove this workaround by ensuring such a race condition never happens. mMainExecutor.executeDelayed( this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON); + mView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + @Override + public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { + if (!mKeyguardViewController.isBouncerShowing()) { + mView.hideKeyboard(); + } + return insets; + } + }); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java index 92e364110547..559db76748ed 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java @@ -71,13 +71,17 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB protected void onViewAttached() { super.onViewAttached(); - for (NumPadKey button: mView.getButtons()) { + boolean showAnimations = !mLockPatternUtils + .isPinEnhancedPrivacyEnabled(KeyguardUpdateMonitor.getCurrentUser()); + mPasswordEntry.setShowPassword(showAnimations); + for (NumPadKey button : mView.getButtons()) { button.setOnTouchListener((v, event) -> { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { mFalsingCollector.avoidGesture(); } return false; }); + button.setAnimationEnabled(showAnimations); } mPasswordEntry.setOnKeyListener(mOnKeyListener); mPasswordEntry.setUserActivityListener(this::onUserInput); @@ -102,12 +106,9 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB View okButton = mView.findViewById(R.id.key_enter); if (okButton != null) { okButton.setOnTouchListener(mActionButtonTouchListener); - okButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mPasswordEntry.isEnabled()) { - verifyPasswordAndUnlock(); - } + okButton.setOnClickListener(v -> { + if (mPasswordEntry.isEnabled()) { + verifyPasswordAndUnlock(); } }); okButton.setOnHoverListener(mLiftToActivateListener); @@ -118,7 +119,7 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB protected void onViewDetached() { super.onViewDetached(); - for (NumPadKey button: mView.getButtons()) { + for (NumPadKey button : mView.getButtons()) { button.setOnTouchListener(null); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index f164e7d33642..c6f0eeed108f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -39,7 +39,6 @@ import static java.lang.Integer.max; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.app.Activity; @@ -1044,13 +1043,10 @@ public class KeyguardSecurityContainer extends ConstraintLayout { int yTranslation = mResources.getDimensionPixelSize(R.dimen.disappear_y_translation); - AnimatorSet anims = new AnimatorSet(); ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation); - ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mView, View.ALPHA, 0f); - - anims.setInterpolator(Interpolators.STANDARD_ACCELERATE); - anims.playTogether(alphaAnim, yAnim); - anims.start(); + yAnim.setInterpolator(Interpolators.STANDARD_ACCELERATE); + yAnim.setDuration(500); + yAnim.start(); } private void setupUserSwitcher() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index f1abdc68f97e..06258b20e06f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -637,12 +637,17 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard public void startAppearAnimation() { if (mCurrentSecurityMode != SecurityMode.None) { - mView.setAlpha(1f); + setAlpha(1f); mView.startAppearAnimation(mCurrentSecurityMode); getCurrentSecurityController().startAppearAnimation(); } } + /** Set the alpha of the security container view */ + public void setAlpha(float alpha) { + mView.setAlpha(alpha); + } + public boolean startDisappearAnimation(Runnable onFinishRunnable) { boolean didRunAnimation = false; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index b53b868025e8..f4c581552bc4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -21,8 +21,6 @@ import android.util.Slog; import com.android.keyguard.KeyguardClockSwitch.ClockSize; import com.android.keyguard.logging.KeyguardLogger; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ClockAnimations; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; @@ -62,7 +60,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV KeyguardUpdateMonitor keyguardUpdateMonitor, ConfigurationController configurationController, DozeParameters dozeParameters, - FeatureFlags featureFlags, ScreenOffAnimationController screenOffAnimationController, KeyguardLogger logger) { super(keyguardStatusView); @@ -73,8 +70,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController, dozeParameters, screenOffAnimationController, /* animateYPos= */ true, logger.getBuffer()); - mKeyguardVisibilityHelper.setOcclusionTransitionFlagEnabled( - featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION)); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index bd67115e5117..a4ec8e1b72f2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -29,6 +29,7 @@ import static android.hardware.biometrics.BiometricConstants.LockoutMode; import static android.hardware.biometrics.BiometricSourceType.FACE; import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; +import static android.os.PowerManager.WAKE_REASON_UNKNOWN; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; @@ -73,11 +74,9 @@ import static com.android.systemui.statusbar.policy.DevicePostureController.DEVI import android.annotation.AnyThread; import android.annotation.MainThread; import android.annotation.SuppressLint; -import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.AlarmManager; -import android.app.UserSwitchObserver; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; @@ -108,7 +107,6 @@ import android.hardware.usb.UsbManager; import android.nfc.NfcAdapter; import android.os.CancellationSignal; import android.os.Handler; -import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.PowerManager; @@ -139,9 +137,9 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.logging.KeyguardUpdateMonitorLogger; +import com.android.settingslib.Utils; import com.android.settingslib.WirelessUtils; import com.android.settingslib.fuelgauge.BatteryStatus; -import com.android.settingslib.Utils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; @@ -152,6 +150,7 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpsysTableLogger; +import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.WeatherData; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -180,6 +179,7 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.TimeZone; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.stream.Collectors; @@ -823,6 +823,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } + private void onBiometricDetected(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + Assert.isMainThread(); + Trace.beginSection("KeyGuardUpdateMonitor#onBiometricDetected"); + for (int i = 0; i < mCallbacks.size(); i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onBiometricDetected(userId, biometricSourceType, isStrongBiometric); + } + } + Trace.endSection(); + } + @VisibleForTesting protected void onFingerprintAuthenticated(int userId, boolean isStrongBiometric) { Assert.isMainThread(); @@ -899,6 +912,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } + private void handleBiometricDetected(int authUserId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + Trace.beginSection("KeyGuardUpdateMonitor#handlerBiometricDetected"); + onBiometricDetected(authUserId, biometricSourceType, isStrongBiometric); + if (biometricSourceType == FINGERPRINT) { + mLogger.logFingerprintDetected(authUserId, isStrongBiometric); + } else if (biometricSourceType == FACE) { + mLogger.logFaceDetected(authUserId, isStrongBiometric); + setFaceRunningState(BIOMETRIC_STATE_STOPPED); + } + + Trace.endSection(); + } + private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) { Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated"); if (mHandler.hasCallbacks(mFpCancelNotReceived)) { @@ -950,8 +977,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private void onFingerprintCancelNotReceived() { mLogger.e("Fp cancellation not received, transitioning to STOPPED"); + final boolean wasCancellingRestarting = mFingerprintRunningState + == BIOMETRIC_STATE_CANCELLING_RESTARTING; mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; - KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_STOP); + if (wasCancellingRestarting) { + KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); + } else { + KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_STOP); + } } private void handleFingerprintError(int msgId, String errString) { @@ -1038,6 +1071,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab () -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE), getBiometricLockoutDelay()); } else { + boolean temporaryLockoutReset = wasLockout && !mFingerprintLockedOut; + if (temporaryLockoutReset) { + mLogger.d("temporaryLockoutReset - stopListeningForFingerprint() to stop" + + " detectFingerprint"); + stopListeningForFingerprint(); + } updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } @@ -1747,10 +1786,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } }; + private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback = + (sensorId, userId, isStrongBiometric) -> { + // Trigger the fingerprint detected path so the bouncer can be shown + handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric); + }; + private final FaceManager.FaceDetectionCallback mFaceDetectionCallback = (sensorId, userId, isStrongBiometric) -> { - // Trigger the face success path so the bouncer can be shown - handleFaceAuthenticated(userId, isStrongBiometric); + // Trigger the face detected path so the bouncer can be shown + handleBiometricDetected(userId, FACE, isStrongBiometric); }; @VisibleForTesting @@ -2175,7 +2220,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab handleDevicePolicyManagerStateChanged(msg.arg1); break; case MSG_USER_SWITCHING: - handleUserSwitching(msg.arg1, (IRemoteCallback) msg.obj); + handleUserSwitching(msg.arg1, (CountDownLatch) msg.obj); break; case MSG_USER_SWITCH_COMPLETE: handleUserSwitchComplete(msg.arg1); @@ -2301,11 +2346,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mHandler, UserHandle.ALL); mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener); - try { - ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } + mUserTracker.addCallback(mUserChangedCallback, mainExecutor); mTrustManager.registerTrustListener(this); @@ -2441,17 +2482,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return mIsFaceEnrolled; } - private final UserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() { + private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @Override - public void onUserSwitching(int newUserId, IRemoteCallback reply) { + public void onUserChanging(int newUser, Context userContext, CountDownLatch latch) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING, - newUserId, 0, reply)); + newUser, 0, latch)); } @Override - public void onUserSwitchComplete(int newUserId) { + public void onUserChanged(int newUser, Context userContext) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCH_COMPLETE, - newUserId, 0)); + newUser, 0)); } }; @@ -2783,8 +2824,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab boolean shouldListen = shouldListenKeyguardState && shouldListenUserState && shouldListenBouncerState && shouldListenUdfpsState - && shouldListenSideFpsState - && !isFingerprintLockedOut(); + && shouldListenSideFpsState; logListenerModelData( new KeyguardFingerprintListenModel( System.currentTimeMillis(), @@ -2843,8 +2883,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean supportsDetect = !mFaceSensorProperties.isEmpty() && mFaceSensorProperties.get(0).supportsFaceDetection && canBypass && !mPrimaryBouncerIsOrWillBeShowing - && !isUserInLockdown(user) - && !isFingerprintLockedOut(); + && !isUserInLockdown(user); final boolean faceAuthAllowedOrDetectionIsNeeded = faceAuthAllowed || supportsDetect; // If the face or fp has recently been authenticated do not attempt to authenticate again. @@ -2940,11 +2979,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLogger.v("startListeningForFingerprint - detect"); mFpm.detectFingerprint( mFingerprintCancelSignal, - (sensorId, user, isStrongBiometric) -> { - mLogger.d("fingerprint detected"); - // Trigger the fingerprint success path so the bouncer can be shown - handleFingerprintAuthenticated(user, isStrongBiometric); - }, + mFingerprintDetectionCallback, new FingerprintAuthenticateOptions.Builder() .setUserId(userId) .build()); @@ -2984,6 +3019,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (unlockPossible) { mFaceCancelSignal = new CancellationSignal(); + final FaceAuthenticateOptions faceAuthenticateOptions = + new SysUiFaceAuthenticateOptions( + userId, + faceAuthUiEvent, + faceAuthUiEvent == FACE_AUTH_UPDATED_STARTED_WAKING_UP + ? faceAuthUiEvent.getExtraInfo() + : WAKE_REASON_UNKNOWN + ).toFaceAuthenticateOptions(); // This would need to be updated for multi-sensor devices final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty() && mFaceSensorProperties.get(0).supportsFaceDetection; @@ -2994,9 +3037,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // Run face detection. (If a face is detected, show the bouncer.) mLogger.v("startListeningForFace - detect"); mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, - new FaceAuthenticateOptions.Builder() - .setUserId(userId) - .build()); + faceAuthenticateOptions); } else { // Don't run face detection. Instead, inform the user // face auth is unavailable and how to proceed. @@ -3015,7 +3056,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean isBypassEnabled = mKeyguardBypassController != null && mKeyguardBypassController.isBypassEnabled(); mFaceManager.authenticate(null /* crypto */, mFaceCancelSignal, - mFaceAuthenticationCallback, null /* handler */, userId); + mFaceAuthenticationCallback, null /* handler */, + faceAuthenticateOptions); } setFaceRunningState(BIOMETRIC_STATE_RUNNING); } @@ -3176,7 +3218,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * Handle {@link #MSG_USER_SWITCHING} */ @VisibleForTesting - void handleUserSwitching(int userId, IRemoteCallback reply) { + void handleUserSwitching(int userId, CountDownLatch latch) { Assert.isMainThread(); clearBiometricRecognized(); mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId)); @@ -3186,11 +3228,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab cb.onUserSwitching(userId); } } - try { - reply.sendResult(null); - } catch (RemoteException e) { - mLogger.logException(e, "Ignored exception while userSwitching"); - } + latch.countDown(); } /** @@ -3963,13 +4001,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mContext.getContentResolver().unregisterContentObserver(mTimeFormatChangeObserver); } - try { - ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver); - } catch (RemoteException e) { - mLogger.logException( - e, - "RemoteException onDestroy. cannot unregister userSwitchObserver"); - } + mUserTracker.removeCallback(mUserChangedCallback); TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 38f3e5065eec..0d4889a4c39f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -214,7 +214,7 @@ public class KeyguardUpdateMonitorCallback { public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) { } /** - * Called when a biometric is recognized. + * Called when a biometric is authenticated. * @param userId the user id for which the biometric sample was authenticated * @param biometricSourceType */ @@ -222,6 +222,14 @@ public class KeyguardUpdateMonitorCallback { boolean isStrongBiometric) { } /** + * Called when a biometric is detected but not successfully authenticated. + * @param userId the user id for which the biometric sample was detected + * @param biometricSourceType + */ + public void onBiometricDetected(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { } + + /** * Called when biometric authentication provides help string (e.g. "Try again") * @param msgId * @param helpString diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java index 7e48193bfc62..a678edc0eb06 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java @@ -28,7 +28,6 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.stack.AnimationProperties; -import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -49,7 +48,6 @@ public class KeyguardVisibilityHelper { private boolean mAnimateYPos; private boolean mKeyguardViewVisibilityAnimating; private boolean mLastOccludedState = false; - private boolean mIsUnoccludeTransitionFlagEnabled = false; private final AnimationProperties mAnimationProperties = new AnimationProperties(); private final LogBuffer mLogBuffer; @@ -77,10 +75,6 @@ public class KeyguardVisibilityHelper { return mKeyguardViewVisibilityAnimating; } - public void setOcclusionTransitionFlagEnabled(boolean enabled) { - mIsUnoccludeTransitionFlagEnabled = enabled; - } - /** * Set the visibility of a keyguard view based on some new state. */ @@ -156,24 +150,9 @@ public class KeyguardVisibilityHelper { // since it may need to be cancelled due to keyguard lifecycle events. mScreenOffAnimationController.animateInKeyguard( mView, mAnimateKeyguardStatusViewVisibleEndRunnable); - } else if (!mIsUnoccludeTransitionFlagEnabled && mLastOccludedState && !isOccluded) { - // An activity was displayed over the lock screen, and has now gone away - log("Unoccluded transition"); - mView.setVisibility(View.VISIBLE); - mView.setAlpha(0f); - - mView.animate() - .setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP) - .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) - .alpha(1f) - .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable) - .start(); } else { log("Direct set Visibility to VISIBLE"); mView.setVisibility(View.VISIBLE); - if (!mIsUnoccludeTransitionFlagEnabled) { - mView.setAlpha(1f); - } } } else { log("Direct set Visibility to GONE"); diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java index b30a0e010e4b..ad669099284f 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java @@ -51,7 +51,6 @@ class NumPadAnimator { private float mStartRadius; private float mEndRadius; private int mHeight; - private boolean mInitialized; private static final int EXPAND_ANIMATION_MS = 100; private static final int EXPAND_COLOR_ANIMATION_MS = 50; @@ -93,15 +92,15 @@ class NumPadAnimator { } void onLayout(int height) { + boolean shouldUpdateHeight = height != mHeight; mHeight = height; mStartRadius = height / 2f; mEndRadius = height / 4f; mExpandAnimator.setFloatValues(mStartRadius, mEndRadius); mContractAnimator.setFloatValues(mEndRadius, mStartRadius); // Set initial corner radius. - if (!mInitialized) { + if (shouldUpdateHeight) { mBackground.setCornerRadius(mStartRadius); - mInitialized = true; } } diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index 3b0644eaab82..7c7680afe368 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -50,6 +50,7 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener { private int mDigit = -1; private int mTextViewResId; private PasswordTextView mTextView; + private boolean mAnimationsEnabled = true; @Nullable private NumPadAnimator mAnimator; @@ -164,11 +165,11 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener { switch(event.getActionMasked()) { case MotionEvent.ACTION_DOWN: doHapticKeyClick(); - if (mAnimator != null) mAnimator.expand(); + if (mAnimator != null && mAnimationsEnabled) mAnimator.expand(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - if (mAnimator != null) mAnimator.contract(); + if (mAnimator != null && mAnimationsEnabled) mAnimator.contract(); break; } return super.onTouchEvent(event); @@ -228,4 +229,11 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener { mAnimator.setProgress(progress); } } + + /** + * Controls the animation when a key is pressed + */ + public void setAnimationEnabled(boolean enabled) { + mAnimationsEnabled = enabled; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java index 8554e11a2f3c..540001135543 100644 --- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java +++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java @@ -30,7 +30,6 @@ import android.graphics.Rect; import android.graphics.Typeface; import android.os.PowerManager; import android.os.SystemClock; -import android.provider.Settings; import android.text.InputType; import android.text.TextUtils; import android.util.AttributeSet; @@ -100,7 +99,7 @@ public class PasswordTextView extends FrameLayout { private Interpolator mAppearInterpolator; private Interpolator mDisappearInterpolator; private Interpolator mFastOutSlowInInterpolator; - private boolean mShowPassword; + private boolean mShowPassword = true; private UserActivityListener mUserActivityListener; private PinShapeInput mPinShapeInput; private boolean mUsePinShapes = false; @@ -158,8 +157,6 @@ public class PasswordTextView extends FrameLayout { mDrawPaint.setTypeface(Typeface.create( context.getString(com.android.internal.R.string.config_headlineFontFamily), 0)); - mShowPassword = Settings.System.getInt(mContext.getContentResolver(), - Settings.System.TEXT_SHOW_PASSWORD, 1) == 1; mAppearInterpolator = AnimationUtils.loadInterpolator(mContext, android.R.interpolator.linear_out_slow_in); mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext, @@ -441,6 +438,13 @@ public class PasswordTextView extends FrameLayout { addView(mPinShapeInput.getView()); } + /** + * Controls whether the last entered digit is briefly shown after being entered + */ + public void setShowPassword(boolean enabled) { + mShowPassword = enabled; + } + private class CharState { char whichChar; ValueAnimator textAnimator; diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt index c414c088529c..e53f6adb62a4 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt @@ -167,6 +167,20 @@ class KeyguardUpdateMonitorLogger @Inject constructor( }, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"}) } + fun logFaceDetected(userId: Int, isStrongBiometric: Boolean) { + logBuffer.log(TAG, DEBUG, { + int1 = userId + bool1 = isStrongBiometric + }, {"Face detected: userId: $int1, isStrongBiometric: $bool1"}) + } + + fun logFingerprintDetected(userId: Int, isStrongBiometric: Boolean) { + logBuffer.log(TAG, DEBUG, { + int1 = userId + bool1 = isStrongBiometric + }, {"Fingerprint detected: userId: $int1, isStrongBiometric: $bool1"}) + } + fun logFingerprintError(msgId: Int, originalErrMsg: String) { logBuffer.log(TAG, DEBUG, { str1 = originalErrMsg diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt index 709ddf5ce748..52312b8e8add 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt @@ -97,7 +97,6 @@ open class AuthBiometricFingerprintIconController( val iconContentDescription = getIconContentDescription(newState) if (iconContentDescription != null) { iconView.contentDescription = iconContentDescription - iconViewOverlay.contentDescription = iconContentDescription } iconView.frame = 0 @@ -152,7 +151,7 @@ open class AuthBiometricFingerprintIconController( STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING, STATE_PENDING_CONFIRMATION, - STATE_AUTHENTICATED -> R.string.accessibility_fingerprint_dialog_fingerprint_icon + STATE_AUTHENTICATED -> R.string.security_settings_sfps_enroll_find_sensor_message STATE_ERROR, STATE_HELP -> R.string.biometric_dialog_try_again else -> null diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index e698faffd3f6..08efd89029ed 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -85,6 +85,8 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; +import kotlin.Unit; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -98,8 +100,6 @@ import java.util.Set; import javax.inject.Inject; import javax.inject.Provider; -import kotlin.Unit; - /** * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the * appropriate biometric UI (e.g. BiometricDialogView). @@ -872,7 +872,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, } /** - * Stores the callback received from {@link com.android.server.display.DisplayModeDirector}. + * Stores the callback received from + * {@link com.android.server.display.mode.DisplayModeDirector}. * * DisplayModeDirector implements {@link IUdfpsRefreshRateRequestCallback} * and registers it with this class by calling diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt index fabc1c1bb908..e16121d1104e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt @@ -49,8 +49,8 @@ constructor( /** * @property messagesToDefer messages that shouldn't show immediately when received, but may be - * shown later if the message is the most frequent acquiredInfo processed and meets [threshold] - * percentage of all passed acquired frames. + * shown later if the message is the most frequent acquiredInfo processed and meets [threshold] + * percentage of all passed acquired frames. */ open class BiometricMessageDeferral( private val messagesToDefer: Set<Int>, @@ -127,8 +127,9 @@ open class BiometricMessageDeferral( /** * Get the most frequent deferred message that meets the [threshold] percentage of processed * frames. + * * @return null if no acquiredInfo have been deferred OR deferred messages didn't meet the - * [threshold] percentage. + * [threshold] percentage. */ fun getDeferredMessage(): CharSequence? { mostFrequentAcquiredInfoToDefer?.let { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index ac6a22c1474b..f7d87fc69e55 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -492,7 +492,9 @@ class OrientationReasonListener( displayManager, handler, BiometricDisplayListener.SensorType.SideFingerprint(sensorProps) - ) { onOrientationChanged(reason) } + ) { + onOrientationChanged(reason) + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt index 2d0d52e067ab..231e7a429c53 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt @@ -348,6 +348,7 @@ constructor( /** * Overrides non-bouncer show logic in shouldPauseAuth to still show icon. + * * @return whether the udfpsBouncer has been newly shown or hidden */ private fun showUdfpsBouncer(show: Boolean): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt index 28bc2b727a27..6854b50370ba 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt @@ -76,5 +76,6 @@ data class NormalizedTouchData( | time: $time | gestureStart: $gestureStart |} - """.trimMargin() + """ + .trimMargin() } diff --git a/packages/SystemUI/src/com/android/systemui/common/coroutine/ChannelExt.kt b/packages/SystemUI/src/com/android/systemui/common/coroutine/ChannelExt.kt index a0b19dc5c96e..c0e1717ea014 100644 --- a/packages/SystemUI/src/com/android/systemui/common/coroutine/ChannelExt.kt +++ b/packages/SystemUI/src/com/android/systemui/common/coroutine/ChannelExt.kt @@ -26,7 +26,6 @@ object ChannelExt { /** * Convenience wrapper around [SendChannel.trySend] that also logs on failure. This is the * equivalent of calling: - * * ``` * sendChannel.trySend(element).onFailure { * Log.e( diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt index b3c18fb3cd98..3744344421a9 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt @@ -86,6 +86,7 @@ internal constructor(wrapper: ControlsFavoritePersistenceWrapper) { * * When the favorites for that application are returned, they will be removed from the auxiliary * file immediately, so they won't be retrieved again. + * * @param componentName the name of the service that provided the controls * @return a list of structures with favorites */ diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt index 822190f21da1..3555d0a7e7fb 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt @@ -166,6 +166,19 @@ interface ControlsController : UserAwareController { ) /** + * Removes favorites for a given component + * @param componentName the name of the service that provides the [Control] + * @return true when favorites is scheduled for deletion + */ + fun removeFavorites(componentName: ComponentName): Boolean + + /** + * Checks if the favorites can be removed. You can't remove components from the preferred list. + * @param componentName the name of the service that provides the [Control] + */ + fun canRemoveFavorites(componentName: ComponentName): Boolean + + /** * Replaces the favorites for the given structure. * * Calling this method will eliminate the previous selection of favorites and replace it with a diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index 700f4f62029f..a171f43013e0 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -499,6 +499,21 @@ class ControlsControllerImpl @Inject constructor ( } } + override fun canRemoveFavorites(componentName: ComponentName): Boolean = + !authorizedPanelsRepository.getPreferredPackages().contains(componentName.packageName) + + override fun removeFavorites(componentName: ComponentName): Boolean { + if (!confirmAvailability()) return false + if (!canRemoveFavorites(componentName)) return false + + executor.execute { + Favorites.removeStructures(componentName) + authorizedPanelsRepository.removeAuthorizedPanels(setOf(componentName.packageName)) + persistenceWrapper.storeFavorites(Favorites.getAllStructures()) + } + return true + } + override fun replaceFavoritesForStructure(structureInfo: StructureInfo) { if (!confirmAvailability()) return executor.execute { @@ -657,10 +672,11 @@ private object Favorites { return true } - fun removeStructures(componentName: ComponentName) { + fun removeStructures(componentName: ComponentName): Boolean { val newFavMap = favMap.toMutableMap() - newFavMap.remove(componentName) + val removed = newFavMap.remove(componentName) != null favMap = newFavMap + return removed } fun addFavorite( diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt index 3e672f391e81..ae9c37aa0e7b 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt @@ -26,6 +26,14 @@ interface AuthorizedPanelsRepository { /** A set of package names that the user has previously authorized to show panels. */ fun getAuthorizedPanels(): Set<String> + /** Preferred applications to query controls suggestions from */ + fun getPreferredPackages(): Set<String> + /** Adds [packageNames] to the set of packages that the user has authorized to show panels. */ fun addAuthorizedPanels(packageNames: Set<String>) + + /** + * Removes [packageNames] from the set of packages that the user has authorized to show panels. + */ + fun removeAuthorizedPanels(packageNames: Set<String>) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt index f7e43a77b573..e51e8326c0a5 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt @@ -37,10 +37,20 @@ constructor( return getAuthorizedPanelsInternal(instantiateSharedPrefs()) } + override fun getPreferredPackages(): Set<String> = + context.resources.getStringArray(R.array.config_controlsPreferredPackages).toSet() + override fun addAuthorizedPanels(packageNames: Set<String>) { addAuthorizedPanelsInternal(instantiateSharedPrefs(), packageNames) } + override fun removeAuthorizedPanels(packageNames: Set<String>) { + with(instantiateSharedPrefs()) { + val currentSet = getAuthorizedPanelsInternal(this) + edit().putStringSet(KEY, currentSet - packageNames).apply() + } + } + private fun getAuthorizedPanelsInternal(sharedPreferences: SharedPreferences): Set<String> { return sharedPreferences.getStringSet(KEY, emptySet())!! } @@ -63,15 +73,7 @@ constructor( // If we've never run this (i.e., the key doesn't exist), add the default packages if (sharedPref.getStringSet(KEY, null) == null) { - sharedPref - .edit() - .putStringSet( - KEY, - context.resources - .getStringArray(R.array.config_controlsPreferredPackages) - .toSet() - ) - .apply() + sharedPref.edit().putStringSet(KEY, getPreferredPackages()).apply() } return sharedPref } diff --git a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt index bb2e2d701aa0..06d4a0888197 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt @@ -38,7 +38,6 @@ import javax.inject.Inject /** * Manager to display a dialog to prompt user to enable controls related Settings: - * * * [Settings.Secure.LOCKSCREEN_SHOW_CONTROLS] * * [Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS] */ @@ -46,20 +45,19 @@ interface ControlsSettingsDialogManager { /** * Shows the corresponding dialog. In order for a dialog to appear, the following must be true - * * * At least one of the Settings in [ControlsSettingsRepository] are `false`. * * The dialog has not been seen by the user too many times (as defined by - * [MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG]). + * [MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG]). * * When the dialogs are shown, the following outcomes are possible: * * User cancels the dialog by clicking outside or going back: we register that the dialog was - * seen but the settings don't change. + * seen but the settings don't change. * * User responds negatively to the dialog: we register that the user doesn't want to change - * the settings (dialog will not appear again) and the settings don't change. + * the settings (dialog will not appear again) and the settings don't change. * * User responds positively to the dialog: the settings are set to `true` and the dialog will - * not appear again. + * not appear again. * * SystemUI closes the dialogs (for example, the activity showing it is closed). In this case, - * we don't modify anything. + * we don't modify anything. * * Of those four scenarios, only the first three will cause [onAttemptCompleted] to be called. * It will also be called if the dialogs are not shown. diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt index 3a3f9b4e5265..bf0a69296dfd 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt @@ -68,7 +68,7 @@ class ControlsActivity @Inject constructor( getLifecycle().addObserver( ControlsAnimations.observerForAnimations( - requireViewById<ViewGroup>(R.id.control_detail_root), + requireViewById(R.id.control_detail_root), window, intent, !featureFlags.isEnabled(Flags.USE_APP_PANELS) @@ -95,7 +95,7 @@ class ControlsActivity @Inject constructor( override fun onStart() { super.onStart() - parent = requireViewById<ViewGroup>(R.id.global_actions_controls) + parent = requireViewById(R.id.control_detail_root) parent.alpha = 0f if (featureFlags.isEnabled(Flags.USE_APP_PANELS) && !keyguardStateController.isUnlocked) { controlsSettingsDialogManager.maybeShowDialog(this) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt new file mode 100644 index 000000000000..d6cfb79101d7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.controls.ui + +import android.app.Dialog +import android.content.Context +import android.content.DialogInterface +import com.android.systemui.R +import com.android.systemui.statusbar.phone.SystemUIDialog +import java.util.function.Consumer +import javax.inject.Inject + +class ControlsDialogsFactory(private val internalDialogFactory: (Context) -> SystemUIDialog) { + + @Inject constructor() : this({ SystemUIDialog(it) }) + + fun createRemoveAppDialog( + context: Context, + appName: CharSequence, + response: Consumer<Boolean> + ): Dialog { + val listener = + DialogInterface.OnClickListener { _, which -> + response.accept(which == DialogInterface.BUTTON_POSITIVE) + } + return internalDialogFactory(context).apply { + setTitle(context.getString(R.string.controls_panel_remove_app_authorization, appName)) + setCanceledOnTouchOutside(true) + setOnCancelListener { response.accept(false) } + setPositiveButton(R.string.controls_dialog_remove, listener) + setNeutralButton(R.string.cancel, listener) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 9405c602caf7..c61dad6fc075 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter import android.animation.ObjectAnimator import android.app.Activity import android.app.ActivityOptions +import android.app.Dialog import android.app.PendingIntent import android.content.ComponentName import android.content.Context @@ -52,7 +53,6 @@ import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.ControlsServiceInfo -import com.android.systemui.controls.settings.ControlsSettingsRepository import com.android.systemui.controls.CustomIconCache import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.controller.StructureInfo @@ -64,6 +64,7 @@ import com.android.systemui.controls.management.ControlsFavoritingActivity import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.management.ControlsProviderSelectorActivity import com.android.systemui.controls.panels.AuthorizedPanelsRepository +import com.android.systemui.controls.settings.ControlsSettingsRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -83,7 +84,7 @@ import com.android.wm.shell.TaskViewFactory import dagger.Lazy import java.io.PrintWriter import java.text.Collator -import java.util.Optional +import java.util.* import java.util.function.Consumer import javax.inject.Inject @@ -108,6 +109,7 @@ class ControlsUiControllerImpl @Inject constructor ( private val controlsSettingsRepository: ControlsSettingsRepository, private val authorizedPanelsRepository: AuthorizedPanelsRepository, private val featureFlags: FeatureFlags, + private val dialogsFactory: ControlsDialogsFactory, dumpManager: DumpManager ) : ControlsUiController, Dumpable { @@ -122,6 +124,7 @@ class ControlsUiControllerImpl @Inject constructor ( private const val ADD_CONTROLS_ID = 1L private const val ADD_APP_ID = 2L private const val EDIT_CONTROLS_ID = 3L + private const val REMOVE_APP_ID = 4L } private var selectedItem: SelectedItem = SelectedItem.EMPTY_SELECTION @@ -151,6 +154,7 @@ class ControlsUiControllerImpl @Inject constructor ( private var openAppIntent: Intent? = null private var overflowMenuAdapter: BaseAdapter? = null + private var removeAppDialog: Dialog? = null private val onSeedingComplete = Consumer<Boolean> { accepted -> @@ -330,6 +334,31 @@ class ControlsUiControllerImpl @Inject constructor ( } } + @VisibleForTesting + internal fun startRemovingApp(componentName: ComponentName, appName: CharSequence) { + removeAppDialog?.cancel() + removeAppDialog = dialogsFactory.createRemoveAppDialog(context, appName) { + if (!controlsController.get().removeFavorites(componentName)) { + return@createRemoveAppDialog + } + if ( + sharedPreferences.getString(PREF_COMPONENT, "") == + componentName.flattenToString() + ) { + sharedPreferences + .edit() + .remove(PREF_COMPONENT) + .remove(PREF_STRUCTURE_OR_APP_NAME) + .remove(PREF_IS_PANEL) + .commit() + } + + allStructures = controlsController.get().getFavorites() + selectedItem = getPreferredSelectedItem(allStructures) + reload(parent) + }.apply { show() } + } + private fun startTargetedActivity(si: StructureInfo, klazz: Class<*>) { val i = Intent(activityContext, klazz) putIntentExtras(i, si) @@ -433,7 +462,10 @@ class ControlsUiControllerImpl @Inject constructor ( val currentApps = panelsAndStructures.map { it.componentName }.toSet() val allApps = controlsListingController.get() .getCurrentServices().map { it.componentName }.toSet() - createMenu(extraApps = (allApps - currentApps).isNotEmpty()) + createMenu( + selectionItem = selectionItem, + extraApps = (allApps - currentApps).isNotEmpty(), + ) } private fun createPanelView(componentName: ComponentName) { @@ -472,7 +504,7 @@ class ControlsUiControllerImpl @Inject constructor ( } } - private fun createMenu(extraApps: Boolean) { + private fun createMenu(selectionItem: SelectionItem, extraApps: Boolean) { val isPanel = selectedItem is SelectedItem.PanelItem val selectedStructure = (selectedItem as? SelectedItem.StructureItem)?.structure ?: EMPTY_STRUCTURE @@ -490,6 +522,13 @@ class ControlsUiControllerImpl @Inject constructor ( ADD_APP_ID )) } + if (featureFlags.isEnabled(Flags.APP_PANELS_REMOVE_APPS_ALLOWED) && + controlsController.get().canRemoveFavorites(selectedItem.componentName)) { + add(OverflowMenuAdapter.MenuItem( + context.getText(R.string.controls_menu_remove), + REMOVE_APP_ID, + )) + } } else { add(OverflowMenuAdapter.MenuItem( context.getText(R.string.controls_menu_add), @@ -529,6 +568,9 @@ class ControlsUiControllerImpl @Inject constructor ( ADD_APP_ID -> startProviderSelectorActivity() ADD_CONTROLS_ID -> startFavoritingActivity(selectedStructure) EDIT_CONTROLS_ID -> startEditingActivity(selectedStructure) + REMOVE_APP_ID -> startRemovingApp( + selectedStructure.componentName, selectionItem.appName + ) } dismiss() } @@ -546,8 +588,12 @@ class ControlsUiControllerImpl @Inject constructor ( RenderInfo.registerComponentIcon(it.componentName, it.icon) } - var adapter = ItemAdapter(context, R.layout.controls_spinner_item).apply { - addAll(items) + val adapter = ItemAdapter(context, R.layout.controls_spinner_item).apply { + add(selected) + addAll(items + .filter { it !== selected } + .sortedBy { it.appName.toString() } + ) } val iconSize = context.resources @@ -728,6 +774,7 @@ class ControlsUiControllerImpl @Inject constructor ( it.value.dismiss() } controlActionCoordinator.closeDialogs() + removeAppDialog?.cancel() } override fun hide(parent: ViewGroup) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt index 3b6ab20e39d4..78e87cafc4f2 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt @@ -71,7 +71,7 @@ class PanelTaskViewController( taskView.post { val roundedCorner = activityContext.resources.getDimensionPixelSize( - R.dimen.notification_corner_radius + R.dimen.controls_panel_corner_radius ) val radii = FloatArray(8) { roundedCorner.toFloat() } taskView.background = diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 9921b1fcd4bc..b86d419f540f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -47,10 +47,7 @@ import com.android.systemui.reardisplay.RearDisplayDialogController import com.android.systemui.recents.Recents import com.android.systemui.settings.dagger.MultiUserUtilsModule import com.android.systemui.shortcut.ShortcutKeyDispatcher -import com.android.systemui.statusbar.notification.fsi.FsiChromeRepo import com.android.systemui.statusbar.notification.InstantAppNotifier -import com.android.systemui.statusbar.notification.fsi.FsiChromeViewModelFactory -import com.android.systemui.statusbar.notification.fsi.FsiChromeViewBinder import com.android.systemui.statusbar.phone.KeyguardLiftController import com.android.systemui.stylus.StylusUsiPowerStartable import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator @@ -91,24 +88,6 @@ abstract class SystemUICoreStartableModule { @ClassKey(ClipboardListener::class) abstract fun bindClipboardListener(sysui: ClipboardListener): CoreStartable - /** Inject into FsiChromeRepo. */ - @Binds - @IntoMap - @ClassKey(FsiChromeRepo::class) - abstract fun bindFSIChromeRepo(sysui: FsiChromeRepo): CoreStartable - - /** Inject into FsiChromeWindowViewModel. */ - @Binds - @IntoMap - @ClassKey(FsiChromeViewModelFactory::class) - abstract fun bindFSIChromeWindowViewModel(sysui: FsiChromeViewModelFactory): CoreStartable - - /** Inject into FsiChromeWindowBinder. */ - @Binds - @IntoMap - @ClassKey(FsiChromeViewBinder::class) - abstract fun bindFsiChromeWindowBinder(sysui: FsiChromeViewBinder): CoreStartable - /** Inject into GlobalActionsComponent. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt b/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt index 84f83f1ae956..45ff963c2a9f 100644 --- a/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt +++ b/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt @@ -128,7 +128,6 @@ constructor( * * This is equivalent of creating a listener manually and adding an event handler for the given * command, like so: - * * ``` * class Demoable { * private val demoHandler = object : DemoMode { diff --git a/packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt b/packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt new file mode 100644 index 000000000000..1390b4db3576 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.devicepolicy + +import android.app.admin.DevicePolicyManager +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL +import android.content.ComponentName + +/** Returns true if the admin of [userId] disallows keyguard shortcuts. */ +fun DevicePolicyManager.areKeyguardShortcutsDisabled( + admin: ComponentName? = null, + userId: Int +): Boolean { + val flags = getKeyguardDisabledFeatures(admin, userId) + return flags and KEYGUARD_DISABLE_SHORTCUTS_ALL == KEYGUARD_DISABLE_SHORTCUTS_ALL || + flags and KEYGUARD_DISABLE_FEATURES_ALL == KEYGUARD_DISABLE_FEATURES_ALL +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt index c3bd5d96590e..ca1cef385755 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt @@ -184,6 +184,7 @@ constructor( /** * Ends the dream content and dream overlay animations, if they're currently running. + * * @see [AnimatorSet.end] */ fun endAnimations() { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index f282aaefe0fd..9374ad93347a 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -16,6 +16,8 @@ package com.android.systemui.dreams; +import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_WINDOW_TITLE; + import android.content.ComponentName; import android.content.Context; import android.graphics.drawable.ColorDrawable; @@ -76,6 +78,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ private final ComponentName mLowLightDreamComponent; private final UiEventLogger mUiEventLogger; private final WindowManager mWindowManager; + private final String mWindowTitle; // A reference to the {@link Window} used to hold the dream overlay. private Window mWindow; @@ -151,7 +154,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ TouchInsetManager touchInsetManager, @Nullable @Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT) ComponentName lowLightDreamComponent, - DreamOverlayCallbackController dreamOverlayCallbackController) { + DreamOverlayCallbackController dreamOverlayCallbackController, + @Named(DREAM_OVERLAY_WINDOW_TITLE) String windowTitle) { mContext = context; mExecutor = executor; mWindowManager = windowManager; @@ -161,6 +165,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mStateController = stateController; mUiEventLogger = uiEventLogger; mDreamOverlayCallbackController = dreamOverlayCallbackController; + mWindowTitle = windowTitle; final ViewModelStore viewModelStore = new ViewModelStore(); final Complication.Host host = @@ -277,7 +282,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ private boolean addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) { mWindow = new PhoneWindow(mContext); // Default to SystemUI name for TalkBack. - mWindow.setTitle(""); + mWindow.setTitle(mWindowTitle); mWindow.setAttributes(layoutParams); mWindow.setWindowManager(null, layoutParams.token, "DreamOverlay", true); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java index e39073bb6711..ff1f31245570 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java @@ -28,6 +28,8 @@ import android.widget.FrameLayout; import com.android.systemui.CoreStartable; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.smartspace.DreamSmartspaceController; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.BcSmartspaceDataPlugin; import com.android.systemui.shared.condition.Monitor; import com.android.systemui.util.condition.ConditionalCoreStartable; @@ -68,6 +70,7 @@ public class SmartSpaceComplication implements Complication { private final DreamSmartspaceController mSmartSpaceController; private final DreamOverlayStateController mDreamOverlayStateController; private final SmartSpaceComplication mComplication; + private final FeatureFlags mFeatureFlags; private final BcSmartspaceDataPlugin.SmartspaceTargetListener mSmartspaceListener = new BcSmartspaceDataPlugin.SmartspaceTargetListener() { @@ -85,15 +88,21 @@ public class SmartSpaceComplication implements Complication { DreamOverlayStateController dreamOverlayStateController, SmartSpaceComplication smartSpaceComplication, DreamSmartspaceController smartSpaceController, - @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) { + @Named(DREAM_PRETEXT_MONITOR) Monitor monitor, + FeatureFlags featureFlags) { super(monitor); mDreamOverlayStateController = dreamOverlayStateController; mComplication = smartSpaceComplication; mSmartSpaceController = smartSpaceController; + mFeatureFlags = featureFlags; } @Override public void onStart() { + if (mFeatureFlags.isEnabled(Flags.HIDE_SMARTSPACE_ON_DREAM_OVERLAY)) { + return; + } + mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() { @Override public void onStateChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java index f598c360dbcf..f130026b76b4 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java @@ -23,6 +23,7 @@ import android.content.res.Resources; import com.android.dream.lowlight.dagger.LowLightDreamModule; import com.android.settingslib.dream.DreamBackend; +import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.DreamOverlayNotificationCountProvider; @@ -66,6 +67,8 @@ public interface DreamModule { String DREAM_SUPPORTED = "dream_supported"; String DREAM_PRETEXT_CONDITIONS = "dream_pretext_conditions"; String DREAM_PRETEXT_MONITOR = "dream_prtext_monitor"; + String DREAM_OVERLAY_WINDOW_TITLE = "dream_overlay_window_title"; + /** * Provides the dream component @@ -139,4 +142,11 @@ public interface DreamModule { @Named(DREAM_PRETEXT_CONDITIONS) Set<Condition> pretextConditions) { return new Monitor(executor, pretextConditions); } + + /** */ + @Provides + @Named(DREAM_OVERLAY_WINDOW_TITLE) + static String providesDreamOverlayWindowTitle(@Main Resources resources) { + return resources.getString(R.string.app_label); + } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index ab78b1bcfca6..febb45ef5ce5 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -64,9 +64,6 @@ object Flags { // TODO(b/259130119): Tracking Bug val FSI_ON_DND_UPDATE = releasedFlag(259130119, "fsi_on_dnd_update") - // TODO(b/265804648): Tracking Bug - @JvmField val DISABLE_FSI = unreleasedFlag(265804648, "disable_fsi") - // TODO(b/254512538): Tracking Bug val INSTANT_VOICE_REPLY = releasedFlag(111, "instant_voice_reply") @@ -87,9 +84,6 @@ object Flags { val NOTIFICATION_GROUP_DISMISSAL_ANIMATION = releasedFlag(259217907, "notification_group_dismissal_animation") - // TODO(b/257506350): Tracking Bug - @JvmField val FSI_CHROME = unreleasedFlag(117, "fsi_chrome") - @JvmField val SIMPLIFIED_APPEAR_FRACTION = unreleasedFlag(259395680, "simplified_appear_fraction", teamfood = true) @@ -189,10 +183,6 @@ object Flags { @JvmField val REVAMPED_WALLPAPER_UI = releasedFlag(222, "revamped_wallpaper_ui") - /** A different path for unocclusion transitions back to keyguard */ - // TODO(b/262859270): Tracking Bug - @JvmField val UNOCCLUSION_TRANSITION = releasedFlag(223, "unocclusion_transition") - // flag for controlling auto pin confirmation and material u shapes in bouncer @JvmField val AUTO_PIN_CONFIRMATION = @@ -234,6 +224,10 @@ object Flags { val SMARTSPACE_DATE_WEATHER_DECOUPLED = sysPropBooleanFlag(403, "persist.sysui.ss.dw_decoupled", default = true) + // TODO(b/270223352): Tracking Bug + @JvmField + val HIDE_SMARTSPACE_ON_DREAM_OVERLAY = unreleasedFlag(404, "hide_smartspace_on_dream_overlay") + // 500 - quick settings val PEOPLE_TILE = resourceBooleanFlag(502, R.bool.flag_conversations, "people_tile") @@ -373,15 +367,24 @@ object Flags { // TODO(b/267166152) : Tracking Bug val MEDIA_RETAIN_RECOMMENDATIONS = releasedFlag(916, "media_retain_recommendations") + // TODO(b/270437894): Tracking Bug + val MEDIA_REMOTE_RESUME = unreleasedFlag(917, "media_remote_resume") + // 1000 - dock val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging") // TODO(b/254512758): Tracking Bug @JvmField val ROUNDED_BOX_RIPPLE = releasedFlag(1002, "rounded_box_ripple") + // TODO(b/270882464): Tracking Bug + val ENABLE_DOCK_SETUP_V2 = unreleasedFlag(1005, "enable_dock_setup_v2") + // TODO(b/265045965): Tracking Bug val SHOW_LOWLIGHT_ON_DIRECT_BOOT = releasedFlag(1003, "show_lowlight_on_direct_boot") + @JvmField + val ENABLE_LOW_LIGHT_CLOCK_UNDOCKED = unreleasedFlag(1004, "enable_low_light_clock_undocked") + // 1100 - windowing @Keep @JvmField @@ -490,9 +493,9 @@ object Flags { val WM_ENABLE_PREDICTIVE_BACK_SYSUI = unreleasedFlag(1204, "persist.wm.debug.predictive_back_sysui_enable", teamfood = true) - // TODO(b/255697805): Tracking Bug + // TODO(b/270987164): Tracking Bug @JvmField - val TRACKPAD_GESTURE_BACK = unreleasedFlag(1205, "trackpad_gesture_back", teamfood = false) + val TRACKPAD_GESTURE_BACK = unreleasedFlag(1205, "trackpad_gesture_back", teamfood = true) // TODO(b/263826204): Tracking Bug @JvmField diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 02bee3efbe2f..2ad1ab722d55 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -127,8 +127,6 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.dagger.KeyguardModule; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -522,8 +520,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private CentralSurfaces mCentralSurfaces; - private boolean mUnocclusionTransitionFlagEnabled = false; - private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = new DeviceConfig.OnPropertiesChangedListener() { @Override @@ -970,9 +966,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { - if (!mUnocclusionTransitionFlagEnabled) { - setOccluded(true /* isOccluded */, true /* animate */); - } if (apps == null || apps.length == 0 || apps[0] == null) { if (DEBUG) { Log.d(TAG, "No apps provided to the OccludeByDream runner; " @@ -1023,7 +1016,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, @Override public void onAnimationEnd(Animator animation) { try { - if (!mIsCancelled && mUnocclusionTransitionFlagEnabled) { + if (!mIsCancelled) { // We're already on the main thread, don't queue this call handleSetOccluded(true /* isOccluded */, false /* animate */); @@ -1200,7 +1193,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, - FeatureFlags featureFlags, Lazy<ShadeController> shadeControllerLazy, Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy, Lazy<ActivityLaunchAnimator> activityLaunchAnimator, @@ -1259,7 +1251,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mDreamOpenAnimationDuration = (int) DREAMING_ANIMATION_DURATION_MS; mDreamCloseAnimationDuration = (int) LOCKSCREEN_ANIMATION_DURATION_MS; - mUnocclusionTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION); } public void userActivity() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 98d3570106ce..47ef0fac17ab 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -39,7 +39,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -113,7 +112,6 @@ public class KeyguardModule { ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, - FeatureFlags featureFlags, Lazy<ShadeController> shadeController, Lazy<NotificationShadeWindowController> notificationShadeWindowController, Lazy<ActivityLaunchAnimator> activityLaunchAnimator, @@ -144,7 +142,6 @@ public class KeyguardModule { screenOnCoordinator, interactionJankMonitor, dreamOverlayStateController, - featureFlags, shadeController, notificationShadeWindowController, activityLaunchAnimator, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt index be73f851fa82..ef0c9a175141 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt @@ -134,7 +134,9 @@ constructor( .flowOn(backgroundDispatcher) .distinctUntilChanged() .onEach { settingsValue = it } - ) { callbackFlowValue, _ -> callbackFlowValue } + ) { callbackFlowValue, _ -> + callbackFlowValue + } override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState { return if (controller.isZenAvailable) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt index 006678546de8..356a8fb65883 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt @@ -102,7 +102,8 @@ constructor( // setup). emit(Unit) } - ) { _, _ -> } + ) { _, _ -> + } .flatMapLatest { conflatedCallbackFlow { // We want to instantiate a new SharedPreferences instance each time either the diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt index baadc66170cc..84abf57cacf2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt @@ -24,8 +24,10 @@ import android.hardware.biometrics.BiometricManager import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback import android.os.Looper import android.os.UserHandle +import android.util.Log import com.android.internal.widget.LockPatternUtils import com.android.systemui.Dumpable +import com.android.systemui.R import com.android.systemui.biometrics.AuthController import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging @@ -35,6 +37,7 @@ 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.dump.DumpManager +import com.android.systemui.keyguard.shared.model.DevicePosture import com.android.systemui.user.data.repository.UserRepository import java.io.PrintWriter import javax.inject.Inject @@ -47,8 +50,10 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.transformLatest @@ -82,6 +87,12 @@ interface BiometricSettingsRepository { /** Whether fingerprint feature is enabled for the current user by the DevicePolicy */ val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> + + /** + * Whether face authentication is supported for the current device posture. Face auth can be + * restricted to specific postures using [R.integer.config_face_auth_supported_posture] + */ + val isFaceAuthSupportedInCurrentPosture: Flow<Boolean> } @SysUISingleton @@ -98,11 +109,27 @@ constructor( @Background backgroundDispatcher: CoroutineDispatcher, biometricManager: BiometricManager?, @Main looper: Looper, + devicePostureRepository: DevicePostureRepository, dumpManager: DumpManager, ) : BiometricSettingsRepository, Dumpable { + override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean> + init { dumpManager.registerDumpable(this) + val configFaceAuthSupportedPosture = + DevicePosture.toPosture( + context.resources.getInteger(R.integer.config_face_auth_supported_posture) + ) + isFaceAuthSupportedInCurrentPosture = + if (configFaceAuthSupportedPosture == DevicePosture.UNKNOWN) { + flowOf(true) + } else { + devicePostureRepository.currentDevicePosture.map { + it == configFaceAuthSupportedPosture + } + } + .onEach { Log.d(TAG, "isFaceAuthSupportedInCurrentPosture value changed to: $it") } } override fun dump(pw: PrintWriter, args: Array<String?>) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt new file mode 100644 index 000000000000..adb1e01d0d00 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.repository + +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.shared.model.DevicePosture +import com.android.systemui.statusbar.policy.DevicePostureController +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +/** Provide current device posture state. */ +interface DevicePostureRepository { + /** Provides the current device posture. */ + val currentDevicePosture: Flow<DevicePosture> +} + +@SysUISingleton +class DevicePostureRepositoryImpl +@Inject +constructor(private val postureController: DevicePostureController) : DevicePostureRepository { + override val currentDevicePosture: Flow<DevicePosture> + get() = conflatedCallbackFlow { + val sendPostureUpdate = { posture: Int -> + val currentDevicePosture = DevicePosture.toPosture(posture) + trySendWithFailureLogging( + currentDevicePosture, + TAG, + "Error sending posture update to $currentDevicePosture" + ) + } + val callback = DevicePostureController.Callback { sendPostureUpdate(it) } + postureController.addCallback(callback) + sendPostureUpdate(postureController.devicePosture) + + awaitClose { postureController.removeCallback(callback) } + } + + companion object { + const val TAG = "PostureRepositoryImpl" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt index 0e85347c24b0..86e5cd738120 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt @@ -53,6 +53,7 @@ interface KeyguardBouncerRepository { val primaryBouncerScrimmed: StateFlow<Boolean> /** * Set how much of the notification panel is showing on the screen. + * * ``` * 0f = panel fully hidden = bouncer fully showing * 1f = panel fully showing = bouncer fully hidden @@ -134,6 +135,7 @@ constructor( override val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow() /** * Set how much of the notification panel is showing on the screen. + * * ``` * 0f = panel fully hidden = bouncer fully showing * 1f = panel fully showing = bouncer fully hidden diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index a3b3d0fd0681..76f20d25b0ec 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -80,6 +80,9 @@ interface KeyguardRepository { */ val isKeyguardShowing: Flow<Boolean> + /** Is the keyguard in a unlocked state? */ + val isKeyguardUnlocked: Flow<Boolean> + /** Is an activity showing over the keyguard? */ val isKeyguardOccluded: Flow<Boolean> @@ -278,6 +281,31 @@ constructor( } .distinctUntilChanged() + override val isKeyguardUnlocked: Flow<Boolean> = + conflatedCallbackFlow { + val callback = + object : KeyguardStateController.Callback { + override fun onUnlockedChanged() { + trySendWithFailureLogging( + keyguardStateController.isUnlocked, + TAG, + "updated isKeyguardUnlocked" + ) + } + } + + keyguardStateController.addCallback(callback) + // Adding the callback does not send an initial update. + trySendWithFailureLogging( + keyguardStateController.isUnlocked, + TAG, + "initial isKeyguardUnlocked" + ) + + awaitClose { keyguardStateController.removeCallback(callback) } + } + .distinctUntilChanged() + override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow { val callback = object : KeyguardStateController.Callback { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt index 4a262f580749..f27f89995dbd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt @@ -31,6 +31,8 @@ interface KeyguardRepositoryModule { @Binds fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository + @Binds fun devicePostureRepository(impl: DevicePostureRepositoryImpl): DevicePostureRepository + @Binds fun biometricSettingsRepository( impl: BiometricSettingsRepositoryImpl diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index 0c4bca616e12..100bc596103d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -68,8 +68,11 @@ interface KeyguardTransitionRepository { /** * Begin a transition from one state to another. Transitions are interruptible, and will issue a * [TransitionStep] with state = [TransitionState.CANCELED] before beginning the next one. + * + * When canceled, there are two options: to continue from the current position of the prior + * transition, or to reset the position. When [resetIfCanceled] == true, it will do the latter. */ - fun startTransition(info: TransitionInfo): UUID? + fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean = false): UUID? /** * Allows manual control of a transition. When calling [startTransition], the consumer must pass @@ -130,7 +133,10 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio ) } - override fun startTransition(info: TransitionInfo): UUID? { + override fun startTransition( + info: TransitionInfo, + resetIfCanceled: Boolean, + ): UUID? { if (lastStep.from == info.from && lastStep.to == info.to) { Log.i(TAG, "Duplicate call to start the transition, rejecting: $info") return null @@ -138,7 +144,11 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio val startingValue = if (lastStep.transitionState != TransitionState.FINISHED) { Log.i(TAG, "Transition still active: $lastStep, canceling") - lastStep.value + if (resetIfCanceled) { + 0f + } else { + lastStep.value + } } else { 0f } @@ -227,10 +237,7 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio } private fun trace(step: TransitionStep, isManual: Boolean) { - if ( - step.transitionState != TransitionState.STARTED && - step.transitionState != TransitionState.FINISHED - ) { + if (step.transitionState == TransitionState.RUNNING) { return } val traceName = @@ -243,7 +250,10 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio val traceCookie = traceName.hashCode() if (step.transitionState == TransitionState.STARTED) { Trace.beginAsyncSection(traceName, traceCookie) - } else if (step.transitionState == TransitionState.FINISHED) { + } else if ( + step.transitionState == TransitionState.FINISHED || + step.transitionState == TransitionState.CANCELED + ) { Trace.endAsyncSection(traceName, traceCookie) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt index 014052956d2f..eae40d61cdb6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt @@ -50,6 +50,7 @@ constructor( /** * Sets the correct bouncer states to show the alternate bouncer if it can show. + * * @return whether alternateBouncer is visible */ fun show(): Boolean { @@ -74,6 +75,7 @@ constructor( * Sets the correct bouncer states to hide the bouncer. Should only be called through * StatusBarKeyguardViewManager until ScrimController is refactored to use * alternateBouncerInteractor. + * * @return true if the alternate bouncer was newly hidden, else false. */ fun hide(): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index 310f44da6e66..e6568f20bc20 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -71,8 +71,7 @@ constructor( isPrimaryBouncerShowing, lastStartedTransitionStep, wakefulnessState, - isAodAvailable - ) -> + isAodAvailable) -> if ( !isAlternateBouncerShowing && !isPrimaryBouncerShowing && diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 8715d1f55069..3beac0b1322e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -34,7 +34,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -57,14 +56,7 @@ constructor( private fun listenForDreamingToLockscreen() { scope.launch { - // Dependending on the dream, either dream state or occluded change will change first, - // so listen for both - combine(keyguardInteractor.isAbleToDream, keyguardInteractor.isKeyguardOccluded) { - isAbleToDream, - isKeyguardOccluded -> - isAbleToDream && isKeyguardOccluded - } - .distinctUntilChanged() + keyguardInteractor.isAbleToDream .sample( combine( keyguardInteractor.dozeTransitionModel, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index d01f48970c97..911861ddde47 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -130,55 +130,59 @@ constructor( shadeRepository.shadeModel .sample( combine( - keyguardTransitionInteractor.finishedKeyguardState, + keyguardTransitionInteractor.startedKeyguardTransitionStep, keyguardInteractor.statusBarState, - ::Pair + keyguardInteractor.isKeyguardUnlocked, + ::toTriple ), - ::toTriple + ::toQuad ) - .collect { (shadeModel, keyguardState, statusBarState) -> + .collect { (shadeModel, keyguardState, statusBarState, isKeyguardUnlocked) -> val id = transitionId if (id != null) { - // An existing `id` means a transition is started, and calls to - // `updateTransition` will control it until FINISHED or CANCELED - var nextState = - if (shadeModel.expansionAmount == 0f) { - TransitionState.FINISHED - } else if (shadeModel.expansionAmount == 1f) { - TransitionState.CANCELED - } else { - TransitionState.RUNNING - } - keyguardTransitionRepository.updateTransition( - id, - 1f - shadeModel.expansionAmount, - nextState, - ) + if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) { + // An existing `id` means a transition is started, and calls to + // `updateTransition` will control it until FINISHED or CANCELED + var nextState = + if (shadeModel.expansionAmount == 0f) { + TransitionState.FINISHED + } else if (shadeModel.expansionAmount == 1f) { + TransitionState.CANCELED + } else { + TransitionState.RUNNING + } + keyguardTransitionRepository.updateTransition( + id, + 1f - shadeModel.expansionAmount, + nextState, + ) - if ( - nextState == TransitionState.CANCELED || - nextState == TransitionState.FINISHED - ) { - transitionId = null - } + if ( + nextState == TransitionState.CANCELED || + nextState == TransitionState.FINISHED + ) { + transitionId = null + } - // If canceled, just put the state back - if (nextState == TransitionState.CANCELED) { - keyguardTransitionRepository.startTransition( - TransitionInfo( - ownerName = name, - from = KeyguardState.PRIMARY_BOUNCER, - to = KeyguardState.LOCKSCREEN, - animator = getAnimator(0.milliseconds) + // If canceled, just put the state back + if (nextState == TransitionState.CANCELED) { + keyguardTransitionRepository.startTransition( + TransitionInfo( + ownerName = name, + from = KeyguardState.PRIMARY_BOUNCER, + to = KeyguardState.LOCKSCREEN, + animator = getAnimator(0.milliseconds) + ) ) - ) + } } } else { // TODO (b/251849525): Remove statusbarstate check when that state is // integrated into KeyguardTransitionRepository if ( - keyguardState == KeyguardState.LOCKSCREEN && + keyguardState.to == KeyguardState.LOCKSCREEN && shadeModel.isUserDragging && + !isKeyguardUnlocked && statusBarState == KEYGUARD ) { transitionId = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt index b59b413d7a40..94961cbf4240 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt @@ -17,6 +17,9 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password +import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -26,6 +29,8 @@ import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.util.kotlin.sample import javax.inject.Inject +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -37,7 +42,8 @@ constructor( @Application private val scope: CoroutineScope, private val keyguardInteractor: KeyguardInteractor, private val keyguardTransitionRepository: KeyguardTransitionRepository, - private val keyguardTransitionInteractor: KeyguardTransitionInteractor + private val keyguardTransitionInteractor: KeyguardTransitionInteractor, + private val keyguardSecurityModel: KeyguardSecurityModel, ) : TransitionInteractor(FromPrimaryBouncerTransitionInteractor::class.simpleName!!) { override fun start() { @@ -93,31 +99,47 @@ constructor( private fun listenForPrimaryBouncerToGone() { scope.launch { keyguardInteractor.isKeyguardGoingAway - .sample(keyguardTransitionInteractor.finishedKeyguardState) { a, b -> Pair(a, b) } - .collect { pair -> - val (isKeyguardGoingAway, keyguardState) = pair - if (isKeyguardGoingAway && keyguardState == KeyguardState.PRIMARY_BOUNCER) { + .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) + .collect { (isKeyguardGoingAway, lastStartedTransitionStep) -> + if ( + isKeyguardGoingAway && + lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER + ) { + val securityMode = + keyguardSecurityModel.getSecurityMode( + KeyguardUpdateMonitor.getCurrentUser() + ) + // IME for password requires a slightly faster animation + val duration = + if (securityMode == Password) { + TO_GONE_SHORT_DURATION + } else { + TO_GONE_DURATION + } keyguardTransitionRepository.startTransition( TransitionInfo( ownerName = name, from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.GONE, - animator = getAnimator(), - ) + animator = getAnimator(duration), + ), + resetIfCanceled = true, ) } } } } - private fun getAnimator(): ValueAnimator { + private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator { return ValueAnimator().apply { setInterpolator(Interpolators.LINEAR) - setDuration(TRANSITION_DURATION_MS) + setDuration(duration.inWholeMilliseconds) } } companion object { - private const val TRANSITION_DURATION_MS = 300L + private val DEFAULT_DURATION = 300.milliseconds + val TO_GONE_DURATION = 250.milliseconds + val TO_GONE_SHORT_DURATION = 200.milliseconds } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index d25aff0add86..ec99049b42e3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -33,7 +33,9 @@ import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDoz import com.android.systemui.keyguard.shared.model.DozeTransitionModel import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.delay @@ -95,6 +97,9 @@ constructor( awaitClose { commandQueue.removeCallback(callback) } } + /** The device wake/sleep state */ + val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness + /** * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means * that doze mode is not running and DREAMING is ok to commence. @@ -109,6 +114,12 @@ constructor( isDreaming && isDozeOff(dozeTransitionModel.to) } ) + .sample( + wakefulnessModel, + { isAbleToDream, wakefulnessModel -> + isAbleToDream && isWakingOrStartingToWake(wakefulnessModel) + } + ) .flatMapLatest { isAbleToDream -> flow { delay(50) @@ -119,6 +130,8 @@ constructor( /** Whether the keyguard is showing or not. */ val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing + /** Whether the keyguard is unlocked or not. */ + val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked /** Whether the keyguard is occluded (covered by an activity). */ val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded /** Whether the keyguard is going away. */ @@ -127,8 +140,6 @@ constructor( val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerVisible /** Whether the alternate bouncer is showing or not. */ val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible - /** The device wake/sleep state */ - val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness /** Observable for the [StatusBarState] */ val statusBarState: Flow<StatusBarState> = repository.statusBarState /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index dfbe1c216847..bc3c7203ce3d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -26,6 +26,7 @@ import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.animation.Expandable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig @@ -119,7 +120,7 @@ constructor( * Notifies that a quick affordance has been "triggered" (clicked) by the user. * * @param configKey The configuration key corresponding to the [KeyguardQuickAffordanceModel] of - * the affordance that was clicked + * the affordance that was clicked * @param expandable An optional [Expandable] for the activity- or dialog-launch animation */ fun onQuickAffordanceTriggered( @@ -198,9 +199,9 @@ constructor( * * @param slotId The ID of the slot. * @param affordanceId The ID of the affordance to remove; if `null`, removes all affordances - * from the slot. + * from the slot. * @return `true` if the affordance was successfully removed; `false` otherwise (for example, if - * the affordance was not on the slot to begin with). + * the affordance was not on the slot to begin with). */ suspend fun unselect(slotId: String, affordanceId: String?): Boolean { check(isUsingRepository) @@ -410,16 +411,10 @@ constructor( ) } - private suspend fun isFeatureDisabledByDevicePolicy(): Boolean { - val flags = - withContext(backgroundDispatcher) { - devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId) - } - val flagsToCheck = - DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL or - DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL - return flagsToCheck and flags != 0 - } + private suspend fun isFeatureDisabledByDevicePolicy(): Boolean = + withContext(backgroundDispatcher) { + devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId) + } companion object { private const val TAG = "KeyguardQuickAffordanceInteractor" diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt index 51b02779a89f..e650b9fc0e47 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt @@ -61,7 +61,15 @@ constructor( } scope.launch { - keyguardInteractor.isDreaming.collect { logger.log(TAG, VERBOSE, "isDreaming", it) } + keyguardInteractor.isAbleToDream.collect { + logger.log(TAG, VERBOSE, "isAbleToDream", it) + } + } + + scope.launch { + keyguardInteractor.isKeyguardOccluded.collect { + logger.log(TAG, VERBOSE, "isOccluded", it) + } } scope.launch { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index 1b7da5b65a03..3c0ec350c5c5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -78,6 +78,10 @@ constructor( val occludedToLockscreenTransition: Flow<TransitionStep> = repository.transition(OCCLUDED, LOCKSCREEN) + /** PRIMARY_BOUNCER->GONE transition information. */ + val primaryBouncerToGoneTransition: Flow<TransitionStep> = + repository.transition(PRIMARY_BOUNCER, GONE) + /** * AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <-> * Lockscreen (0f). diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DevicePosture.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DevicePosture.kt new file mode 100644 index 000000000000..fff7cfe1d6a3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DevicePosture.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.shared.model + +import com.android.systemui.statusbar.policy.DevicePostureController + +/** Represents the possible posture states of the device. */ +enum class DevicePosture { + UNKNOWN, + CLOSED, + HALF_OPENED, + OPENED, + FLIPPED; + + companion object { + fun toPosture(@DevicePostureController.DevicePostureInt posture: Int): DevicePosture { + return when (posture) { + DevicePostureController.DEVICE_POSTURE_CLOSED -> CLOSED + DevicePostureController.DEVICE_POSTURE_HALF_OPENED -> HALF_OPENED + DevicePostureController.DEVICE_POSTURE_OPENED -> OPENED + DevicePostureController.DEVICE_POSTURE_FLIPPED -> FLIPPED + DevicePostureController.DEVICE_POSTURE_UNKNOWN -> UNKNOWN + else -> UNKNOWN + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt new file mode 100644 index 000000000000..a79513ebd867 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.shared.model + +import android.hardware.face.FaceAuthenticateOptions +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_ASSISTANT_VISIBLE +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_STARTED_WAKING_UP +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_UDFPS_POINTER_DOWN +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_UNKNOWN +import android.hardware.face.FaceAuthenticateOptions.AuthenticateReason +import android.os.PowerManager +import android.os.PowerManager.WAKE_REASON_UNKNOWN +import android.util.Log +import com.android.internal.logging.UiEvent +import com.android.internal.logging.UiEventLogger +import com.android.keyguard.FaceAuthUiEvent + +/** + * Wrapper for [FaceAuthenticateOptions] to convert SystemUI values to their corresponding value in + * [FaceAuthenticateOptions]. + */ +data class SysUiFaceAuthenticateOptions( + val userId: Int, + private val faceAuthUiEvent: UiEventLogger.UiEventEnum, + @PowerManager.WakeReason val wakeReason: Int = WAKE_REASON_UNKNOWN +) { + val authenticateReason = setAuthenticateReason(faceAuthUiEvent) + + /** + * The [FaceAuthUiEvent] for this operation. This method converts the UiEvent to the framework + * [AuthenticateReason]. + */ + @AuthenticateReason + fun setAuthenticateReason(uiEvent: UiEventLogger.UiEventEnum): Int { + return when (uiEvent) { + FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP -> { + AUTHENTICATE_REASON_STARTED_WAKING_UP + } + FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN, + FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN -> { + AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN + } + FaceAuthUiEvent.FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED -> { + AUTHENTICATE_REASON_ASSISTANT_VISIBLE + } + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN -> { + AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN + } + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED -> { + AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED + } + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED -> { + AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED + } + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED -> { + AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED + } + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER -> { + AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER + } + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_UDFPS_POINTER_DOWN -> { + AUTHENTICATE_REASON_UDFPS_POINTER_DOWN + } + else -> { + Log.e("FaceAuthenticateOptions", " unmapped FaceAuthUiEvent $uiEvent") + AUTHENTICATE_REASON_UNKNOWN + } + } + } + + /** Builds the instance. */ + fun toFaceAuthenticateOptions(): FaceAuthenticateOptions { + return FaceAuthenticateOptions.Builder() + .setUserId(userId) + .setAuthenticateReason(authenticateReason) + .setWakeReason(wakeReason) + .build() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt index 7db567b2a0e9..2337ffc35fa6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt @@ -31,6 +31,7 @@ import com.android.settingslib.Utils import com.android.systemui.keyguard.data.BouncerViewDelegate import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel +import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.ActivityStarter import kotlinx.coroutines.awaitCancellation @@ -44,6 +45,7 @@ object KeyguardBouncerViewBinder { fun bind( view: ViewGroup, viewModel: KeyguardBouncerViewModel, + primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel, componentFactory: KeyguardBouncerComponent.Factory ) { // Builds the KeyguardSecurityContainerController from bouncer view group. @@ -145,6 +147,12 @@ object KeyguardBouncerViewBinder { } launch { + primaryBouncerToGoneTransitionViewModel.bouncerAlpha.collect { alpha -> + securityContainerController.setAlpha(alpha) + } + } + + launch { viewModel.bouncerExpansionAmount .filter { it == EXPANSION_VISIBLE } .collect { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt index ef3f242a39a9..86717537efd3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt @@ -34,7 +34,7 @@ object KeyguardLongPressViewBinder { * @param viewModel The view-model that models the UI state. * @param onSingleTap A callback to invoke when the system decides that there was a single tap. * @param falsingManager [FalsingManager] for making sure the long-press didn't just happen in - * the user's pocket. + * the user's pocket. */ @JvmStatic fun bind( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt index 1e3b60c27d84..ab9e6a4ce045 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt @@ -135,7 +135,7 @@ constructor( * * @param initiallySelectedSlotId The ID of the initial slot to render as the selected one. * @param shouldHighlightSelectedAffordance Whether the selected quick affordance should be - * highlighted (while all others are dimmed to make the selected one stand out). + * highlighted (while all others are dimmed to make the selected one stand out). */ fun enablePreviewMode( initiallySelectedSlotId: String?, @@ -187,6 +187,7 @@ constructor( previewMode.isInPreviewMode && previewMode.shouldHighlightSelectedAffordance && !isSelected, + forceInactive = previewMode.isInPreviewMode ) } .distinctUntilChanged() @@ -198,6 +199,7 @@ constructor( isClickable: Boolean, isSelected: Boolean, isDimmed: Boolean, + forceInactive: Boolean, ): KeyguardQuickAffordanceViewModel { return when (this) { is KeyguardQuickAffordanceModel.Visible -> @@ -213,7 +215,7 @@ constructor( ) }, isClickable = isClickable, - isActivated = activationState is ActivationState.Active, + isActivated = !forceInactive && activationState is ActivationState.Active, isSelected = isSelected, useLongPress = quickAffordanceInteractor.useLongPress, isDimmed = isDimmed, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt new file mode 100644 index 000000000000..08907916a8c3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.flow.Flow + +/** + * Breaks down PRIMARY_BOUNCER->GONE transition into discrete steps for corresponding views to + * consume. + */ +@SysUISingleton +class PrimaryBouncerToGoneTransitionViewModel +@Inject +constructor( + private val interactor: KeyguardTransitionInteractor, +) { + private val transitionAnimation = + KeyguardTransitionAnimationFlow( + transitionDuration = TO_GONE_DURATION, + transitionFlow = interactor.primaryBouncerToGoneTransition, + ) + + /** Bouncer container alpha */ + val bouncerAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 200.milliseconds, + onStep = { 1f - it }, + ) + + /** Scrim alpha */ + val scrimAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = TO_GONE_DURATION, + interpolator = EMPHASIZED_ACCELERATE, + onStep = { 1f - it }, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt index d69ac7fe035d..34a67403fc84 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt @@ -47,13 +47,13 @@ import kotlinx.coroutines.launch * fresh one. * * @param coroutineContext An optional [CoroutineContext] to replace the dispatcher [block] is - * invoked on. + * invoked on. * @param block The block of code that should be run when the view becomes attached. It can end up - * being invoked multiple times if the view is reattached after being detached. + * being invoked multiple times if the view is reattached after being detached. * @return A [DisposableHandle] to invoke when the caller of the function destroys its [View] and is - * no longer interested in the [block] being run the next time its attached. Calling this is an - * optional optimization as the logic will be properly cleaned up and destroyed each time the view - * is detached. Using this is not *thread-safe* and should only be used on the main thread. + * no longer interested in the [block] being run the next time its attached. Calling this is an + * optional optimization as the logic will be properly cleaned up and destroyed each time the view + * is detached. Using this is not *thread-safe* and should only be used on the main thread. */ @MainThread fun View.repeatWhenAttached( @@ -125,7 +125,6 @@ private fun createLifecycleOwnerAndRun( * The implementation requires the caller to call [onCreate] and [onDestroy] when the view is * attached to or detached from a view hierarchy. After [onCreate] and before [onDestroy] is called, * the implementation monitors window state in the following way - * * * If the window is not visible, we are in the [Lifecycle.State.CREATED] state * * If the window is visible but not focused, we are in the [Lifecycle.State.STARTED] state * * If the window is visible and focused, we are in the [Lifecycle.State.RESUMED] state diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt index f7349a2a7ae6..647e3a15ba2f 100644 --- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt @@ -16,7 +16,6 @@ private const val TAG = "KeyguardFaceAuthManagerLog" * Helper class for logging for [com.android.keyguard.faceauth.KeyguardFaceAuthManager] * * To enable logcat echoing for an entire buffer: - * * ``` * adb shell settings put global systemui/buffer/KeyguardFaceAuthManagerLog <logLevel> * diff --git a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt index 5acaa46c25d6..edc278d1ae4f 100644 --- a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt @@ -33,7 +33,6 @@ private const val TAG = "ScreenDecorationsLog" * Helper class for logging for [com.android.systemui.ScreenDecorations] * * To enable logcat echoing for an entire buffer: - * * ``` * adb shell settings put global systemui/buffer/ScreenDecorationsLog <logLevel> * diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 220993f8ad4f..ca1ed1f5b0be 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -62,6 +62,15 @@ public class LogModule { return factory.create("NotifLog", maxSize, false /* systrace */); } + /** Provides a logging buffer for all logs related to notifications on the lockscreen. */ + @Provides + @SysUISingleton + @NotificationLockscreenLog + public static LogBuffer provideNotificationLockScreenLogBuffer( + LogBufferFactory factory) { + return factory.create("NotifLockscreenLog", 50, false /* systrace */); + } + /** Provides a logging buffer for logs related to heads up presentation of notifications. */ @Provides @SysUISingleton diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Columns.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLockscreenLog.java index 23ad53c007da..a2d381ec90f0 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Columns.kt +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLockscreenLog.java @@ -14,18 +14,20 @@ * limitations under the License. */ -package com.android.credentialmanager.common.ui +package com.android.systemui.log.dagger; -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.runtime.Composable -import androidx.compose.ui.unit.dp +import static java.lang.annotation.RetentionPolicy.RUNTIME; -@Composable -fun EntryListColumn(content: LazyListScope.() -> Unit) { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(2.dp), - content = content, - ) -}
\ No newline at end of file +import com.android.systemui.plugins.log.LogBuffer; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +/** A {@link LogBuffer} for notification & lockscreen related messages. */ +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface NotificationLockscreenLog { +} diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt index 1712dab8aff9..29f273a5ed41 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt @@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.Flow * * Some parts of System UI maintain a lot of pieces of state at once. * [com.android.systemui.plugins.log.LogBuffer] allows us to easily log change events: - * * - 10-10 10:10:10.456: state2 updated to newVal2 * - 10-10 10:11:00.000: stateN updated to StateN(val1=true, val2=1) * - 10-10 10:11:02.123: stateN updated to StateN(val1=true, val2=2) @@ -37,7 +36,6 @@ import kotlinx.coroutines.flow.Flow * - 10-10 10:11:06.000: stateN updated to StateN(val1=false, val2=3) * * However, it can sometimes be more useful to view the state changes in table format: - * * - timestamp--------- | state1- | state2- | ... | stateN.val1 | stateN.val2 * - ------------------------------------------------------------------------- * - 10-10 10:10:10.123 | val1--- | val2--- | ... | false------ | 0----------- @@ -56,23 +54,18 @@ import kotlinx.coroutines.flow.Flow * individual fields. * * How it works: - * * 1) Create an instance of this buffer via [TableLogBufferFactory]. - * * 2) For any states being logged, implement [Diffable]. Implementing [Diffable] allows the state to - * only log the fields that have *changed* since the previous update, instead of always logging all - * fields. - * + * only log the fields that have *changed* since the previous update, instead of always logging + * all fields. * 3) Each time a change in a state happens, call [logDiffs]. If your state is emitted using a - * [Flow], you should use the [logDiffsForTable] extension function to automatically log diffs any - * time your flow emits a new value. + * [Flow], you should use the [logDiffsForTable] extension function to automatically log diffs + * any time your flow emits a new value. * * When a dump occurs, there will be two dumps: - * * 1) The change events under the dumpable name "$name-changes". - * * 2) This class will coalesce all the diffs into a table format and log them under the dumpable - * name "$name-table". + * name "$name-table". * * @param maxSize the maximum size of the buffer. Must be > 0. */ @@ -99,11 +92,10 @@ class TableLogBuffer( * The [newVal] object's method [Diffable.logDiffs] will be used to fetch the diffs. * * @param columnPrefix a prefix that will be applied to every column name that gets logged. This - * ensures that all the columns related to the same state object will be grouped together in the - * table. - * + * ensures that all the columns related to the same state object will be grouped together in + * the table. * @throws IllegalArgumentException if [columnPrefix] or column name contain "|". "|" is used as - * the separator token for parsing, so it can't be present in any part of the column name. + * the separator token for parsing, so it can't be present in any part of the column name. */ @Synchronized fun <T : Diffable<T>> logDiffs(columnPrefix: String, prevVal: T, newVal: T) { @@ -117,7 +109,7 @@ class TableLogBuffer( * Logs change(s) to the buffer using [rowInitializer]. * * @param rowInitializer a function that will be called immediately to store relevant data on - * the row. + * the row. */ @Synchronized fun logChange(columnPrefix: String, rowInitializer: (TableRowLogger) -> Unit) { diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt index 7ccc43ce62c2..06668d33408d 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt @@ -38,7 +38,6 @@ constructor( * * @param name a unique table name * @param maxSize the buffer max size. See [adjustMaxSize] - * * @return a new [TableLogBuffer] registered with [DumpManager] */ fun create( diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt index a057c9f22be3..2509f21242cd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt @@ -187,6 +187,7 @@ constructor( /** * Handle request to change the current position in the media track. + * * @param position Place to seek to in the track. */ @AnyThread diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt index 0b57175defe7..ae03f27b32cd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt @@ -52,6 +52,7 @@ data class SmartspaceMediaData( * Indicates if all the data is valid. * * TODO(b/230333302): Make MediaControlPanel more flexible so that we can display fewer than + * * ``` * [NUM_REQUIRED_RECOMMENDATIONS]. * ``` diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt index 97717a64ce26..207df6bc4422 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt @@ -329,9 +329,8 @@ constructor( * Return the time since last active for the most-recent media. * * @param sortedEntries userEntries sorted from the earliest to the most-recent. - * * @return The duration in milliseconds from the most-recent media's last active timestamp to - * the present. MAX_VALUE will be returned if there is no media. + * the present. MAX_VALUE will be returned if there is no media. */ private fun timeSinceActiveForMostRecentMedia( sortedEntries: SortedMap<String, MediaData> diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt index af60e0e2df76..72c4aabd44b9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt @@ -526,8 +526,8 @@ class MediaDataManager( * through the internal listener pipeline. * * @param immediately indicates should apply the UI changes immediately, otherwise wait until - * the next refresh-round before UI becomes visible. Should only be true if the update is - * initiated by user's interaction. + * the next refresh-round before UI becomes visible. Should only be true if the update is + * initiated by user's interaction. */ private fun notifySmartspaceMediaDataRemoved(key: String, immediately: Boolean) { internalListeners.forEach { it.onSmartspaceMediaDataRemoved(key, immediately) } @@ -536,6 +536,7 @@ class MediaDataManager( /** * Called whenever the player has been paused or stopped for a while, or swiped from QQS. This * will make the player not active anymore, hiding it from QQS and Keyguard. + * * @see MediaData.active */ internal fun setTimedOut(key: String, timedOut: Boolean, forceUpdate: Boolean = false) { @@ -1024,6 +1025,7 @@ class MediaDataManager( * @param packageName Package name for the media app * @param controller MediaController for the current session * @return a Pair consisting of a list of media actions, and a list of ints representing which + * * ``` * of those actions should be shown in the compact player * ``` @@ -1127,6 +1129,7 @@ class MediaDataManager( * [PlaybackState.ACTION_SKIP_TO_NEXT] * @return * ``` + * * A [MediaAction] with correct values set, or null if the state doesn't support it */ private fun getStandardAction( @@ -1229,6 +1232,7 @@ class MediaDataManager( } /** * Load a bitmap from a URI + * * @param uri the uri to load * @return bitmap, or null if couldn't be loaded */ @@ -1342,10 +1346,13 @@ class MediaDataManager( fun onNotificationRemoved(key: String) { Assert.isMainThread() val removed = mediaEntries.remove(key) ?: return - + val isEligibleForResume = + removed.isLocalSession() || + (mediaFlags.isRemoteResumeAllowed() && + removed.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE) if (keyguardUpdateMonitor.isUserInLockdown(removed.userId)) { logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) - } else if (useMediaResumption && removed.resumeAction != null && removed.isLocalSession()) { + } else if (useMediaResumption && removed.resumeAction != null && isEligibleForResume) { convertToResumePlayer(key, removed) } else if (mediaFlags.isRetainingPlayersEnabled()) { handlePossibleRemoval(key, removed, notificationRemoved = true) @@ -1519,15 +1526,13 @@ class MediaDataManager( * notification key) or vice versa. * * @param immediately indicates should apply the UI changes immediately, otherwise wait - * until the next refresh-round before UI becomes visible. True by default to take in place - * immediately. - * + * until the next refresh-round before UI becomes visible. True by default to take in + * place immediately. * @param receivedSmartspaceCardLatency is the latency between headphone connects and sysUI - * displays Smartspace media targets. Will be 0 if the data is not activated by Smartspace - * signal. - * + * displays Smartspace media targets. Will be 0 if the data is not activated by Smartspace + * signal. * @param isSsReactivated indicates resume media card is reactivated by Smartspace - * recommendation signal + * recommendation signal */ fun onMediaDataLoaded( key: String, @@ -1542,8 +1547,8 @@ class MediaDataManager( * Called whenever there's new Smartspace media data loaded. * * @param shouldPrioritize indicates the sorting priority of the Smartspace card. If true, - * it will be prioritized as the first card. Otherwise, it will show up as the last card as - * default. + * it will be prioritized as the first card. Otherwise, it will show up as the last card + * as default. */ fun onSmartspaceMediaDataLoaded( key: String, @@ -1558,8 +1563,8 @@ class MediaDataManager( * Called whenever a previously existing Smartspace media data was removed. * * @param immediately indicates should apply the UI changes immediately, otherwise wait - * until the next refresh-round before UI becomes visible. True by default to take in place - * immediately. + * until the next refresh-round before UI becomes visible. True by default to take in + * place immediately. */ fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean = true) {} } @@ -1568,7 +1573,7 @@ class MediaDataManager( * Converts the pass-in SmartspaceTarget to SmartspaceMediaData * * @return An empty SmartspaceMediaData with the valid target Id is returned if the - * SmartspaceTarget's data is invalid. + * SmartspaceTarget's data is invalid. */ private fun toSmartspaceMediaData(target: SmartspaceTarget): SmartspaceMediaData { var dismissIntent: Intent? = null diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt index 6a512be091e1..120704c0582a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt @@ -408,9 +408,9 @@ constructor( * [LocalMediaManager.DeviceCallback.onAboutToConnectDeviceAdded] for more information. * * @property fullMediaDevice a full-fledged [MediaDevice] object representing the device. If - * non-null, prefer using [fullMediaDevice] over [backupMediaDeviceData]. + * non-null, prefer using [fullMediaDevice] over [backupMediaDeviceData]. * @property backupMediaDeviceData a backup [MediaDeviceData] object containing the minimum - * information required to display the device. Only use if [fullMediaDevice] is null. + * information required to display the device. Only use if [fullMediaDevice] is null. */ private data class AboutToConnectDevice( val fullMediaDevice: MediaDevice? = null, diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt index 878962dc60b4..a1d9214cb215 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt @@ -60,6 +60,7 @@ constructor( /** * Callback representing that a media object is now expired: + * * @param key Media control unique identifier * @param timedOut True when expired for {@code PAUSED_MEDIA_TIMEOUT} for active media, * ``` @@ -70,6 +71,7 @@ constructor( /** * Callback representing that a media object [PlaybackState] has changed. + * * @param key Media control unique identifier * @param state The new [PlaybackState] */ @@ -77,6 +79,7 @@ constructor( /** * Callback representing that the [MediaSession] for an active control has been destroyed + * * @param key Media control unique identifier */ lateinit var sessionCallback: (String) -> Unit diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt index 2d10b823f784..92e0c851a462 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt @@ -37,6 +37,7 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.media.controls.models.player.MediaData import com.android.systemui.media.controls.pipeline.MediaDataManager import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT +import com.android.systemui.media.controls.util.MediaFlags import com.android.systemui.settings.UserTracker import com.android.systemui.tuner.TunerService import com.android.systemui.util.Utils @@ -63,7 +64,8 @@ constructor( private val tunerService: TunerService, private val mediaBrowserFactory: ResumeMediaBrowserFactory, dumpManager: DumpManager, - private val systemClock: SystemClock + private val systemClock: SystemClock, + private val mediaFlags: MediaFlags, ) : MediaDataManager.Listener, Dumpable { private var useMediaResumption: Boolean = Utils.useMediaResumption(context) @@ -231,7 +233,11 @@ constructor( mediaBrowser = null } // If we don't have a resume action, check if we haven't already - if (data.resumeAction == null && !data.hasCheckedForResume && data.isLocalSession()) { + val isEligibleForResume = + data.isLocalSession() || + (mediaFlags.isRemoteResumeAllowed() && + data.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE) + if (data.resumeAction == null && !data.hasCheckedForResume && isEligibleForResume) { // TODO also check for a media button receiver intended for restarting (b/154127084) Log.d(TAG, "Checking for service component for " + data.packageName) val pm = context.packageManager @@ -291,6 +297,7 @@ constructor( /** * Add the component to the saved list of media browser services, checking for duplicates and * removing older components that exceed the maximum limit + * * @param componentName */ private fun updateResumptionList(componentName: ComponentName) { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt index 335ce1d3d694..095cf09a6c2c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt @@ -52,10 +52,12 @@ class ResumeMediaBrowserLogger @Inject constructor(@MediaBrowserLog private val * event. * * @param isBrowserConnected true if there's a currently connected + * * ``` * [android.media.browse.MediaBrowser] and false otherwise. * @param componentName * ``` + * * the component name for the [ResumeMediaBrowser] that triggered this log. */ fun logSessionDestroyed(isBrowserConnected: Boolean, componentName: ComponentName) = diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt index d2793bca867b..f5cc04331f94 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt @@ -24,10 +24,12 @@ import android.graphics.drawable.Drawable * and conflicts due to media notifications arriving at any time during an animation. It does this * in two parts. * - Exit animations fired as a result of user input are tracked. When these are running, any + * * ``` * bind actions are delayed until the animation completes (and then fired in sequence). * ``` * - Continuous animations are tracked using their rebind id. Later calls using the same + * * ``` * rebind id will be totally ignored to prevent the continuous animation from restarting. * ``` diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt index 4827a16d229d..2b42604e7160 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt @@ -201,7 +201,9 @@ internal constructor( animatingColorTransitionFactory( loadDefaultColor(R.attr.textColorSecondary), ::textSecondaryFromScheme - ) { textSecondary -> mediaViewHolder.artistText.setTextColor(textSecondary) } + ) { textSecondary -> + mediaViewHolder.artistText.setTextColor(textSecondary) + } val textTertiary = animatingColorTransitionFactory( diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt index 9f86cd88788b..3669493f4e41 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt @@ -159,6 +159,7 @@ class IlluminationDrawable : Drawable() { /** * Cross fade background. + * * @see setTintList * @see backgroundColor */ diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt index 6cf051ad7668..680a8b6603d6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt @@ -853,10 +853,12 @@ constructor( * @param startLocation the start location of our state or -1 if this is directly set * @param endLocation the ending location of our state. * @param progress the progress of the transition between startLocation and endlocation. If + * * ``` * this is not a guided transformation, this will be 1.0f * @param immediately * ``` + * * should this state be applied immediately, canceling all animations? */ fun setCurrentState( @@ -1100,17 +1102,17 @@ constructor( * * @param eventId UI event id (e.g. 800 for SMARTSPACE_CARD_SEEN) * @param instanceId id to uniquely identify a card, e.g. each headphone generates a new - * instanceId + * instanceId * @param uid uid for the application that media comes from * @param surfaces list of display surfaces the media card is on (e.g. lockscreen, shade) when - * the event happened + * the event happened * @param interactedSubcardRank the rank for interacted media item for recommendation card, -1 - * for tapping on card but not on any media item, 0 for first media item, 1 for second, etc. + * for tapping on card but not on any media item, 0 for first media item, 1 for second, etc. * @param interactedSubcardCardinality how many media items were shown to the user when there is - * user interaction + * user interaction * @param rank the rank for media card in the media carousel, starting from 0 * @param receivedLatencyMillis latency in milliseconds for card received events. E.g. latency - * between headphone connection to sysUI displays media recommendation card + * between headphone connection to sysUI displays media recommendation card * @param isSwipeToDismiss whether is to log swipe-to-dismiss event */ fun logSmartspaceCardReported( @@ -1371,6 +1373,7 @@ internal object MediaPlayerData { /** * Removes media player given the key. + * * @param isDismissed determines whether the media player is removed from the carousel. */ fun removeMediaPlayer(key: String, isDismissed: Boolean = false) = diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt index 66f12d6242b0..7fc7bdb872c9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt @@ -28,7 +28,6 @@ import android.net.Uri import android.os.Handler import android.os.UserHandle import android.provider.Settings -import android.util.Log import android.util.MathUtils import android.view.View import android.view.ViewGroup @@ -418,8 +417,8 @@ constructor( * Calculate the alpha of the view when given a cross-fade progress. * * @param crossFadeProgress The current cross fade progress. 0.5f means it's just switching - * between the start and the end location and the content is fully faded, while 0.75f means that - * we're halfway faded in again in the target state. + * between the start and the end location and the content is fully faded, while 0.75f means + * that we're halfway faded in again in the target state. */ private fun calculateAlphaFromCrossFade(crossFadeProgress: Float): Float { if (crossFadeProgress <= 0.5f) { @@ -629,6 +628,7 @@ constructor( * * @param forceNoAnimation optional parameter telling the system not to animate * @param forceStateUpdate optional parameter telling the system to update transition state + * * ``` * even if location did not change * ``` @@ -944,7 +944,7 @@ constructor( /** * @return the current transformation progress if we're in a guided transformation and -1 - * otherwise + * otherwise */ private fun getTransformationProgress(): Float { if (skipQqsOnExpansion) { @@ -1055,17 +1055,6 @@ constructor( // This will either do a full layout pass and remeasure, or it will bypass // that and directly set the mediaFrame's bounds within the premeasured host. targetHost.addView(mediaFrame) - - if (mediaFrame.childCount > 0) { - val child = mediaFrame.getChildAt(0) - if (mediaFrame.height < child.height) { - Log.wtf( - TAG, - "mediaFrame height is too small for child: " + - "${mediaFrame.height} vs ${child.height}" - ) - } - } } if (isCrossFadeAnimatorRunning) { // When cross-fading with an animation, we only notify the media carousel of the diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt index 455b7de3dc0c..be570b4a1119 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt @@ -126,6 +126,7 @@ constructor( * remeasurings later on. * * @param location the location this host name has. Used to identify the host during + * * ``` * transitions. * ``` diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt index b9b0459ad615..0788e6172a78 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt @@ -348,14 +348,17 @@ constructor( * bottom of UMO reach the bottom of this group It will change to alpha 1.0 when the visible * bottom of UMO reach the top of the group below e.g.Album title, artist title and play-pause * button will change alpha together. + * * ``` * And their alpha becomes 1.0 when the visible bottom of UMO reach the top of controls, * including progress bar, next button, previous button * ``` + * * widgetGroupIds: a group of widgets have same state during UMO is squished, * ``` * e.g. Album title, artist title and play-pause button * ``` + * * groupEndPosition: the height of UMO, when the height reaches this value, * ``` * widgets in this group should have 1.0 as alpha @@ -363,6 +366,7 @@ constructor( * visible when the height of UMO reaches the top of controls group * (progress bar, previous button and next button) * ``` + * * squishedViewState: hold the widgetState of each widget, which will be modified * squishFraction: the squishFraction of UMO */ @@ -665,7 +669,7 @@ constructor( * * @param location Target * @param locationWhenHidden Location that will be used when the target is not - * [MediaHost.visible] + * [MediaHost.visible] * @return State require for executing a transition, and also the respective [MediaHost]. */ private fun obtainViewStateForLocation(@MediaLocation location: Int): TransitionViewState? { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt index c3fa76ec9433..9bc66f6c98d0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt @@ -61,4 +61,7 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) { /** If true, do not automatically dismiss the recommendation card */ fun isPersistentSsCardEnabled() = featureFlags.isEnabled(Flags.MEDIA_RETAIN_RECOMMENDATIONS) + + /** Check whether we allow remote media to generate resume controls */ + fun isRemoteResumeAllowed() = featureFlags.isEnabled(Flags.MEDIA_REMOTE_RESUME) } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt index 720c44a0904b..ee93c3788243 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt @@ -43,7 +43,7 @@ class MediaTttUtils { * * @param appPackageName the package name of the app playing the media. * @param onPackageNotFoundException a function run if a - * [PackageManager.NameNotFoundException] occurs. + * [PackageManager.NameNotFoundException] occurs. * @param isReceiver indicates whether the icon is displayed in a receiver view. */ fun getIconInfoFromPackageName( 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 7a77c476aa11..01398cf81314 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 @@ -70,6 +70,8 @@ constructor( RECENT_IGNORE_UNAVAILABLE, userTracker.userId, backgroundExecutor - ) { tasks -> continuation.resume(tasks) } + ) { tasks -> + continuation.resume(tasks) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index f3d60145a057..342e0b006c18 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -234,6 +234,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private boolean mLogGesture = false; private boolean mInRejectedExclusion = false; private boolean mIsOnLeftEdge; + private boolean mDeferSetIsOnLeftEdge; private boolean mIsAttached; private boolean mIsGesturalModeEnabled; @@ -878,7 +879,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack // either the bouncer is showing or the notification panel is hidden mInputEventReceiver.setBatchingEnabled(false); if (isTrackpadEvent) { - // TODO: show the back arrow based on the direction of the swipe. + // Since trackpad gestures don't have zones, this will be determined later by the + // direction of the gesture. {@code mIsOnLeftEdge} is set to false to begin with. + mDeferSetIsOnLeftEdge = true; mIsOnLeftEdge = false; } else { mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset; @@ -899,7 +902,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mEdgeBackPlugin.onMotionEvent(ev); dispatchToBackAnimation(ev); } - if (mLogGesture) { + if (mLogGesture || isTrackpadEvent) { mDownPoint.set(ev.getX(), ev.getY()); mEndPoint.set(-1, -1); mThresholdCrossed = false; @@ -907,9 +910,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack // For debugging purposes, only log edge points (isWithinInsets ? mGestureLogInsideInsets : mGestureLogOutsideInsets).log(String.format( - "Gesture [%d,alw=%B,%B,%B,%B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]", + "Gesture [%d,alw=%B,%B,%B,%B,%B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]", System.currentTimeMillis(), isTrackpadEvent, mAllowGesture, mIsOnLeftEdge, - mIsBackGestureAllowed, + mDeferSetIsOnLeftEdge, mIsBackGestureAllowed, QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize, mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion)); } else if (mAllowGesture || mLogGesture) { @@ -928,6 +931,14 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mLogGesture = false; return; } else if (action == MotionEvent.ACTION_MOVE) { + if (isTrackpadEvent && mDeferSetIsOnLeftEdge) { + // mIsOnLeftEdge is determined by the relative position between the down + // and the current motion event for trackpad gestures instead of zoning. + mIsOnLeftEdge = mEndPoint.x > mDownPoint.x; + mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge); + mDeferSetIsOnLeftEdge = false; + } + if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) { if (mAllowGesture) { logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_LONG_PRESS); diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index f7b7db4e366f..c65f0aaab91f 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -17,6 +17,7 @@ package com.android.systemui.notetask import android.app.KeyguardManager +import android.app.admin.DevicePolicyManager import android.content.ActivityNotFoundException import android.content.ComponentName import android.content.Context @@ -27,7 +28,9 @@ import android.os.UserManager import android.util.Log import androidx.annotation.VisibleForTesting import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity +import com.android.systemui.settings.UserTracker import com.android.systemui.util.kotlin.getOrNull import com.android.wm.shell.bubbles.Bubble import com.android.wm.shell.bubbles.Bubbles @@ -54,6 +57,8 @@ constructor( private val optionalUserManager: Optional<UserManager>, private val optionalKeyguardManager: Optional<KeyguardManager>, @NoteTaskEnabledKey private val isEnabled: Boolean, + private val devicePolicyManager: DevicePolicyManager, + private val userTracker: UserTracker, ) { @VisibleForTesting val infoReference = AtomicReference<NoteTaskInfo?>() @@ -107,11 +112,23 @@ constructor( // TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing. if (!userManager.isUserUnlocked) return + val isKeyguardLocked = keyguardManager.isKeyguardLocked + // KeyguardQuickAffordanceInteractor blocks the quick affordance from showing in the + // keyguard if it is not allowed by the admin policy. Here we block any other way to show + // note task when the screen is locked. + if ( + isKeyguardLocked && + devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId) + ) { + logDebug { "Enterprise policy disallows launching note app when the screen is locked." } + return + } + val info = resolver.resolveInfo( entryPoint = entryPoint, isInMultiWindowMode = isInMultiWindowMode, - isKeyguardLocked = keyguardManager.isKeyguardLocked, + isKeyguardLocked = isKeyguardLocked, ) ?: return diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt index b5d757c6c287..0f75f9591074 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt @@ -31,6 +31,7 @@ constructor( private val roleManager: RoleManager, private val packageManager: PackageManager, ) { + fun resolveInfo( entryPoint: NoteTaskEntryPoint? = null, isInMultiWindowMode: Boolean = false, @@ -38,7 +39,8 @@ constructor( ): NoteTaskInfo? { // TODO(b/267634412): Select UserHandle depending on where the user initiated note-taking. val user = context.user - val packageName = roleManager.getRoleHoldersAsUser(ROLE_NOTES, user).firstOrNull() + val packageName = + roleManager.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, user).firstOrNull() if (packageName.isNullOrEmpty()) return null diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt index 3f4f8d538bf9..fb3c0cb54f84 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.notetask +import android.view.KeyEvent import androidx.annotation.VisibleForTesting import com.android.systemui.statusbar.CommandQueue import com.android.wm.shell.bubbles.Bubbles @@ -35,7 +36,7 @@ constructor( val callbacks = object : CommandQueue.Callbacks { override fun handleSystemKey(keyCode: Int) { - if (keyCode == NoteTaskController.NOTE_TASK_KEY_EVENT) { + if (keyCode == KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) { controller.showNoteTask(NoteTaskEntryPoint.TAIL_BUTTON) } } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt index f16110d7cf5b..ba8999c068e3 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt @@ -51,7 +51,7 @@ interface NoteTaskModule { featureFlags: FeatureFlags, roleManager: RoleManager, ): Boolean { - val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskInfoResolver.ROLE_NOTES) + val isRoleAvailable = roleManager.isRoleAvailable(RoleManager.ROLE_NOTES) val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS) return isRoleAvailable && isFeatureEnabled } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt index 0a1d0089735f..8ced46461dbb 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt @@ -33,8 +33,8 @@ import javax.inject.Inject * launched, creating a new shortcut for [CreateNoteTaskShortcutActivity], and will finish. * * @see <a - * href="https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#custom-pinned">Creating - * a custom shortcut activity</a> + * href="https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#custom-pinned">Creating + * a custom shortcut activity</a> */ class CreateNoteTaskShortcutActivity @Inject constructor() : ComponentActivity() { diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt index 2b84bf8b4e2e..80fce6ae288b 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt @@ -49,7 +49,7 @@ constructor( return Intent(context, LaunchNoteTaskActivity::class.java).apply { // Intent's action must be set in shortcuts, or an exception will be thrown. // TODO(b/254606432): Use Intent.ACTION_CREATE_NOTE instead. - action = NoteTaskController.ACTION_CREATE_NOTE + action = Intent.ACTION_CREATE_NOTE } } } diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt index ff3ec72e6bae..d40112fd5660 100644 --- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt @@ -26,8 +26,7 @@ import dagger.multibindings.StringKey @Module interface QRCodeScannerModule { - /** - */ + /** */ @Binds @IntoMap @StringKey(QRCodeScannerTile.TILE_SPEC) diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt index 03bb7a0f45da..8387c1dd60a5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt @@ -71,8 +71,8 @@ interface FooterActionsInteractor { /** * Show the device monitoring dialog, expanded from [expandable] if it's not null. * - * Important: [quickSettingsContext] *must* be the [Context] associated to the [Quick Settings - * fragment][com.android.systemui.qs.QSFragment]. + * Important: [quickSettingsContext] *must* be the [Context] associated to the + * [Quick Settings fragment][com.android.systemui.qs.QSFragment]. */ fun showDeviceMonitoringDialog(quickSettingsContext: Context, expandable: Expandable?) diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt index fbf32b3b99ea..f170ac1d9d4e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt @@ -196,9 +196,9 @@ class FooterActionsViewModel( * Observe the device monitoring dialog requests and show the dialog accordingly. This function * will suspend indefinitely and will need to be cancelled to stop observing. * - * Important: [quickSettingsContext] must be the [Context] associated to the [Quick Settings - * fragment][com.android.systemui.qs.QSFragment], and the call to this function must be - * cancelled when that fragment is destroyed. + * Important: [quickSettingsContext] must be the [Context] associated to the + * [Quick Settings fragment][com.android.systemui.qs.QSFragment], and the call to this function + * must be cancelled when that fragment is destroyed. */ suspend fun observeDeviceMonitoringDialogRequests(quickSettingsContext: Context) { footerActionsInteractor.deviceMonitoringDialogRequests.collect { diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt index dd21be971b36..30509e23d186 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt @@ -124,8 +124,9 @@ class ScreenRecordPermissionDialog( /** * Starts screen capture after some countdown + * * @param captureTarget target to capture (could be e.g. a task) or null to record the whole - * screen + * screen */ private fun requestScreenCapture(captureTarget: MediaProjectionCaptureTarget?) { val userContext = userContextProvider.userContext diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt index 310baafbae1a..a8f99bef2423 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt @@ -70,7 +70,7 @@ object ActionIntentCreator { /** * @return an ACTION_EDIT intent for the given URI, directed to config_screenshotEditor if - * available. + * available. */ fun createEditIntent(uri: Uri, context: Context): Intent { val editIntent = Intent(Intent.ACTION_EDIT) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt index 1b728b8aa9cc..236213cb023f 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt @@ -44,7 +44,7 @@ constructor( /** * @return a populated WorkProfileFirstRunData object if a work profile first run message should - * be shown + * be shown */ fun onScreenshotTaken(userHandle: UserHandle?): WorkProfileFirstRunData? { if (userHandle == null) return null diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java index b36f0d7bacfc..10e2afe0baa9 100644 --- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java @@ -27,6 +27,7 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.Xfermode; import android.graphics.drawable.Drawable; import android.view.animation.DecelerateInterpolator; @@ -41,7 +42,11 @@ import com.android.systemui.statusbar.notification.stack.StackStateAnimator; public class ScrimDrawable extends Drawable { private static final String TAG = "ScrimDrawable"; + private boolean mShouldUseLargeScreenSize; private final Paint mPaint; + private final Path mPath = new Path(); + private final RectF mBoundsRectF = new RectF(); + private int mAlpha = 255; private int mMainColor; private ValueAnimator mColorAnimation; @@ -49,11 +54,13 @@ public class ScrimDrawable extends Drawable { private float mCornerRadius; private ConcaveInfo mConcaveInfo; private int mBottomEdgePosition; + private float mBottomEdgeRadius = -1; private boolean mCornerRadiusEnabled; public ScrimDrawable() { mPaint = new Paint(); mPaint.setStyle(Paint.Style.FILL); + mShouldUseLargeScreenSize = false; } /** @@ -133,6 +140,10 @@ public class ScrimDrawable extends Drawable { return PixelFormat.TRANSLUCENT; } + public void setShouldUseLargeScreenSize(boolean v) { + mShouldUseLargeScreenSize = v; + } + /** * Corner radius used by either concave or convex corners. */ @@ -191,6 +202,10 @@ public class ScrimDrawable extends Drawable { invalidateSelf(); } + public void setBottomEdgeRadius(float radius) { + mBottomEdgeRadius = radius; + } + @Override public void draw(@NonNull Canvas canvas) { mPaint.setColor(mMainColor); @@ -198,9 +213,46 @@ public class ScrimDrawable extends Drawable { if (mConcaveInfo != null) { drawConcave(canvas); } else if (mCornerRadiusEnabled && mCornerRadius > 0) { - canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right, - getBounds().bottom, - /* x radius*/ mCornerRadius, /* y radius*/ mCornerRadius, mPaint); + float topEdgeRadius = mCornerRadius; + float bottomEdgeRadius = mBottomEdgeRadius == -1.0 ? mCornerRadius : mBottomEdgeRadius; + + mBoundsRectF.set(getBounds()); + + // When the back gesture causes the notification scrim to be scaled down, + // this offset "reveals" the rounded bottom edge as it "pulls away". + // We must *not* make this adjustment on largescreen shades (where the corner is sharp). + if (!mShouldUseLargeScreenSize && mBottomEdgeRadius != -1) { + mBoundsRectF.bottom -= bottomEdgeRadius; + } + + // We need a box with rounded corners but its lower corners are not rounded on large + // screen devices in "portrait" orientation. + // Thus, we cannot draw a symmetric rounded rectangle via canvas.drawRoundRect() + // and must build a box with different corner radii at the top and at the bottom. + // Additionally, when the scrim is pushed to the very bottom of the screen, do not draw + // anything (drawing a rounded box with these specifications is not possible). + // TODO(b/271030611) perhaps this could be accomplished via Path.addRoundRect instead? + if (mBoundsRectF.bottom - mBoundsRectF.top > bottomEdgeRadius) { + mPath.reset(); + mPath.moveTo(mBoundsRectF.right, mBoundsRectF.top + topEdgeRadius); + mPath.cubicTo(mBoundsRectF.right, mBoundsRectF.top + topEdgeRadius, + mBoundsRectF.right, mBoundsRectF.top, + mBoundsRectF.right - topEdgeRadius, mBoundsRectF.top); + mPath.lineTo(mBoundsRectF.left + topEdgeRadius, mBoundsRectF.top); + mPath.cubicTo(mBoundsRectF.left + topEdgeRadius, mBoundsRectF.top, + mBoundsRectF.left, mBoundsRectF.top, + mBoundsRectF.left, mBoundsRectF.top + topEdgeRadius); + mPath.lineTo(mBoundsRectF.left, mBoundsRectF.bottom - bottomEdgeRadius); + mPath.cubicTo(mBoundsRectF.left, mBoundsRectF.bottom - bottomEdgeRadius, + mBoundsRectF.left, mBoundsRectF.bottom, + mBoundsRectF.left + bottomEdgeRadius, mBoundsRectF.bottom); + mPath.lineTo(mBoundsRectF.right - bottomEdgeRadius, mBoundsRectF.bottom); + mPath.cubicTo(mBoundsRectF.right - bottomEdgeRadius, mBoundsRectF.bottom, + mBoundsRectF.right, mBoundsRectF.bottom, + mBoundsRectF.right, mBoundsRectF.bottom - bottomEdgeRadius); + mPath.close(); + canvas.drawPath(mPath, mPaint); + } } else { canvas.drawRect(getBounds().left, getBounds().top, getBounds().right, getBounds().bottom, mPaint); diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java index f68e0429ef7c..fc89a9e637ec 100644 --- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java +++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java @@ -20,6 +20,7 @@ import static java.lang.Float.isNaN; import android.annotation.NonNull; import android.content.Context; +import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PorterDuff; @@ -37,6 +38,7 @@ import androidx.core.graphics.ColorUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; +import com.android.systemui.util.LargeScreenUtils; import java.util.concurrent.Executor; @@ -102,6 +104,13 @@ public class ScrimView extends View { @Override protected void onDraw(Canvas canvas) { if (mDrawable.getAlpha() > 0) { + Resources res = getResources(); + // Scrim behind notification shade has sharp (not rounded) corners on large screens + // which scrim itself cannot know, so we set it here. + if (mDrawable instanceof ScrimDrawable) { + ((ScrimDrawable) mDrawable).setShouldUseLargeScreenSize( + LargeScreenUtils.shouldUseLargeScreenShadeHeader(res)); + } mDrawable.draw(canvas); } } @@ -170,6 +179,15 @@ public class ScrimView extends View { }); } + /** + * Set corner radius of the bottom edge of the Notification scrim. + */ + public void setBottomEdgeRadius(float radius) { + if (mDrawable instanceof ScrimDrawable) { + ((ScrimDrawable) mDrawable).setBottomEdgeRadius(radius); + } + } + @VisibleForTesting Drawable getDrawable() { return mDrawable; diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt index 287e8101f86d..33a3125d1c68 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt @@ -19,6 +19,7 @@ package com.android.systemui.settings import android.content.Context import android.content.pm.UserInfo import android.os.UserHandle +import java.util.concurrent.CountDownLatch import java.util.concurrent.Executor /** @@ -67,14 +68,25 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider { interface Callback { /** + * Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be + * auto-decremented after the completion of this method. + */ + @JvmDefault + fun onUserChanging(newUser: Int, userContext: Context) {} + + /** * Notifies that the current user is being changed. * Override this method to run things while the screen is frozen for the user switch. * Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the * screen further. Please be aware that code executed in this callback will lengthen the - * user switch duration. + * user switch duration. When overriding this method, countDown() MUST be called on the + * latch once execution is complete. */ @JvmDefault - fun onUserChanging(newUser: Int, userContext: Context) {} + fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) { + onUserChanging(newUser, userContext) + latch.countDown() + } /** * Notifies that the current user has changed. diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt index 3a5d0a7e0d26..0b2ae05b7c9b 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt @@ -182,9 +182,22 @@ open class UserTrackerImpl internal constructor( Log.i(TAG, "Switching to user $newUserId") setUserIdInternal(newUserId) - notifySubscribers { - onUserChanging(newUserId, userContext) - }.await() + + val list = synchronized(callbacks) { + callbacks.toList() + } + val latch = CountDownLatch(list.size) + list.forEach { + val callback = it.callback.get() + if (callback != null) { + it.executor.execute { + callback.onUserChanging(userId, userContext, latch) + } + } else { + latch.countDown() + } + } + latch.await() } @WorkerThread @@ -224,25 +237,18 @@ open class UserTrackerImpl internal constructor( } } - private inline fun notifySubscribers( - crossinline action: UserTracker.Callback.() -> Unit - ): CountDownLatch { + private inline fun notifySubscribers(crossinline action: UserTracker.Callback.() -> Unit) { val list = synchronized(callbacks) { callbacks.toList() } - val latch = CountDownLatch(list.size) list.forEach { if (it.callback.get() != null) { it.executor.execute { it.callback.get()?.action() - latch.countDown() } - } else { - latch.countDown() } } - return latch } override fun dump(pw: PrintWriter, args: Array<out String>) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index b502b4d02e00..b1987c151e5f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -23,6 +23,7 @@ import static android.view.View.VISIBLE; import static androidx.constraintlayout.widget.ConstraintSet.END; import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION; import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE; @@ -71,6 +72,7 @@ import android.os.VibrationEffect; import android.provider.Settings; import android.transition.ChangeBounds; import android.transition.Transition; +import android.transition.TransitionListenerAdapter; import android.transition.TransitionManager; import android.transition.TransitionSet; import android.transition.TransitionValues; @@ -98,6 +100,7 @@ import android.widget.FrameLayout; import androidx.constraintlayout.widget.ConstraintSet; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.policy.SystemBarUtils; @@ -293,7 +296,19 @@ public final class NotificationPanelViewController implements Dumpable { * custom clock animation is in use. */ private static final int KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION = 1000; + /** + * Whether the Shade should animate to reflect Back gesture progress. + * To minimize latency at runtime, we cache this, else we'd be reading it every time + * updateQsExpansion() is called... and it's called very often. + * + * Whenever we change this flag, SysUI is restarted, so it's never going to be "stale". + */ + public final boolean mAnimateBack; + /** + * The minimum scale to "squish" the Shade and associated elements down to, for Back gesture + */ + public static final float SHADE_BACK_ANIM_MIN_SCALE = 0.9f; private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; private final Resources mResources; private final KeyguardStateController mKeyguardStateController; @@ -353,6 +368,7 @@ public final class NotificationPanelViewController implements Dumpable { private final NotificationGutsManager mGutsManager; private final AlternateBouncerInteractor mAlternateBouncerInteractor; private final QuickSettingsController mQsController; + private final InteractionJankMonitor mInteractionJankMonitor; private long mDownTime; private boolean mTouchSlopExceededBeforeDown; @@ -361,6 +377,8 @@ public final class NotificationPanelViewController implements Dumpable { private CentralSurfaces mCentralSurfaces; private HeadsUpManagerPhone mHeadsUpManager; private float mExpandedHeight = 0; + /** The current squish amount for the predictive back animation */ + private float mCurrentBackProgress = 0.0f; private boolean mTracking; private boolean mHintAnimationRunning; private KeyguardBottomAreaView mKeyguardBottomArea; @@ -465,7 +483,7 @@ public final class NotificationPanelViewController implements Dumpable { private int mPanelAlpha; private Runnable mPanelAlphaEndAction; private float mBottomAreaShadeAlpha; - private final ValueAnimator mBottomAreaShadeAlphaAnimator; + final ValueAnimator mBottomAreaShadeAlphaAnimator; private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha", NotificationPanelView::setPanelAlphaInternal, NotificationPanelView::getCurrentPanelAlpha, @@ -597,7 +615,6 @@ public final class NotificationPanelViewController implements Dumpable { private int mLockscreenToDreamingTransitionTranslationY; private int mGoneToDreamingTransitionTranslationY; private int mLockscreenToOccludedTransitionTranslationY; - private boolean mUnocclusionTransitionFlagEnabled = false; private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */, mNextCollapseSpeedUpFactor, false /* expandBecauseOfFalsing */); @@ -643,6 +660,19 @@ public final class NotificationPanelViewController implements Dumpable { step.getTransitionState() == TransitionState.RUNNING; }; + private final TransitionListenerAdapter mKeyguardStatusAlignmentTransitionListener = + new TransitionListenerAdapter() { + @Override + public void onTransitionCancel(Transition transition) { + mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION); + } + + @Override + public void onTransitionEnd(Transition transition) { + mInteractionJankMonitor.end(CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION); + } + }; + @Inject public NotificationPanelViewController(NotificationPanelView view, @Main Handler handler, @@ -707,6 +737,7 @@ public final class NotificationPanelViewController implements Dumpable { NotificationStackSizeCalculator notificationStackSizeCalculator, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, ShadeTransitionController shadeTransitionController, + InteractionJankMonitor interactionJankMonitor, SystemClock systemClock, KeyguardBottomAreaViewModel keyguardBottomAreaViewModel, KeyguardBottomAreaInteractor keyguardBottomAreaInteractor, @@ -721,6 +752,7 @@ public final class NotificationPanelViewController implements Dumpable { DumpManager dumpManager, KeyguardLongPressViewModel keyguardLongPressViewModel, KeyguardInteractor keyguardInteractor) { + mInteractionJankMonitor = interactionJankMonitor; keyguardStateController.addCallback(new KeyguardStateController.Callback() { @Override public void onKeyguardFadingAwayChanged() { @@ -816,6 +848,7 @@ public final class NotificationPanelViewController implements Dumpable { mShadeHeaderController = shadeHeaderController; mLayoutInflater = layoutInflater; mFeatureFlags = featureFlags; + mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE); mFalsingCollector = falsingCollector; mPowerManager = powerManager; mWakeUpCoordinator = coordinator; @@ -886,7 +919,6 @@ public final class NotificationPanelViewController implements Dumpable { mNotificationPanelUnfoldAnimationController = unfoldComponent.map( SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); - mUnocclusionTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION); updateUserSwitcherFlags(); mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel; mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor; @@ -1045,62 +1077,50 @@ public final class NotificationPanelViewController implements Dumpable { mNotificationPanelUnfoldAnimationController.ifPresent(controller -> controller.setup(mNotificationContainerParent)); - if (mUnocclusionTransitionFlagEnabled) { - // Dreaming->Lockscreen - collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(), - mDreamingToLockscreenTransition, mMainDispatcher); - collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(), - setTransitionAlpha(mNotificationStackScrollLayoutController), - mMainDispatcher); - collectFlow(mView, mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY( - mDreamingToLockscreenTransitionTranslationY), - setTransitionY(mNotificationStackScrollLayoutController), - mMainDispatcher); - - // Occluded->Lockscreen - collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(), - mOccludedToLockscreenTransition, mMainDispatcher); - collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(), - setTransitionAlpha(mNotificationStackScrollLayoutController), - mMainDispatcher); - collectFlow(mView, mOccludedToLockscreenTransitionViewModel.lockscreenTranslationY( - mOccludedToLockscreenTransitionTranslationY), - setTransitionY(mNotificationStackScrollLayoutController), - mMainDispatcher); - - // Lockscreen->Dreaming - collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToDreamingTransition(), - mLockscreenToDreamingTransition, mMainDispatcher); - collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(), - setTransitionAlpha(mNotificationStackScrollLayoutController), - mMainDispatcher); - collectFlow(mView, mLockscreenToDreamingTransitionViewModel.lockscreenTranslationY( - mLockscreenToDreamingTransitionTranslationY), - setTransitionY(mNotificationStackScrollLayoutController), - mMainDispatcher); - - // Gone->Dreaming - collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(), - mGoneToDreamingTransition, mMainDispatcher); - collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(), - setTransitionAlpha(mNotificationStackScrollLayoutController), - mMainDispatcher); - collectFlow(mView, mGoneToDreamingTransitionViewModel.lockscreenTranslationY( - mGoneToDreamingTransitionTranslationY), - setTransitionY(mNotificationStackScrollLayoutController), - mMainDispatcher); - - // Lockscreen->Occluded - collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(), - mLockscreenToOccludedTransition, mMainDispatcher); - collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(), - setTransitionAlpha(mNotificationStackScrollLayoutController), - mMainDispatcher); - collectFlow(mView, mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY( - mLockscreenToOccludedTransitionTranslationY), - setTransitionY(mNotificationStackScrollLayoutController), - mMainDispatcher); - } + // Dreaming->Lockscreen + collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(), + mDreamingToLockscreenTransition, mMainDispatcher); + collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(), + setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); + collectFlow(mView, mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY( + mDreamingToLockscreenTransitionTranslationY), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); + + // Occluded->Lockscreen + collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(), + mOccludedToLockscreenTransition, mMainDispatcher); + collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(), + setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); + collectFlow(mView, mOccludedToLockscreenTransitionViewModel.lockscreenTranslationY( + mOccludedToLockscreenTransitionTranslationY), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); + + // Lockscreen->Dreaming + collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToDreamingTransition(), + mLockscreenToDreamingTransition, mMainDispatcher); + collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(), + setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); + collectFlow(mView, mLockscreenToDreamingTransitionViewModel.lockscreenTranslationY( + mLockscreenToDreamingTransitionTranslationY), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); + + // Gone->Dreaming + collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(), + mGoneToDreamingTransition, mMainDispatcher); + collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(), + setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); + collectFlow(mView, mGoneToDreamingTransitionViewModel.lockscreenTranslationY( + mGoneToDreamingTransitionTranslationY), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); + + // Lockscreen->Occluded + collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(), + mLockscreenToOccludedTransition, mMainDispatcher); + collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(), + setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); + collectFlow(mView, mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY( + mLockscreenToOccludedTransitionTranslationY), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); } @VisibleForTesting @@ -1554,6 +1574,7 @@ public final class NotificationPanelViewController implements Dumpable { int statusConstraint = shouldBeCentered ? PARENT_ID : R.id.qs_edge_guideline; constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END); if (animate) { + mInteractionJankMonitor.begin(mView, CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION); ChangeBounds transition = new ChangeBounds(); if (mSplitShadeEnabled) { // Excluding media from the transition on split-shade, as it doesn't transition @@ -1577,6 +1598,7 @@ public final class NotificationPanelViewController implements Dumpable { // The clock container can sometimes be null. If it is, just fall back to the // old animation rather than setting up the custom animations. if (clockContainerView == null || clockContainerView.getChildCount() == 0) { + transition.addListener(mKeyguardStatusAlignmentTransitionListener); TransitionManager.beginDelayedTransition( mNotificationContainerParent, transition); } else { @@ -1595,10 +1617,11 @@ public final class NotificationPanelViewController implements Dumpable { adapter.setDuration(KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION); adapter.addTarget(clockView); set.addTransition(adapter); - + set.addListener(mKeyguardStatusAlignmentTransitionListener); TransitionManager.beginDelayedTransition(mNotificationContainerParent, set); } } else { + transition.addListener(mKeyguardStatusAlignmentTransitionListener); TransitionManager.beginDelayedTransition( mNotificationContainerParent, transition); } @@ -1965,6 +1988,14 @@ public final class NotificationPanelViewController implements Dumpable { if (mFixedDuration != NO_FIXED_DURATION) { animator.setDuration(mFixedDuration); } + + // Reset Predictive Back animation's transform after Shade is completely hidden. + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + resetBackTransformation(); + } + }); } animator.addListener(new AnimatorListenerAdapter() { private boolean mCancelled; @@ -2189,6 +2220,53 @@ public final class NotificationPanelViewController implements Dumpable { } } + /** + * When the back gesture triggers a fully-expanded shade --> QQS shade collapse transition, + * the expansionFraction goes down from 1.0 --> 0.0 (collapsing), so the current "squish" amount + * (mCurrentBackProgress) must be un-applied from various UI elements in tandem, such that, + * as the shade ends up in its half-expanded state (with QQS above), it is back at 100% scale. + * Without this, the shade would collapse, and stay squished. + */ + public void adjustBackAnimationScale(float expansionFraction) { + if (expansionFraction > 0.0f) { // collapsing + float animatedFraction = expansionFraction * mCurrentBackProgress; + applyBackScaling(animatedFraction); + } else { + // collapsed! reset, so that if we re-expand shade, it won't start off "squished" + mCurrentBackProgress = 0; + } + } + + //TODO(b/270981268): allow cancelling back animation mid-flight + /** Called when Back gesture has been committed (i.e. a back event has definitely occurred) */ + public void onBackPressed() { + closeQsIfPossible(); + } + /** Sets back progress. */ + public void onBackProgressed(float progressFraction) { + // TODO: non-linearly transform progress fraction into squish amount (ease-in, linear out) + mCurrentBackProgress = progressFraction; + applyBackScaling(progressFraction); + } + + /** Resets back progress. */ + public void resetBackTransformation() { + mCurrentBackProgress = 0.0f; + applyBackScaling(0.0f); + } + + /** Scales multiple elements in tandem to achieve the illusion of the QS+Shade shrinking + * as a single visual element (used by the Predictive Back Gesture preview animation). + * fraction = 0 implies "no scaling", and 1 means "scale down to minimum size (90%)". + */ + public void applyBackScaling(float fraction) { + if (mNotificationContainerParent == null) { + return; + } + float scale = MathUtils.lerp(1.0f, SHADE_BACK_ANIM_MIN_SCALE, fraction); + mNotificationContainerParent.applyBackScaling(scale, mSplitShadeEnabled); + mScrimController.applyBackScaling(scale); + } /** */ public float getLockscreenShadeDragProgress() { // mTransitioningToFullShadeProgress > 0 means we're doing regular lockscreen to shade @@ -2480,9 +2558,6 @@ public final class NotificationPanelViewController implements Dumpable { } private void onExpandingFinished() { - if (!mUnocclusionTransitionFlagEnabled) { - mScrimController.onExpandingFinished(); - } mNotificationStackScrollLayoutController.onExpansionStopped(); mHeadsUpManager.onExpandingFinished(); mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed()); @@ -4868,6 +4943,11 @@ public final class NotificationPanelViewController implements Dumpable { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: + if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE && mAnimateBack) { + // Cache the gesture insets now, so we can quickly query them during + // ACTION_MOVE and decide whether to intercept events for back gesture anim. + mQsController.updateGestureInsetsCache(); + } mShadeLog.logMotionEvent(event, "onTouch: down action"); startExpandMotion(x, y, false /* startTracking */, mExpandedHeight); mMinExpandHeight = 0.0f; @@ -4917,6 +4997,12 @@ public final class NotificationPanelViewController implements Dumpable { } break; case MotionEvent.ACTION_MOVE: + // If the shade is half-collapsed, a horizontal swipe inwards from L/R edge + // must be routed to the back gesture (which shows a preview animation). + if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE && mAnimateBack + && mQsController.shouldBackBypassQuickSettings(x)) { + return false; + } if (isFullyCollapsed()) { // If panel is fully collapsed, reset haptic effect before adding movement. mHasVibratedOnOpen = false; diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 60fa865b83bc..c130b3913b64 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -38,8 +38,6 @@ import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.R; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dock.DockManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; @@ -47,6 +45,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel; +import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationInsetsController; @@ -132,11 +131,11 @@ public class NotificationShadeWindowViewController { NotificationInsetsController notificationInsetsController, AmbientState ambientState, PulsingGestureListener pulsingGestureListener, - FeatureFlags featureFlags, KeyguardBouncerViewModel keyguardBouncerViewModel, KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory, AlternateBouncerInteractor alternateBouncerInteractor, - KeyguardTransitionInteractor keyguardTransitionInteractor + KeyguardTransitionInteractor keyguardTransitionInteractor, + PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel ) { mLockscreenShadeTransitionController = transitionController; mFalsingCollector = falsingCollector; @@ -163,12 +162,11 @@ public class NotificationShadeWindowViewController { KeyguardBouncerViewBinder.bind( mView.findViewById(R.id.keyguard_bouncer_container), keyguardBouncerViewModel, + primaryBouncerToGoneTransitionViewModel, keyguardBouncerComponentFactory); - if (featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION)) { - collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(), - mLockscreenToDreamingTransition); - } + collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(), + mLockscreenToDreamingTransition); } /** diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java index f73dde632051..7dff6ea99029 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java @@ -20,6 +20,7 @@ import android.app.Fragment; import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; +import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.WindowInsets; @@ -55,6 +56,13 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout private QS mQs; private View mQSContainer; + /** + * These are used to compute the bounding box containing the shade and the notification scrim, + * which is then used to drive the Back gesture animation. + */ + private final Rect mUpperRect = new Rect(); + private final Rect mBoundingBoxRect = new Rect(); + @Nullable private Consumer<Configuration> mConfigurationChangedListener; @@ -172,4 +180,37 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout public void applyConstraints(ConstraintSet constraintSet) { constraintSet.applyTo(this); } + + /** + * Scale multiple elements in tandem, for the predictive back animation. + * This is how the Shade responds to the Back gesture (by scaling). + * Without the common center, individual elements will scale about their respective centers. + * Scaling the entire NotificationsQuickSettingsContainer will also resize the shade header + * (which we don't want). + */ + public void applyBackScaling(float scale, boolean usingSplitShade) { + if (mStackScroller == null || mQSContainer == null) { + return; + } + + mQSContainer.getBoundsOnScreen(mUpperRect); + mStackScroller.getBoundsOnScreen(mBoundingBoxRect); + mBoundingBoxRect.union(mUpperRect); + + float cx = mBoundingBoxRect.centerX(); + float cy = mBoundingBoxRect.centerY(); + + mQSContainer.setPivotX(cx); + mQSContainer.setPivotY(cy); + mQSContainer.setScaleX(scale); + mQSContainer.setScaleY(scale); + + // When in large-screen split-shade mode, the notification stack scroller scales correctly + // only if the pivot point is at the left edge of the screen (because of its dimensions). + // When not in large-screen split-shade mode, we can scale correctly via the (cx,cy) above. + mStackScroller.setPivotX(usingSplitShade ? 0.0f : cx); + mStackScroller.setPivotY(cy); + mStackScroller.setScaleX(scale); + mStackScroller.setScaleY(scale); + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index 099ad9473673..6857f4cc9e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -31,6 +31,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.app.Fragment; import android.content.res.Resources; +import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; import android.util.Log; @@ -40,6 +41,9 @@ import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; @@ -63,6 +67,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.shade.transition.ShadeTransitionController; +import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -83,10 +88,10 @@ import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.LargeScreenUtils; -import javax.inject.Inject; - import dagger.Lazy; +import javax.inject.Inject; + /** Handles QuickSettings touch handling, expansion and animation state * TODO (b/264460656) make this dumpable */ @@ -223,6 +228,13 @@ public class QuickSettingsController { private boolean mAnimatorExpand; /** + * The gesture inset currently in effect -- used to decide whether a back gesture should + * receive a horizontal swipe inwards from the left/right vertical edge of the screen. + * We cache this on ACTION_DOWN, and query it during both ACTION_DOWN and ACTION_MOVE events. + */ + private Insets mCachedGestureInsets; + + /** * The amount of progress we are currently in if we're transitioning to the full shade. * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full * shade. This value can also go beyond 1.1 when we're overshooting! @@ -406,6 +418,7 @@ public class QuickSettingsController { mQuickQsHeaderHeight = mLargeScreenShadeHeaderHeight; mEnableClipping = mResources.getBoolean(R.bool.qs_enable_clipping); + updateGestureInsetsCache(); } // TODO (b/265054088): move this and others to a CoreStartable @@ -469,6 +482,26 @@ public class QuickSettingsController { || touchX > mQsFrame.getX() + mQsFrame.getWidth(); } + /** + * Computes (and caches) the gesture insets for the current window. Intended to be called + * on ACTION_DOWN, and safely queried repeatedly thereafter during ACTION_MOVE events. + */ + public void updateGestureInsetsCache() { + WindowManager wm = this.mPanelView.getContext().getSystemService(WindowManager.class); + WindowMetrics windowMetrics = wm.getCurrentWindowMetrics(); + mCachedGestureInsets = windowMetrics.getWindowInsets().getInsets( + WindowInsets.Type.systemGestures()); + } + + /** + * Returns whether x coordinate lies in the vertical edges of the screen + * (the only place where a back gesture can be initiated). + */ + public boolean shouldBackBypassQuickSettings(float touchX) { + return (touchX < mCachedGestureInsets.left) + || (touchX > mKeyguardStatusBar.getWidth() - mCachedGestureInsets.right); + } + /** Returns whether touch is within QS area */ private boolean isTouchInQsArea(float x, float y) { if (isSplitShadeAndTouchXOutsideQs(x)) { @@ -926,6 +959,10 @@ public class QuickSettingsController { getHeaderTranslation(), squishiness ); + if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE + && mPanelViewControllerLazy.get().mAnimateBack) { + mPanelViewControllerLazy.get().adjustBackAnimationScale(adjustedExpansionFraction); + } mMediaHierarchyManager.setQsExpansion(qsExpansionFraction); int qsPanelBottomY = calculateBottomPosition(qsExpansionFraction); mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY); @@ -1113,6 +1150,7 @@ public class QuickSettingsController { float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius; radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius, Math.min(top / (float) mScrimCornerRadius, 1f)); + mScrimController.setNotificationBottomRadius(radius); } if (isQsFragmentCreated()) { float qsTranslation = 0; @@ -1505,18 +1543,31 @@ public class QuickSettingsController { } private void handleDown(MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN - && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) { - mFalsingCollector.onQsDown(); - mShadeLog.logMotionEvent(event, "handleQsDown: down action, QS tracking enabled"); - mTracking = true; - onExpansionStarted(); - mInitialHeightOnTouch = mExpansionHeight; - mInitialTouchY = event.getY(); - mInitialTouchX = event.getX(); - // TODO (b/265193930): remove dependency on NPVC - // If we interrupt an expansion gesture here, make sure to update the state correctly. - mPanelViewControllerLazy.get().notifyExpandingFinished(); + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + // When the shade is fully-expanded, an inward swipe from the L/R edge should first + // allow the back gesture's animation to preview the shade animation (if enabled). + // (swipes starting closer to the center of the screen will not be affected) + if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE + && mPanelViewControllerLazy.get().mAnimateBack) { + updateGestureInsetsCache(); + if (shouldBackBypassQuickSettings(event.getX())) { + return; + } + } + if (shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) { + mFalsingCollector.onQsDown(); + mShadeLog.logMotionEvent(event, + "handleQsDown: down action, QS tracking enabled"); + mTracking = true; + onExpansionStarted(); + mInitialHeightOnTouch = mExpansionHeight; + mInitialTouchY = event.getY(); + mInitialTouchX = event.getX(); + // TODO (b/265193930): remove dependency on NPVC + // If we interrupt an expansion gesture here, make sure to update the state + // correctly. + mPanelViewControllerLazy.get().notifyExpandingFinished(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt index a1767cc5888d..f4b1cc5f71be 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt @@ -107,7 +107,7 @@ class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents { * * @param fraction the fraction from the expansion in [0, 1] * @param expanded whether the panel is currently expanded; this is independent from the - * fraction as the panel also might be expanded if the fraction is 0. + * fraction as the panel also might be expanded if the fraction is 0. * @param tracking whether we're currently tracking the user's gesture. */ fun onPanelExpansionChanged( diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index 37773e952875..b79f32a6eae1 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -70,9 +70,9 @@ import javax.inject.Named * * [header] is a [MotionLayout] that has two transitions: * * [HEADER_TRANSITION_ID]: [QQS_HEADER_CONSTRAINT] <-> [QS_HEADER_CONSTRAINT] for portrait - * handheld device configuration. + * handheld device configuration. * * [LARGE_SCREEN_HEADER_TRANSITION_ID]: [LARGE_SCREEN_HEADER_CONSTRAINT] for all other - * configurations + * configurations */ @CentralSurfacesScope class ShadeHeaderController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt index 62c225ba0b4e..df8c6abfff97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt @@ -148,7 +148,8 @@ constructor( qsDragFraction: $qsTransitionFraction qsSquishFraction: $qsSquishTransitionFraction isTransitioningToFullShade: $isTransitioningToFullShade - """.trimIndent() + """ + .trimIndent() ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt index 42b874fd7156..7297ae689224 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt @@ -74,7 +74,7 @@ constructor( /** * @return a context with the MCC/MNC [Configuration] values corresponding to this - * subscriptionId + * subscriptionId */ fun getMobileContextForSub(subId: Int, context: Context): Context { if (demoModeController.isInDemoMode) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt index 64b7ac9ee0a1..5fa83ef5d454 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt @@ -39,6 +39,7 @@ import javax.inject.Inject * - Simple prioritization: Privacy > Battery > connectivity (encoded in [StatusEvent]) * - Only schedules a single event, and throws away lowest priority events * ``` + * * There are 4 basic stages of animation at play here: * ``` * 1. System chrome animation OUT @@ -46,6 +47,7 @@ import javax.inject.Inject * 3. Chip animation OUT; potentially into a dot * 4. System chrome animation IN * ``` + * * Thus we can keep all animations synchronized with two separate ValueAnimators, one for system * chrome and the other for the chip. These can animate from 0,1 and listeners can parameterize * their respective views based on the progress of the animator. Interpolation differences TBD @@ -168,7 +170,7 @@ constructor( * 3. Update the scheduler state so that clients know where we are * 4. Maybe: provide scaffolding such as: dot location, margins, etc * 5. Maybe: define a maximum animation length and enforce it. Probably only doable if we - * collect all of the animators and run them together. + * collect all of the animators and run them together. */ private fun runChipAnimation() { statusBarWindowController.setForceStatusBarVisible(true) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt index f395bea16131..82c5ee64b046 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt @@ -334,9 +334,9 @@ constructor( } val ssView = plugin.getView(parent) + configPlugin?.let { ssView.registerConfigProvider(it) } ssView.setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) ssView.registerDataProvider(plugin) - configPlugin?.let { ssView.registerConfigProvider(it) } ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter { override fun startIntent(view: View, intent: Intent, showOnLockscreen: Boolean) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java index 0a5e9867a17f..11582d7e3cc8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java @@ -29,7 +29,6 @@ import android.app.AppGlobals; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.app.SynchronousUserSwitchObserver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -52,7 +51,9 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.CoreStartable; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; +import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.NotificationChannels; @@ -73,6 +74,8 @@ public class InstantAppNotifier private final Context mContext; private final Handler mHandler = new Handler(); + private final UserTracker mUserTracker; + private final Executor mMainExecutor; private final Executor mUiBgExecutor; private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>(); private final CommandQueue mCommandQueue; @@ -82,10 +85,14 @@ public class InstantAppNotifier public InstantAppNotifier( Context context, CommandQueue commandQueue, + UserTracker userTracker, + @Main Executor mainExecutor, @UiBackground Executor uiBgExecutor, KeyguardStateController keyguardStateController) { mContext = context; mCommandQueue = commandQueue; + mUserTracker = userTracker; + mMainExecutor = mainExecutor; mUiBgExecutor = uiBgExecutor; mKeyguardStateController = keyguardStateController; } @@ -93,11 +100,7 @@ public class InstantAppNotifier @Override public void start() { // listen for user / profile change. - try { - ActivityManager.getService().registerUserSwitchObserver(mUserSwitchListener, TAG); - } catch (RemoteException e) { - // Ignore - } + mUserTracker.addCallback(mUserSwitchListener, mMainExecutor); mCommandQueue.addCallback(this); mKeyguardStateController.addCallback(this); @@ -129,13 +132,10 @@ public class InstantAppNotifier updateForegroundInstantApps(); } - private final SynchronousUserSwitchObserver mUserSwitchListener = - new SynchronousUserSwitchObserver() { - @Override - public void onUserSwitching(int newUserId) throws RemoteException {} - + private final UserTracker.Callback mUserSwitchListener = + new UserTracker.Callback() { @Override - public void onUserSwitchComplete(int newUserId) throws RemoteException { + public void onUserChanged(int newUser, Context userContext) { mHandler.post( () -> { updateForegroundInstantApps(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt index fc89be2c6670..00d8c421c721 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt @@ -28,10 +28,6 @@ class NotifPipelineFlags @Inject constructor( val featureFlags: FeatureFlags, val sysPropFlags: FlagResolver, ) { - init { - featureFlags.addListener(Flags.DISABLE_FSI) { event -> event.requestNoRestart() } - } - fun isDevLoggingEnabled(): Boolean = featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING) @@ -40,8 +36,6 @@ class NotifPipelineFlags @Inject constructor( fun fsiOnDNDUpdate(): Boolean = featureFlags.isEnabled(Flags.FSI_ON_DND_UPDATE) - fun disableFsi(): Boolean = featureFlags.isEnabled(Flags.DISABLE_FSI) - fun forceDemoteFsi(): Boolean = sysPropFlags.isEnabled(NotificationFlags.FSI_FORCE_DEMOTE) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 7e53d5431353..8874f59d6c17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification import android.animation.ObjectAnimator import android.util.FloatProperty +import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton @@ -302,29 +303,29 @@ class NotificationWakeUpCoordinator @Inject constructor( // the doze amount to 0f (not dozing) so that the notifications are no longer hidden. // See: UnlockedScreenOffAnimationController.onFinishedWakingUp() setDozeAmount(0f, 0f, source = "Override: Shade->Shade (lock cancelled by unlock)") + this.state = newState + return } if (overrideDozeAmountIfAnimatingScreenOff(mLinearDozeAmount)) { + this.state = newState return } if (overrideDozeAmountIfBypass()) { + this.state = newState return } maybeClearDozeAmountOverrideHidingNotifs() - if (bypassController.bypassEnabled && - newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED && - (!statusBarStateController.isDozing || shouldAnimateVisibility())) { - // We're leaving shade locked. Let's animate the notifications away - setNotificationsVisible(visible = true, increaseSpeed = false, animate = false) - setNotificationsVisible(visible = false, increaseSpeed = false, animate = true) - } - this.state = newState } + @VisibleForTesting + val statusBarState: Int + get() = state + override fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) { val collapsedEnough = event.fraction <= 0.9f if (collapsedEnough != this.collapsedEnoughToHide) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt index 44645315ca80..88d9ffcdcf3e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt @@ -13,7 +13,7 @@ package com.android.systemui.statusbar.notification -import com.android.systemui.log.dagger.NotificationLog +import com.android.systemui.log.dagger.NotificationLockscreenLog import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogLevel.DEBUG import com.android.systemui.statusbar.StatusBarState @@ -21,7 +21,12 @@ import javax.inject.Inject class NotificationWakeUpCoordinatorLogger @Inject -constructor(@NotificationLog private val buffer: LogBuffer) { +constructor(@NotificationLockscreenLog private val buffer: LogBuffer) { + private var lastSetDozeAmountLogWasFractional = false + private var lastSetDozeAmountLogState = -1 + private var lastSetDozeAmountLogSource = "undefined" + private var lastOnDozeAmountChangedLogWasFractional = false + fun logSetDozeAmount( linear: Float, eased: Float, @@ -29,6 +34,20 @@ constructor(@NotificationLog private val buffer: LogBuffer) { state: Int, changed: Boolean, ) { + // Avoid logging on every frame of the animation if important values are not changing + val isFractional = linear != 1f && linear != 0f + if ( + lastSetDozeAmountLogWasFractional && + isFractional && + lastSetDozeAmountLogState == state && + lastSetDozeAmountLogSource == source + ) { + return + } + lastSetDozeAmountLogWasFractional = isFractional + lastSetDozeAmountLogState = state + lastSetDozeAmountLogSource = source + buffer.log( TAG, DEBUG, @@ -66,6 +85,10 @@ constructor(@NotificationLog private val buffer: LogBuffer) { } fun logOnDozeAmountChanged(linear: Float, eased: Float) { + // Avoid logging on every frame of the animation when values are fractional + val isFractional = linear != 1f && linear != 0f + if (lastOnDozeAmountChangedLogWasFractional && isFractional) return + lastOnDozeAmountChangedLogWasFractional = isFractional buffer.log( TAG, DEBUG, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt index a35617c88caf..6deef2e11828 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt @@ -315,6 +315,7 @@ interface Roundable { /** * State object for a `Roundable` class. + * * @param targetView Will handle the [AnimatableProperty] * @param roundable Target of the radius animation * @param maxRadius Max corner radius in pixels @@ -436,7 +437,6 @@ interface SourceType { * This is the most convenient way to define a new [SourceType]. * * For example: - * * ```kotlin * private val SECTION = SourceType.from("Section") * ``` diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt index 1fccf82b21af..0a9dddc1c75e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt @@ -46,6 +46,7 @@ constructor( /** * Visits every entry and its children to mark the dismissible entries. + * * @param markedKeys set to store the marked entry keys * @param entries to visit * @param isLocked the locked state of the device diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt deleted file mode 100644 index b48322822c86..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt +++ /dev/null @@ -1,102 +0,0 @@ -package com.android.systemui.statusbar.notification.fsi - -import android.app.PendingIntent -import android.content.Context -import android.content.pm.PackageManager -import android.graphics.drawable.Drawable -import android.os.RemoteException -import android.service.dreams.IDreamManager -import com.android.systemui.CoreStartable -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.KeyguardRepository -import com.android.systemui.statusbar.notification.collection.NotificationEntry -import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider -import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log -import com.android.systemui.statusbar.phone.CentralSurfaces -import java.util.concurrent.Executor -import javax.inject.Inject -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow - -/** - * Class that bridges the gap between clean app architecture and existing code. Provides new - * implementation of StatusBarNotificationActivityStarter launchFullscreenIntent that pipes - * one-directional data => FsiChromeViewModel => FsiChromeView. - */ -@SysUISingleton -class FsiChromeRepo -@Inject -constructor( - private val context: Context, - private val pm: PackageManager, - private val keyguardRepo: KeyguardRepository, - private val launchFullScreenIntentProvider: LaunchFullScreenIntentProvider, - private val featureFlags: FeatureFlags, - private val uiBgExecutor: Executor, - private val dreamManager: IDreamManager, - private val centralSurfaces: CentralSurfaces -) : CoreStartable { - - companion object { - private const val classTag = "FsiChromeRepo" - } - - data class FSIInfo( - val appName: String, - val appIcon: Drawable, - val fullscreenIntent: PendingIntent - ) - - private val _infoFlow = MutableStateFlow<FSIInfo?>(null) - val infoFlow: StateFlow<FSIInfo?> = _infoFlow - - override fun start() { - log("$classTag start listening for FSI notifications") - - // Listen for FSI launch events for the lifetime of SystemUI. - launchFullScreenIntentProvider.registerListener { entry -> launchFullscreenIntent(entry) } - } - - fun dismiss() { - _infoFlow.value = null - } - - fun onFullscreen() { - // TODO(b/243421660) implement transition from container to fullscreen - } - - fun stopScreenSaver() { - uiBgExecutor.execute { - try { - dreamManager.awaken() - } catch (e: RemoteException) { - e.printStackTrace() - } - } - } - - fun launchFullscreenIntent(entry: NotificationEntry) { - if (!featureFlags.isEnabled(Flags.FSI_CHROME)) { - return - } - if (!keyguardRepo.isKeyguardShowing()) { - return - } - stopScreenSaver() - - var appName = pm.getApplicationLabel(context.applicationInfo) as String - val appIcon = pm.getApplicationIcon(context.packageName) - val fullscreenIntent = entry.sbn.notification.fullScreenIntent - - log("FsiChromeRepo launchFullscreenIntent appName=$appName appIcon $appIcon") - _infoFlow.value = FSIInfo(appName, appIcon, fullscreenIntent) - - // If screen is off or we're showing AOD, show lockscreen. - centralSurfaces.wakeUpForFullScreenIntent() - - // Don't show HUN since we're already showing FSI. - entry.notifyFullScreenIntentLaunched() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeView.kt deleted file mode 100644 index 6e5fcf40440c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeView.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.android.systemui.statusbar.notification.fsi - -import android.content.Context -import android.graphics.Color -import android.graphics.Color.DKGRAY -import android.graphics.Outline -import android.util.AttributeSet -import android.view.View -import android.view.ViewOutlineProvider -import android.widget.Button -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView -import com.android.systemui.R -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log - -@SysUISingleton -class FsiChromeView -@JvmOverloads -constructor( - context: Context?, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, - defStyleRes: Int = 0 -) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) { - - companion object { - private const val classTag = "FsiChromeView" - } - - lateinit var chromeContainer: LinearLayout - lateinit var appIconImageView: ImageView - lateinit var appNameTextView: TextView - lateinit var dismissButton: Button - lateinit var fullscreenButton: Button - - private val cornerRadius: Float = - resources.getDimensionPixelSize(R.dimen.notification_corner_radius).toFloat() - private val vertPadding: Int = - resources.getDimensionPixelSize(R.dimen.fsi_chrome_vertical_padding) - private val sidePadding: Int = - resources.getDimensionPixelSize(R.dimen.notification_side_paddings) - - init { - log("$classTag init") - } - - override fun onFinishInflate() { - log("$classTag onFinishInflate") - super.onFinishInflate() - - setBackgroundColor(Color.TRANSPARENT) - setPadding( - sidePadding, - vertPadding, - sidePadding, - vertPadding - ) // Make smaller than fullscreen. - - chromeContainer = findViewById(R.id.fsi_chrome) - chromeContainer.setBackgroundColor(DKGRAY) - - appIconImageView = findViewById(R.id.fsi_app_icon) - appNameTextView = findViewById(R.id.fsi_app_name) - dismissButton = findViewById(R.id.fsi_dismiss_button) - fullscreenButton = findViewById(R.id.fsi_fullscreen_button) - - outlineProvider = - object : ViewOutlineProvider() { - override fun getOutline(view: View, outline: Outline) { - outline.setRoundRect( - /* left */ sidePadding, - /* top */ vertPadding, - /* right */ view.width - sidePadding, - /* bottom */ view.height - vertPadding, - cornerRadius - ) - } - } - clipToOutline = true - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt deleted file mode 100644 index 1a3927ba9b06..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt +++ /dev/null @@ -1,99 +0,0 @@ -package com.android.systemui.statusbar.notification.fsi - -import android.content.Context -import android.view.LayoutInflater -import android.view.WindowManager -import com.android.systemui.CoreStartable -import com.android.systemui.R -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log -import com.android.systemui.statusbar.phone.CentralSurfaces -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -import java.util.concurrent.Executor -import javax.inject.Inject - -@SysUISingleton -class FsiChromeViewBinder -@Inject -constructor( - val context: Context, - val windowManager: WindowManager, - val viewModelFactory: FsiChromeViewModelFactory, - val layoutInflater: LayoutInflater, - val centralSurfaces: CentralSurfaces, - @Main val mainExecutor: Executor, - @Application val scope: CoroutineScope, -) : CoreStartable { - - companion object { - private const val classTag = "FsiChromeViewBinder" - } - - private val fsiChromeView = - layoutInflater.inflate(R.layout.fsi_chrome_view, null /* root */, false /* attachToRoot */) - as FsiChromeView - - var addedToWindowManager = false - var cornerRadius: Int = context.resources.getDimensionPixelSize( - R.dimen.notification_corner_radius) - - override fun start() { - val methodTag = "start" - log("$classTag $methodTag ") - - scope.launch { - log("$classTag $methodTag launch ") - viewModelFactory.viewModelFlow.collect { vm -> updateForViewModel(vm) } - } - } - - private fun updateForViewModel(vm: FsiChromeViewModel?) { - val methodTag = "updateForViewModel" - - if (vm == null) { - log("$classTag $methodTag viewModel is null, removing from window manager") - - if (addedToWindowManager) { - windowManager.removeView(fsiChromeView) - addedToWindowManager = false - } - return - } - - bindViewModel(vm, windowManager) - - if (addedToWindowManager) { - log("$classTag $methodTag already addedToWindowManager") - } else { - windowManager.addView(fsiChromeView, FsiTaskViewConfig.getWmLayoutParams("PackageName")) - addedToWindowManager = true - } - } - - private fun bindViewModel( - vm: FsiChromeViewModel, - windowManager: WindowManager, - ) { - log("$classTag bindViewModel") - - fsiChromeView.appIconImageView.setImageDrawable(vm.appIcon) - fsiChromeView.appNameTextView.text = vm.appName - - fsiChromeView.dismissButton.setOnClickListener { vm.onDismiss() } - fsiChromeView.fullscreenButton.setOnClickListener { vm.onFullscreen() } - - vm.taskView.cornerRadius = cornerRadius.toFloat() - vm.taskView.startActivity( - vm.fsi, - FsiTaskViewConfig.getFillInIntent(), - FsiTaskViewConfig.getActivityOptions(context, windowManager), - FsiTaskViewConfig.getLaunchBounds(windowManager) - ) - - log("$classTag bindViewModel started taskview activity") - fsiChromeView.addView(vm.taskView) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactory.kt deleted file mode 100644 index 1ca698b6bd58..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactory.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.android.systemui.statusbar.notification.fsi - -import android.annotation.UiContext -import android.app.PendingIntent -import android.content.Context -import android.graphics.drawable.Drawable -import com.android.systemui.CoreStartable -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log -import com.android.wm.shell.TaskView -import com.android.wm.shell.TaskViewFactory -import java.util.Optional -import java.util.concurrent.Executor -import javax.inject.Inject -import kotlin.coroutines.resume -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.suspendCancellableCoroutine - -/** - * Handle view-related data for fullscreen intent container on lockscreen. Wraps FsiChromeRepo, - * transforms events/state into view-relevant representation for FsiChromeView. Alive for lifetime - * of SystemUI. - */ -@SysUISingleton -class FsiChromeViewModelFactory -@Inject -constructor( - val repo: FsiChromeRepo, - val taskViewFactory: Optional<TaskViewFactory>, - @UiContext val context: Context, - @Main val mainExecutor: Executor, -) : CoreStartable { - - companion object { - private const val classTag = "FsiChromeViewModelFactory" - } - - val viewModelFlow: Flow<FsiChromeViewModel?> = - repo.infoFlow.mapLatest { fsiInfo -> - fsiInfo?.let { - log("$classTag viewModelFlow got new fsiInfo") - - // mapLatest emits null when FSIInfo is null - FsiChromeViewModel( - fsiInfo.appName, - fsiInfo.appIcon, - createTaskView(), - fsiInfo.fullscreenIntent, - repo - ) - } - } - - override fun start() { - log("$classTag start") - } - - private suspend fun createTaskView(): TaskView = suspendCancellableCoroutine { k -> - log("$classTag createTaskView") - - taskViewFactory.get().create(context, mainExecutor) { taskView -> k.resume(taskView) } - } -} - -// Alive for lifetime of FSI. -data class FsiChromeViewModel( - val appName: String, - val appIcon: Drawable, - val taskView: TaskView, - val fsi: PendingIntent, - val repo: FsiChromeRepo -) { - companion object { - private const val classTag = "FsiChromeViewModel" - } - - fun onDismiss() { - log("$classTag onDismiss") - repo.dismiss() - } - fun onFullscreen() { - log("$classTag onFullscreen") - repo.onFullscreen() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt deleted file mode 100644 index d9e3f8fbf146..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.android.systemui.statusbar.notification.fsi - -class FsiDebug { - - companion object { - private const val debugTag = "FsiDebug" - private const val debug = true - - fun log(s: Any) { - if (!debug) { - return - } - android.util.Log.d(debugTag, "$s") - } - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt deleted file mode 100644 index 034ab56d5a65..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.android.systemui.statusbar.notification.fsi - -import android.app.ActivityOptions -import android.content.Context -import android.content.Intent -import android.graphics.PixelFormat -import android.graphics.Rect -import android.os.Binder -import android.view.ViewGroup -import android.view.WindowManager - -/** - * Config for adding the FsiChromeView window to WindowManager and starting the FSI activity. - */ -class FsiTaskViewConfig { - - companion object { - - private const val classTag = "FsiTaskViewConfig" - - fun getWmLayoutParams(packageName: String): WindowManager.LayoutParams { - val params: WindowManager.LayoutParams? - params = - WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, - WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or - WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED or - WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER, - PixelFormat.TRANSLUCENT - ) - params.setTrustedOverlay() - params.fitInsetsTypes = 0 - params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE - params.token = Binder() - params.packageName = packageName - params.layoutInDisplayCutoutMode = - WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS - params.privateFlags = - params.privateFlags or WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS - return params - } - - fun getFillInIntent(): Intent { - val fillInIntent = Intent() - fillInIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) - fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - // FLAG_ACTIVITY_NEW_TASK is auto-applied because - // we're starting the FSI activity from a non-Activity context - return fillInIntent - } - - fun getLaunchBounds(windowManager: WindowManager): Rect { - // TODO(b/243421660) check this works for non-resizeable activity - return Rect() - } - - fun getActivityOptions(context: Context, windowManager: WindowManager): ActivityOptions { - // Custom options so there is no activity transition animation - val options = - ActivityOptions.makeCustomAnimation(context, 0 /* enterResId */, 0 /* exitResId */) - - options.taskAlwaysOnTop = true - - options.pendingIntentLaunchFlags = - Intent.FLAG_ACTIVITY_NEW_DOCUMENT or - Intent.FLAG_ACTIVITY_MULTIPLE_TASK or - Intent.FLAG_ACTIVITY_NEW_TASK - - options.launchBounds = getLaunchBounds(windowManager) - return options - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java index ae19febadfaa..9001470ad406 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java @@ -35,10 +35,6 @@ public interface NotificationInterruptStateProvider { */ NO_FSI_SHOW_STICKY_HUN(false), /** - * Full screen intents are disabled. - */ - NO_FSI_DISABLED(false), - /** * No full screen intent included, so there is nothing to show. */ NO_FULL_SCREEN_INTENT(false), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index 0163dbef2760..9f45b9d67189 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -244,10 +244,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter @Override public FullScreenIntentDecision getFullScreenIntentDecision(NotificationEntry entry) { - if (mFlags.disableFsi()) { - return FullScreenIntentDecision.NO_FSI_DISABLED; - } - if (entry.getSbn().getNotification().fullScreenIntent == null) { if (entry.isStickyAndNotDemoted()) { return FullScreenIntentDecision.NO_FSI_SHOW_STICKY_HUN; @@ -343,9 +339,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter case NO_FSI_SHOW_STICKY_HUN: mLogger.logNoFullscreen(entry, "Permission denied, show sticky HUN"); return; - case NO_FSI_DISABLED: - mLogger.logNoFullscreen(entry, "Disabled"); - return; case NO_FULL_SCREEN_INTENT: return; case NO_FSI_SUPPRESSED_BY_DND: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 2868116fe697..92c5b632f813 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -211,7 +211,11 @@ public class NotificationStackScrollLayoutController { public void onViewAttachedToWindow(View v) { mConfigurationController.addCallback(mConfigurationListener); mZenModeController.addCallback(mZenModeControllerCallback); - mBarState = mStatusBarStateController.getState(); + final int newBarState = mStatusBarStateController.getState(); + if (newBarState != mBarState) { + mStateListener.onStateChanged(newBarState); + mStateListener.onStatePostChange(); + } mStatusBarStateController.addCallback( mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index aaf9300e7cc8..c6f56d482d43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -251,13 +251,13 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc || (isFastNonDismissGesture && isAbleToShowMenu); int menuSnapTarget = menuRow.getMenuSnapTarget(); boolean isNonFalseMenuRevealingGesture = - !isFalseGesture() && isMenuRevealingGestureAwayFromMenu; + isMenuRevealingGestureAwayFromMenu && !isFalseGesture(); if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture) && menuSnapTarget != 0) { // Menu has not been snapped to previously and this is menu revealing gesture snapOpen(animView, menuSnapTarget, velocity); menuRow.onSnapOpen(); - } else if (isDismissGesture(ev) && !gestureTowardsMenu) { + } else if (isDismissGesture && !gestureTowardsMenu) { dismiss(animView, velocity); menuRow.onDismiss(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt index 548d1a135948..8b6d6a4f3170 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt @@ -25,9 +25,10 @@ constructor( /** * This method looks for views that can be rounded (and implement [Roundable]) during a * notification swipe. + * * @return The [Roundable] targets above/below the [viewSwiped] (if available). The - * [RoundableTargets.before] and [RoundableTargets.after] parameters can be `null` if there is - * no above/below notification or the notification is not part of the same section. + * [RoundableTargets.before] and [RoundableTargets.after] parameters can be `null` if there is + * no above/below notification or the notification is not part of the same section. */ fun findRoundableTargets( viewSwiped: ExpandableNotificationRow, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 9f3836105a95..7855cdfeb4c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -374,6 +374,17 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp } @Override + public void onBiometricDetected(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + Trace.beginSection("BiometricUnlockController#onBiometricDetected"); + if (mUpdateMonitor.isGoingToSleep()) { + Trace.endSection(); + return; + } + startWakeAndUnlock(MODE_SHOW_BOUNCER); + } + + @Override public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, boolean isStrongBiometric) { Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 8dcfec71b68e..9e62817fcc67 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -375,8 +375,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn void fadeKeyguardAfterLaunchTransition(Runnable beforeFading, Runnable endRunnable, Runnable cancelRunnable); - void animateKeyguardUnoccluding(); - void startLaunchTransitionTimeout(); boolean hideKeyguardImpl(boolean forceStateChange); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 93b580c5bd7a..664d61acf7cc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -104,6 +104,8 @@ import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; import android.widget.DateTimeView; +import android.window.BackEvent; +import android.window.OnBackAnimationCallback; import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedDispatcher; @@ -508,6 +510,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private final BrightnessSliderController.Factory mBrightnessSliderFactory; private final FeatureFlags mFeatureFlags; + private final boolean mAnimateBack; private final FragmentService mFragmentService; private final ScreenOffAnimationController mScreenOffAnimationController; private final WallpaperController mWallpaperController; @@ -654,6 +657,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final InteractionJankMonitor mJankMonitor; + /** Existing callback that handles back gesture invoked for the Shade. */ private final OnBackInvokedCallback mOnBackInvokedCallback = () -> { if (DEBUG) { Log.d(TAG, "mOnBackInvokedCallback() called"); @@ -661,6 +665,33 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { onBackPressed(); }; + private boolean shouldBackBeHandled() { + return (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED + && !isBouncerShowingOverDream()); + } + + /** + * New callback that handles back gesture invoked, cancel, progress + * and provides feedback via Shade animation. + * (enabled via the WM_SHADE_ANIMATE_BACK_GESTURE flag) + */ + private final OnBackAnimationCallback mOnBackAnimationCallback = new OnBackAnimationCallback() { + @Override + public void onBackInvoked() { + onBackPressed(); + } + + @Override + public void onBackProgressed(BackEvent event) { + if (shouldBackBeHandled()) { + if (mNotificationPanelViewController.canPanelBeCollapsed()) { + float fraction = event.getProgress(); + mNotificationPanelViewController.onBackProgressed(fraction); + } + } + } + }; + /** * Public constructor for CentralSurfaces. * @@ -882,6 +913,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) { mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true); } + // Based on teamfood flag, enable predictive back animation for the Shade. + mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE); } private void initBubbles(Bubbles bubbles) { @@ -2221,10 +2254,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { pw.println("Current Status Bar state:"); pw.println(" mExpandedVisible=" + mShadeController.isExpandedVisible()); pw.println(" mDisplayMetrics=" + mDisplayMetrics); - pw.println(" mStackScroller: " + CentralSurfaces.viewInfo(mStackScroller)); - pw.println(" mStackScroller: " + CentralSurfaces.viewInfo(mStackScroller) - + " scroll " + mStackScroller.getScrollX() + pw.print(" mStackScroller: " + CentralSurfaces.viewInfo(mStackScroller)); + pw.print(" scroll " + mStackScroller.getScrollX() + "," + mStackScroller.getScrollY()); + pw.println(" translationX " + mStackScroller.getTranslationX()); } pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); @@ -2706,7 +2739,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { if (viewRootImpl != null) { viewRootImpl.getOnBackInvokedDispatcher() .registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, - mOnBackInvokedCallback); + mAnimateBack ? mOnBackAnimationCallback + : mOnBackInvokedCallback); mIsBackCallbackRegistered = true; if (DEBUG) Log.d(TAG, "is now VISIBLE to user AND callback registered"); } @@ -2721,7 +2755,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { ViewRootImpl viewRootImpl = getViewRootImpl(); if (viewRootImpl != null) { viewRootImpl.getOnBackInvokedDispatcher() - .unregisterOnBackInvokedCallback(mOnBackInvokedCallback); + .unregisterOnBackInvokedCallback( + mAnimateBack ? mOnBackAnimationCallback + : mOnBackInvokedCallback); mIsBackCallbackRegistered = false; if (DEBUG) Log.d(TAG, "is NOT VISIBLE to user, AND callback unregistered"); } @@ -2998,16 +3034,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } /** - * Plays the animation when an activity that was occluding Keyguard goes away. - */ - @Override - public void animateKeyguardUnoccluding() { - mNotificationPanelViewController.setExpandedFraction(0f); - mCommandQueueCallbacks.animateExpandNotificationsPanel(); - mScrimController.setUnocclusionAnimationRunning(true); - } - - /** * Starts the timeout when we try to start the affordances on Keyguard. We usually rely that * Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen * because the launched app crashed or something else went wrong. @@ -3263,9 +3289,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) { return true; } - if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED - && !isBouncerShowingOverDream()) { + if (shouldBackBeHandled()) { if (mNotificationPanelViewController.canPanelBeCollapsed()) { + // this is the Shade dismiss animation, so make sure QQS closes when it ends. + mNotificationPanelViewController.onBackPressed(); mShadeController.animateCollapseShade(); } return true; @@ -3720,6 +3747,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { @Override public void notifyBiometricAuthModeChanged() { mDozeServiceHost.updateDozing(); + if (mBiometricUnlockController.getMode() + == BiometricUnlockController.MODE_DISMISS_BOUNCER) { + // Don't update the scrim controller at this time, in favor of the transition repository + // updating the scrim + return; + } updateScrimController(); } @@ -3772,6 +3805,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } else { mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED); } + // This will cancel the keyguardFadingAway animation if it is running. We need to do + // this as otherwise it can remain pending and leave keyguard in a weird state. + mUnlockScrimCallback.onCancelled(); } else if (mBouncerShowing && !unlocking) { // Bouncer needs the front scrim when it's on top of an activity, // tapping on a notification, editing QS or being dismissed by diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 6c532a5c5fab..e6b76ad0e00c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -22,8 +22,6 @@ import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.AlarmManager.AlarmClockInfo; -import android.app.IActivityManager; -import android.app.SynchronousUserSwitchObserver; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -134,7 +132,6 @@ public class PhoneStatusBarPolicy private final NextAlarmController mNextAlarmController; private final AlarmManager mAlarmManager; private final UserInfoController mUserInfoController; - private final IActivityManager mIActivityManager; private final UserManager mUserManager; private final UserTracker mUserTracker; private final DevicePolicyManager mDevicePolicyManager; @@ -149,6 +146,7 @@ public class PhoneStatusBarPolicy private final KeyguardStateController mKeyguardStateController; private final LocationController mLocationController; private final PrivacyItemController mPrivacyItemController; + private final Executor mMainExecutor; private final Executor mUiBgExecutor; private final SensorPrivacyController mSensorPrivacyController; private final RecordingController mRecordingController; @@ -168,16 +166,17 @@ public class PhoneStatusBarPolicy @Inject public PhoneStatusBarPolicy(StatusBarIconController iconController, CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher, - @UiBackground Executor uiBgExecutor, @Main Looper looper, @Main Resources resources, - CastController castController, HotspotController hotspotController, - BluetoothController bluetoothController, NextAlarmController nextAlarmController, - UserInfoController userInfoController, RotationLockController rotationLockController, - DataSaverController dataSaverController, ZenModeController zenModeController, + @Main Executor mainExecutor, @UiBackground Executor uiBgExecutor, @Main Looper looper, + @Main Resources resources, CastController castController, + HotspotController hotspotController, BluetoothController bluetoothController, + NextAlarmController nextAlarmController, UserInfoController userInfoController, + RotationLockController rotationLockController, DataSaverController dataSaverController, + ZenModeController zenModeController, DeviceProvisionedController deviceProvisionedController, KeyguardStateController keyguardStateController, LocationController locationController, - SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager, - AlarmManager alarmManager, UserManager userManager, UserTracker userTracker, + SensorPrivacyController sensorPrivacyController, AlarmManager alarmManager, + UserManager userManager, UserTracker userTracker, DevicePolicyManager devicePolicyManager, RecordingController recordingController, @Nullable TelecomManager telecomManager, @DisplayId int displayId, @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil, @@ -195,7 +194,6 @@ public class PhoneStatusBarPolicy mNextAlarmController = nextAlarmController; mAlarmManager = alarmManager; mUserInfoController = userInfoController; - mIActivityManager = iActivityManager; mUserManager = userManager; mUserTracker = userTracker; mDevicePolicyManager = devicePolicyManager; @@ -208,6 +206,7 @@ public class PhoneStatusBarPolicy mPrivacyItemController = privacyItemController; mSensorPrivacyController = sensorPrivacyController; mRecordingController = recordingController; + mMainExecutor = mainExecutor; mUiBgExecutor = uiBgExecutor; mTelecomManager = telecomManager; mRingerModeTracker = ringerModeTracker; @@ -256,11 +255,7 @@ public class PhoneStatusBarPolicy mRingerModeTracker.getRingerModeInternal().observeForever(observer); // listen for user / profile change. - try { - mIActivityManager.registerUserSwitchObserver(mUserSwitchListener, TAG); - } catch (RemoteException e) { - // Ignore - } + mUserTracker.addCallback(mUserSwitchListener, mMainExecutor); // TTY status updateTTY(); @@ -555,15 +550,15 @@ public class PhoneStatusBarPolicy }); } - private final SynchronousUserSwitchObserver mUserSwitchListener = - new SynchronousUserSwitchObserver() { + private final UserTracker.Callback mUserSwitchListener = + new UserTracker.Callback() { @Override - public void onUserSwitching(int newUserId) throws RemoteException { + public void onUserChanging(int newUser, Context userContext) { mHandler.post(() -> mUserInfoController.reloadUserInfo()); } @Override - public void onUserSwitchComplete(int newUserId) throws RemoteException { + public void onUserChanged(int newUser, Context userContext) { mHandler.post(() -> { updateAlarm(); updateManagedProfile(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 80093a3da325..ce650d557dd3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; + import static java.lang.Float.isNaN; import android.animation.Animator; @@ -53,7 +55,11 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants; +import com.android.systemui.keyguard.shared.model.TransitionState; +import com.android.systemui.keyguard.shared.model.TransitionStep; +import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; import com.android.systemui.scrim.ScrimView; import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.notification.stack.ViewState; @@ -71,6 +77,8 @@ import java.util.function.Consumer; import javax.inject.Inject; +import kotlinx.coroutines.CoroutineDispatcher; + /** * Controls both the scrim behind the notifications and in front of the notifications (when a * security method gets shown). @@ -138,26 +146,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private boolean mTransitioningToFullShade; /** - * Is there currently an unocclusion animation running. Used to avoid bright flickers - * of the notification scrim. - */ - private boolean mUnOcclusionAnimationRunning; - - /** * The percentage of the bouncer which is hidden. If 1, the bouncer is completely hidden. If * 0, the bouncer is visible. */ @FloatRange(from = 0, to = 1) private float mBouncerHiddenFraction = KeyguardBouncerConstants.EXPANSION_HIDDEN; - /** - * Set whether an unocclusion animation is currently running on the notification panel. Used - * to avoid bright flickers of the notification scrim. - */ - public void setUnocclusionAnimationRunning(boolean unocclusionAnimationRunning) { - mUnOcclusionAnimationRunning = unocclusionAnimationRunning; - } - @IntDef(prefix = {"VISIBILITY_"}, value = { TRANSPARENT, SEMI_TRANSPARENT, @@ -265,6 +259,28 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private boolean mWakeLockHeld; private boolean mKeyguardOccluded; + private KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private CoroutineDispatcher mMainDispatcher; + private boolean mIsBouncerToGoneTransitionStarted = false; + private boolean mIsBouncerToGoneTransitionRunning = false; + private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; + private final Consumer<Float> mScrimAlphaConsumer = + (Float alpha) -> { + mScrimInFront.setViewAlpha(0f); + mNotificationsScrim.setViewAlpha(0f); + mScrimBehind.setViewAlpha(alpha); + }; + final Consumer<TransitionStep> mPrimaryBouncerToGoneTransition = + (TransitionStep step) -> { + mIsBouncerToGoneTransitionRunning = + step.getTransitionState() == TransitionState.RUNNING; + mIsBouncerToGoneTransitionStarted = + step.getTransitionState() == TransitionState.STARTED; + if (mIsBouncerToGoneTransitionStarted) { + transitionTo(ScrimState.UNLOCKED); + } + }; + @Inject public ScrimController( LightBarController lightBarController, @@ -279,7 +295,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump @Main Executor mainExecutor, ScreenOffAnimationController screenOffAnimationController, KeyguardUnlockAnimationController keyguardUnlockAnimationController, - StatusBarKeyguardViewManager statusBarKeyguardViewManager) { + StatusBarKeyguardViewManager statusBarKeyguardViewManager, + PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, + KeyguardTransitionInteractor keyguardTransitionInteractor, + @Main CoroutineDispatcher mainDispatcher) { mScrimStateListener = lightBarController::setScrimState; mDefaultScrimAlpha = BUSY_SCRIM_ALPHA; @@ -318,6 +337,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } }); mColors = new GradientColors(); + mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel; + mKeyguardTransitionInteractor = keyguardTransitionInteractor; + mMainDispatcher = mainDispatcher; } /** @@ -357,11 +379,23 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump for (ScrimState state : ScrimState.values()) { state.prepare(state); } + + collectFlow(behindScrim, mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition(), + mPrimaryBouncerToGoneTransition, mMainDispatcher); + collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(), + mScrimAlphaConsumer, mMainDispatcher); } - /** - * Sets corner radius of scrims. - */ + // TODO(b/270984686) recompute scrim height accurately, based on shade contents. + /** Set corner radius of the bottom edge of the Notification scrim. */ + public void setNotificationBottomRadius(float radius) { + if (mNotificationsScrim == null) { + return; + } + mNotificationsScrim.setBottomEdgeRadius(radius); + } + + /** Sets corner radius of scrims. */ public void setScrimCornerRadius(int radius) { if (mScrimBehind == null || mNotificationsScrim == null) { return; @@ -379,6 +413,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } public void transitionTo(ScrimState state, Callback callback) { + if (mIsBouncerToGoneTransitionRunning) { + Log.i(TAG, "Skipping transition to: " + state + + " while mIsBouncerToGoneTransitionRunning"); + return; + } if (state == mState) { // Call the callback anyway, unless it's already enqueued if (callback != null && mCallback != callback) { @@ -525,6 +564,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump scheduleUpdate(); } + /** This is used by the predictive back gesture animation to scale the Shade. */ + public void applyBackScaling(float scale) { + mNotificationsScrim.setScaleX(scale); + mNotificationsScrim.setScaleY(scale); + } + public void onTrackingStarted() { mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen(); if (!mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) { @@ -532,10 +577,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } } - public void onExpandingFinished() { - setUnocclusionAnimationRunning(false); - } - @VisibleForTesting protected void onHideWallpaperTimeout() { if (mState != ScrimState.AOD && mState != ScrimState.PULSING) { @@ -802,10 +843,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mBehindAlpha = 0; mNotificationsAlpha = 0; } else { - // Behind scrim will finish fading in at 30% expansion. float behindFraction = MathUtils .constrainedMap(0f, 1f, 0f, 0.3f, mPanelExpansionFraction); - mBehindAlpha = behindFraction * mDefaultScrimAlpha; + if (!mIsBouncerToGoneTransitionStarted) { + mBehindAlpha = behindFraction * mDefaultScrimAlpha; + } // Delay fade-in of notification scrim a bit further, to coincide with the // behind scrim finishing fading in. // Also to coincide with the view starting to fade in, otherwise the empty @@ -875,13 +917,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump if (mKeyguardOccluded || hideNotificationScrim) { mNotificationsAlpha = 0; } - if (mUnOcclusionAnimationRunning && mState == ScrimState.KEYGUARD) { - // We're unoccluding the keyguard and don't want to have a bright flash. - mNotificationsAlpha = ScrimState.KEYGUARD.getNotifAlpha(); - mNotificationsTint = ScrimState.KEYGUARD.getNotifTint(); - mBehindAlpha = ScrimState.KEYGUARD.getBehindAlpha(); - mBehindTint = ScrimState.KEYGUARD.getBehindTint(); - } } if (mState != ScrimState.UNLOCKED) { mAnimatingPanelExpansionOnUnlock = false; @@ -1150,7 +1185,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_tint", Color.alpha(tint)); scrimView.setTint(tint); - scrimView.setViewAlpha(alpha); + if (!mIsBouncerToGoneTransitionRunning) { + scrimView.setViewAlpha(alpha); + } } else { scrim.setAlpha(alpha); } @@ -1498,6 +1535,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } public void setKeyguardOccluded(boolean keyguardOccluded) { + if (mKeyguardOccluded == keyguardOccluded) { + return; + } mKeyguardOccluded = keyguardOccluded; updateScrims(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index a127139fcc69..66f5b6508494 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -281,7 +281,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private float mQsExpansion; final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>(); private boolean mIsModernAlternateBouncerEnabled; - private boolean mIsUnoccludeTransitionFlagEnabled; private boolean mIsBackAnimationEnabled; private OnDismissAction mAfterKeyguardGoneAction; @@ -361,7 +360,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null); mIsModernAlternateBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER); mAlternateBouncerInteractor = alternateBouncerInteractor; - mIsUnoccludeTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION); mIsBackAnimationEnabled = featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM); } @@ -880,11 +878,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // by a FLAG_DISMISS_KEYGUARD_ACTIVITY. reset(isOccluding /* hideBouncerWhenShowing*/); } - if (!mIsUnoccludeTransitionFlagEnabled) { - if (animate && !isOccluded && isShowing && !primaryBouncerIsShowing()) { - mCentralSurfaces.animateKeyguardUnoccluding(); - } - } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 3471a4656637..726b2344309f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -561,10 +561,6 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mLogger.logFullScreenIntentSuppressedByVR(entry); return; } - if (mFeatureFlags.isEnabled(Flags.FSI_CHROME)) { - // FsiChromeRepo runs its own implementation of launchFullScreenIntent - return; - } // Stop screensaver if the notification has a fullscreen intent. // (like an incoming phone call) mUiBgExecutor.execute(() -> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt index 8c82fbac90b8..f4e3eab8593d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt @@ -45,7 +45,7 @@ import kotlinx.coroutines.flow.asStateFlow * 1. Define a new `private val` wrapping the key using [BooleanCarrierConfig] * 2. Define a public `val` exposing the wrapped flow using [BooleanCarrierConfig.config] * 3. Add the new [BooleanCarrierConfig] to the list of tracked configs, so they are properly - * updated when a new carrier config comes down + * updated when a new carrier config comes down */ class SystemUiCarrierConfig internal constructor( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt index b3d5b1e7e450..53a208cd171e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt @@ -353,8 +353,8 @@ constructor( * True if the checked subId is in the list of current subs or the active mobile data subId * * @param checkedSubs the list to validate [subId] against. To invalidate the cache, pass in the - * new subscription list. Otherwise use [subscriptions.value] to validate a subId against the - * current known subscriptions + * new subscription list. Otherwise use [subscriptions.value] to validate a subId against the + * current known subscriptions */ private fun checkSub(subId: Int, checkedSubs: List<SubscriptionModel>): Boolean { if (activeMobileDataSubscriptionId.value == subId) return true diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt index 7b0f95271d63..4caf2b09a3f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt @@ -92,7 +92,8 @@ interface MobileIconInteractor { * 1. The default network name, if one is configured * 2. A derived name based off of the intent [ACTION_SERVICE_PROVIDERS_UPDATED] * 3. Or, in the case where the repository sends us the default network name, we check for an - * override in [connectionInfo.operatorAlphaShort], a value that is derived from [ServiceState] + * override in [connectionInfo.operatorAlphaShort], a value that is derived from + * [ServiceState] */ val networkName: StateFlow<NetworkNameModel> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt index 24cd9304f8dd..8e103f7bee2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt @@ -25,7 +25,7 @@ import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags * allows the mobile icon to change some view parameters at different locations * * @param commonImpl for convenience, this class wraps a base interface that can provides all of the - * common implementations between locations. See [MobileIconViewModel] + * common implementations between locations. See [MobileIconViewModel] */ abstract class LocationBasedMobileViewModel( val commonImpl: MobileIconViewModelCommon, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt index e0e0ed795e4a..b1296179d7f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt @@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.stateIn * or the [WifiRepositoryImpl]'s prod implementation, based on the current demo mode value. In this * way, downstream clients can all consist of real implementations and not care about which * repository is responsible for the data. Graphically: - * * ``` * RealRepository * │ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt index bdb656b9d2d5..1e223b1920ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt @@ -146,7 +146,7 @@ constructor( * * @param guestUserId id of the guest user to remove * @param targetUserId id of the user to switch to after guest is removed. If - * `UserHandle.USER_NULL`, then switch immediately to the newly created guest user. + * `UserHandle.USER_NULL`, then switch immediately to the newly created guest user. */ fun removeGuestUser(guestUserId: Int, targetUserId: Int) { userInteractor.removeGuestUser( @@ -160,9 +160,9 @@ constructor( * * @param guestUserId user id of the guest user to exit * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when - * target user id is not known + * target user id is not known * @param forceRemoveGuestOnExit true: remove guest before switching user, false: remove guest - * only if its ephemeral, else keep guest + * only if its ephemeral, else keep guest */ fun exitGuestUser(guestUserId: Int, targetUserId: Int, forceRemoveGuestOnExit: Boolean) { userInteractor.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit) diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt index 462504e82199..4e27ce6721d7 100644 --- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt @@ -82,6 +82,8 @@ constructor( fun startListener() { handler.post { if (hasStarted) return@post + logDebug { "Listener has started." } + hasStarted = true isInUsiSession = inputManager.hasInputDevice { @@ -116,6 +118,10 @@ constructor( val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return + logDebug { + "Stylus InputDevice added: $deviceId ${device.name}, " + + "External: ${device.isExternal}" + } if (!device.isExternal) { registerBatteryListener(deviceId) @@ -137,6 +143,7 @@ constructor( val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return + logDebug { "Stylus InputDevice changed: $deviceId ${device.name}" } val currAddress: String? = device.bluetoothAddress val prevAddress: String? = inputDeviceAddressMap[deviceId] @@ -157,6 +164,8 @@ constructor( if (!hasStarted) return if (!inputDeviceAddressMap.contains(deviceId)) return + logDebug { "Stylus InputDevice removed: $deviceId" } + unregisterBatteryListener(deviceId) val btAddress: String? = inputDeviceAddressMap[deviceId] @@ -180,6 +189,11 @@ constructor( val isCharging = String(value) == "true" + logDebug { + "Charging state metadata changed for device $inputDeviceId " + + "${device.address}: $isCharging" + } + executeStylusBatteryCallbacks { cb -> cb.onStylusBluetoothChargingStateChanged(inputDeviceId, device, isCharging) } @@ -194,13 +208,10 @@ constructor( handler.post { if (!hasStarted) return@post - if (DEBUG) { - Log.d( - TAG, - "onBatteryStateChanged for $deviceId. " + - "batteryState present: ${batteryState.isPresent}, " + - "capacity: ${batteryState.capacity}" - ) + logDebug { + "Battery state changed for $deviceId. " + + "batteryState present: ${batteryState.isPresent}, " + + "capacity: ${batteryState.capacity}" } val batteryStateValid = isBatteryStateValid(batteryState) @@ -216,7 +227,7 @@ constructor( } private fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) { - trackAndLogBluetoothSession(deviceId, true) + trackAndLogBluetoothSession(deviceId, btAddress, true) val device: BluetoothDevice = bluetoothAdapter?.getRemoteDevice(btAddress) ?: return try { bluetoothAdapter.addOnMetadataChangedListener(device, executor, this) @@ -226,7 +237,7 @@ constructor( } private fun onStylusBluetoothDisconnected(deviceId: Int, btAddress: String) { - trackAndLogBluetoothSession(deviceId, false) + trackAndLogBluetoothSession(deviceId, btAddress, false) val device: BluetoothDevice = bluetoothAdapter?.getRemoteDevice(btAddress) ?: return try { bluetoothAdapter.removeOnMetadataChangedListener(device, this) @@ -245,6 +256,7 @@ constructor( if (!featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)) return if (InputSettings.isStylusEverUsed(context)) return + logDebug { "Stylus used for the first time." } InputSettings.setStylusEverUsed(context, true) executeStylusCallbacks { cb -> cb.onStylusFirstUsed() } } @@ -259,12 +271,7 @@ constructor( // TODO(b/268618918) handle cases where an invalid battery callback from a previous stylus // is sent after the actual valid callback if (batteryStateValid && usiSessionId == null) { - if (DEBUG) { - Log.d( - TAG, - "USI battery newly present, entering new USI session. Device ID: $deviceId" - ) - } + logDebug { "USI battery newly present, entering new USI session: $deviceId" } usiSessionId = instanceIdSequence.newInstanceId() uiEventLogger.logWithInstanceId( StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED, @@ -273,9 +280,7 @@ constructor( usiSessionId ) } else if (!batteryStateValid && usiSessionId != null) { - if (DEBUG) { - Log.d(TAG, "USI battery newly absent, exiting USI session Device ID: $deviceId") - } + logDebug { "USI battery newly absent, exiting USI session: $deviceId" } uiEventLogger.logWithInstanceId( StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_REMOVED, 0, @@ -286,8 +291,17 @@ constructor( } } - private fun trackAndLogBluetoothSession(deviceId: Int, bluetoothConnected: Boolean) { - if (bluetoothConnected) { + private fun trackAndLogBluetoothSession( + deviceId: Int, + btAddress: String, + btConnected: Boolean + ) { + logDebug { + "Bluetooth stylus ${if (btConnected) "connected" else "disconnected"}:" + + " $deviceId $btAddress" + } + + if (btConnected) { inputDeviceBtSessionIdMap[deviceId] = instanceIdSequence.newInstanceId() uiEventLogger.logWithInstanceId( StylusUiEvent.BLUETOOTH_STYLUS_CONNECTED, @@ -383,7 +397,13 @@ constructor( } companion object { - private val TAG = StylusManager::class.simpleName.orEmpty() - private val DEBUG = false + val TAG = StylusManager::class.simpleName.orEmpty() + const val DEBUG = false + } +} + +private inline fun logDebug(message: () -> String) { + if (StylusManager.DEBUG) { + Log.d(StylusManager.TAG, message()) } } diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt index ec0a6e7a80d8..21b0efadb8d5 100644 --- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt @@ -26,6 +26,7 @@ import android.content.Intent import android.content.IntentFilter import android.hardware.BatteryState import android.hardware.input.InputManager +import android.os.Build import android.os.Bundle import android.os.Handler import android.os.UserHandle @@ -109,6 +110,10 @@ constructor( inputDeviceId = deviceId batteryCapacity = batteryState.capacity + logDebug { + "Updating notification battery state to $batteryCapacity " + + "for InputDevice $deviceId." + } refresh() } } @@ -125,12 +130,14 @@ constructor( handler.post updateSuppressed@{ if (suppressed == suppress) return@updateSuppressed + logDebug { "Updating notification suppression to $suppress." } suppressed = suppress refresh() } } private fun hideNotification() { + logDebug { "Cancelling USI low battery notification." } instanceId = null notificationManager.cancel(USI_NOTIFICATION_ID) } @@ -153,6 +160,7 @@ constructor( .setAutoCancel(true) .build() + logDebug { "Show or update USI low battery notification at $batteryCapacity." } logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_SHOWN) notificationManager.notify(USI_NOTIFICATION_ID, notification) } @@ -180,10 +188,12 @@ constructor( override fun onReceive(context: Context, intent: Intent) { when (intent.action) { ACTION_DISMISSED_LOW_BATTERY -> { + logDebug { "USI low battery notification dismissed." } logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_DISMISSED) updateSuppression(true) } ACTION_CLICKED_LOW_BATTERY -> { + logDebug { "USI low battery notification clicked." } logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_CLICKED) updateSuppression(true) if (inputDeviceId == null) return @@ -233,6 +243,8 @@ constructor( } companion object { + val TAG = StylusUsiPowerUI::class.simpleName.orEmpty() + // Low battery threshold matches CrOS, see: // https://source.chromium.org/chromium/chromium/src/+/main:ash/system/power/peripheral_battery_notifier.cc;l=41 private const val LOW_BATTERY_THRESHOLD = 0.16f @@ -251,3 +263,9 @@ constructor( @VisibleForTesting const val KEY_SETTINGS_FRAGMENT_ARGS = ":settings:show_fragment_args" } } + +private inline fun logDebug(message: () -> String) { + if (Build.IS_DEBUGGABLE) { + Log.d(StylusUsiPowerUI.TAG, message()) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TouchableRegionViewController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TouchableRegionViewController.kt index 60241a9684d9..cf0184f9e1ae 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TouchableRegionViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TouchableRegionViewController.kt @@ -27,7 +27,7 @@ import com.android.systemui.util.ViewController * pass through to the window below. * * @param touchableRegionSetter a function that, given the view and an out rect, fills the rect with - * the touchable region of this view. + * the touchable region of this view. */ class TouchableRegionViewController( view: View, diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt index 01a81deabc95..16123882046c 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt @@ -35,7 +35,7 @@ open class ChipbarAnimator @Inject constructor() { * Animates [innerView] and its children into view. * * @return true if the animation was successfully started and false if the animation can't be - * run for any reason. + * run for any reason. * * See [ViewHierarchyAnimator.animateAddition]. */ @@ -55,7 +55,7 @@ open class ChipbarAnimator @Inject constructor() { * Animates [innerView] and its children out of view. * * @return true if the animation was successfully started and false if the animation can't be - * run for any reason. + * run for any reason. * * See [ViewHierarchyAnimator.animateRemoval]. */ diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt index fe46318daa30..125cc761d400 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt @@ -28,10 +28,10 @@ import com.android.systemui.temporarydisplay.ViewPriority * A container for all the state needed to display a chipbar via [ChipbarCoordinator]. * * @property startIcon the icon to display at the start of the chipbar (on the left in LTR locales; - * on the right in RTL locales). + * on the right in RTL locales). * @property text the text to display. * @property endItem an optional end item to display at the end of the chipbar (on the right in LTR - * locales; on the left in RTL locales). + * locales; on the left in RTL locales). * @property vibrationEffect an optional vibration effect when the chipbar is displayed * @property allowSwipeToDismiss true if users are allowed to swipe up to dismiss this chipbar. */ diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt index 2683971f852c..981f429d1f8f 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt @@ -61,8 +61,6 @@ class FoldStateLoggingProviderImpl( foldStateProvider.stop() } - override fun onHingeAngleUpdate(angle: Float) {} - override fun onFoldUpdate(@FoldUpdate update: Int) { val now = clock.elapsedRealtime() when (update) { @@ -77,6 +75,10 @@ class FoldStateLoggingProviderImpl( } } + override fun onUnfoldedScreenAvailable() { + Log.d(TAG, "Unfolded screen available") + } + private fun dispatchState(@LoggedFoldedStates current: Int) { val now = clock.elapsedRealtime() val previous = lastState diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt index 5f89d5d144e8..9304a462b6a8 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt @@ -45,7 +45,7 @@ constructor( * * @param successCallback is called when the user creation is successful. * @param errorCallback is called when userManager.createUser returns null. (Exceptions are not - * handled by this class) + * handled by this class) */ fun createUser( userName: String?, diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt index 70523bb6b81d..a0b56aa6f5c3 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt @@ -17,11 +17,8 @@ package com.android.systemui.user.data.repository -import android.app.IActivityManager -import android.app.UserSwitchObserver import android.content.Context import android.content.pm.UserInfo -import android.os.IRemoteCallback import android.os.UserHandle import android.os.UserManager import android.provider.Settings @@ -121,7 +118,6 @@ constructor( @Background private val backgroundDispatcher: CoroutineDispatcher, private val globalSettings: GlobalSettings, private val tracker: UserTracker, - private val activityManager: IActivityManager, featureFlags: FeatureFlags, ) : UserRepository { @@ -213,18 +209,18 @@ constructor( private fun observeUserSwitching() { conflatedCallbackFlow { val callback = - object : UserSwitchObserver() { - override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback) { + object : UserTracker.Callback { + override fun onUserChanging(newUser: Int, userContext: Context) { trySendWithFailureLogging(true, TAG, "userSwitching started") } - override fun onUserSwitchComplete(newUserId: Int) { + override fun onUserChanged(newUserId: Int, userContext: Context) { trySendWithFailureLogging(false, TAG, "userSwitching completed") } } - activityManager.registerUserSwitchObserver(callback, TAG) + tracker.addCallback(callback, mainDispatcher.asExecutor()) trySendWithFailureLogging(false, TAG, "initial value defaulting to false") - awaitClose { activityManager.unregisterUserSwitchObserver(callback) } + awaitClose { tracker.removeCallback(callback) } } .onEach { _isUserSwitchingInProgress.value = it } // TODO (b/262838215), Make this stateIn and initialize directly in field declaration diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt index 2f63f32557e9..f026f0f9de37 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt @@ -313,7 +313,7 @@ constructor( * to create a new one. * * @return The multi-user user ID of the newly created guest user, or [UserHandle.USER_NULL] if - * the guest couldn't be created. + * the guest couldn't be created. */ @UserIdInt private suspend fun createInBackground(): Int { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt index 082c8ccd9657..13b3b1afd30b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt @@ -18,8 +18,10 @@ package com.android.keyguard import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import android.view.View import android.view.inputmethod.InputMethodManager import android.widget.EditText +import android.widget.ImageView import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils @@ -30,6 +32,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock @@ -37,6 +40,7 @@ import org.mockito.Mockito import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` +import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations @SmallTest @@ -76,7 +80,9 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() { Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry)) .thenReturn(passwordEntry) `when`(keyguardPasswordView.resources).thenReturn(context.resources) - keyguardPasswordViewController = + `when`(keyguardPasswordView.findViewById<ImageView>(R.id.switch_ime_button)) + .thenReturn(mock(ImageView::class.java)) + keyguardPasswordViewController = KeyguardPasswordViewController( keyguardPasswordView, keyguardUpdateMonitor, @@ -113,6 +119,18 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() { } @Test + fun onApplyWindowInsetsListener_onApplyWindowInsets() { + `when`(keyguardViewController.isBouncerShowing).thenReturn(false) + val argumentCaptor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java) + + keyguardPasswordViewController.onViewAttached() + verify(keyguardPasswordView).setOnApplyWindowInsetsListener(argumentCaptor.capture()) + argumentCaptor.value.onApplyWindowInsets(keyguardPasswordView, null) + + verify(keyguardPasswordView).hideKeyboard() + } + + @Test fun testHideKeyboardWhenOnPause() { keyguardPasswordViewController.onPause() keyguardPasswordView.post { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java index dfad15d68375..71449145d668 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java @@ -26,7 +26,6 @@ import android.testing.AndroidTestingRunner; import com.android.keyguard.logging.KeyguardLogger; import com.android.systemui.SysuiTestCase; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ClockAnimations; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; @@ -61,8 +60,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase { @Mock DozeParameters mDozeParameters; @Mock - FeatureFlags mFeatureFlags; - @Mock ScreenOffAnimationController mScreenOffAnimationController; @Captor private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor; @@ -83,7 +80,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase { mKeyguardUpdateMonitor, mConfigurationController, mDozeParameters, - mFeatureFlags, mScreenOffAnimationController, mKeyguardLogger); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 09b738fd26db..bd77c327e765 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -18,9 +18,10 @@ package com.android.keyguard; import static android.app.StatusBarManager.SESSION_KEYGUARD; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; -import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT; +import static android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN; +import static android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_STARTED_WAKING_UP; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON; import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE; import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID; @@ -30,7 +31,6 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE; -import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING; import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT; import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT; @@ -59,8 +59,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Activity; -import android.app.ActivityManager; -import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.app.trust.IStrongAuthTracker; import android.app.trust.TrustManager; @@ -82,6 +80,7 @@ import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.SensorProperties; +import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorProperties; import android.hardware.face.FaceSensorPropertiesInternal; @@ -97,7 +96,6 @@ import android.os.BatteryManager; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Handler; -import android.os.IRemoteCallback; import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; @@ -156,6 +154,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; @@ -239,8 +238,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Mock private KeyguardUpdateMonitorLogger mKeyguardUpdateMonitorLogger; @Mock - private IActivityManager mActivityService; - @Mock private SessionTracker mSessionTracker; @Mock private UiEventLogger mUiEventLogger; @@ -282,8 +279,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Before public void setup() throws RemoteException { MockitoAnnotations.initMocks(this); - when(mActivityService.getCurrentUser()).thenReturn(mCurrentUserInfo); - when(mActivityService.getCurrentUserId()).thenReturn(mCurrentUserId); when(mFaceManager.isHardwareDetected()).thenReturn(true); when(mAuthController.isFaceAuthEnrolled(anyInt())).thenReturn(true); when(mFaceManager.getSensorPropertiesInternal()).thenReturn(mFaceSensorProperties); @@ -322,13 +317,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mMockitoSession = ExtendedMockito.mockitoSession() .spyStatic(SubscriptionManager.class) - .spyStatic(ActivityManager.class) .startMocking(); ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID) .when(SubscriptionManager::getDefaultSubscriptionId); KeyguardUpdateMonitor.setCurrentUser(mCurrentUserId); when(mUserTracker.getUserId()).thenReturn(mCurrentUserId); - ExtendedMockito.doReturn(mActivityService).when(ActivityManager::getService); mContext.getOrCreateTestableResources().addOverride( com.android.systemui.R.integer.config_face_auth_supported_posture, @@ -633,20 +626,48 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testOnlyDetectFingerprint_whenFingerprintUnlockNotAllowed() { - // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks) - // will trigger updateBiometricListeningState(); - clearInvocations(mFingerprintManager); - mKeyguardUpdateMonitor.resetBiometricListeningState(); - - when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); - mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */); - mTestableLooper.processAllMessages(); + givenDetectFingerprintWithClearingFingerprintManagerInvocations(); verifyFingerprintAuthenticateNeverCalled(); verifyFingerprintDetectCall(); } @Test + public void whenDetectFingerprint_biometricDetectCallback() { + ArgumentCaptor<FingerprintManager.FingerprintDetectionCallback> fpDetectCallbackCaptor = + ArgumentCaptor.forClass(FingerprintManager.FingerprintDetectionCallback.class); + + givenDetectFingerprintWithClearingFingerprintManagerInvocations(); + verify(mFingerprintManager).detectFingerprint( + any(), fpDetectCallbackCaptor.capture(), any()); + fpDetectCallbackCaptor.getValue().onFingerprintDetected(0, 0, true); + + // THEN verify keyguardUpdateMonitorCallback receives a detect callback + // and NO authenticate callbacks + verify(mTestCallback).onBiometricDetected( + eq(0), eq(BiometricSourceType.FINGERPRINT), eq(true)); + verify(mTestCallback, never()).onBiometricAuthenticated( + anyInt(), any(), anyBoolean()); + } + + @Test + public void whenDetectFace_biometricDetectCallback() { + ArgumentCaptor<FaceManager.FaceDetectionCallback> faceDetectCallbackCaptor = + ArgumentCaptor.forClass(FaceManager.FaceDetectionCallback.class); + + givenDetectFace(); + verify(mFaceManager).detectFace(any(), faceDetectCallbackCaptor.capture(), any()); + faceDetectCallbackCaptor.getValue().onFaceDetected(0, 0, false); + + // THEN verify keyguardUpdateMonitorCallback receives a detect callback + // and NO authenticate callbacks + verify(mTestCallback).onBiometricDetected( + eq(0), eq(BiometricSourceType.FACE), eq(false)); + verify(mTestCallback, never()).onBiometricAuthenticated( + anyInt(), any(), anyBoolean()); + } + + @Test public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricAllowed() { // GIVEN unlocking with biometric is allowed strongAuthNotRequired(); @@ -674,7 +695,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { strongAuthNotRequired(); // WHEN fingerprint is locked out - fingerprintErrorLockedOut(); + fingerprintErrorTemporaryLockedOut(); // THEN unlocking with face is not allowed Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed( @@ -697,7 +718,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { strongAuthNotRequired(); // WHEN fingerprint is locked out - fingerprintErrorLockedOut(); + fingerprintErrorTemporaryLockedOut(); // THEN unlocking with fingerprint is not allowed Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed( @@ -721,7 +742,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser())); // WHEN fingerprint is locked out - fingerprintErrorLockedOut(); + fingerprintErrorTemporaryLockedOut(); // THEN user is NOT considered as "having trust" and bouncer cannot be skipped Assert.assertFalse(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser())); @@ -781,11 +802,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void nofaceDetect_whenStrongAuthRequiredAndBypassUdfpsSupportedAndFpRunning() { - // GIVEN mocked keyguardUpdateMonitorCallback - KeyguardUpdateMonitorCallback keyguardUpdateMonitorCallback = - mock(KeyguardUpdateMonitorCallback.class); - mKeyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback); - // GIVEN bypass is enabled, face detection is supported lockscreenBypassIsAllowed(); supportsFaceDetection(); @@ -804,22 +820,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { verifyFaceAuthenticateNeverCalled(); // THEN biometric help message sent to callback - verify(keyguardUpdateMonitorCallback).onBiometricHelp( + verify(mTestCallback).onBiometricHelp( eq(BIOMETRIC_HELP_FACE_NOT_AVAILABLE), anyString(), eq(BiometricSourceType.FACE)); } @Test public void faceDetect_whenStrongAuthRequiredAndBypass() { - // GIVEN bypass is enabled, face detection is supported and strong auth is required - lockscreenBypassIsAllowed(); - supportsFaceDetection(); - strongAuthRequiredEncrypted(); - keyguardIsVisible(); - // fingerprint is NOT running, UDFPS is NOT supported - - // WHEN the device wakes up - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); + givenDetectFace(); // FACE detect is triggered, not authenticate verifyFaceDetectCall(); @@ -836,39 +843,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void noFaceRun_whenFpLockout() { - // GIVEN bypass is enabled, face detection is supported and strong auth is required - lockscreenBypassIsAllowed(); - supportsFaceDetection(); - strongAuthRequiredEncrypted(); - keyguardIsVisible(); - // fingerprint is NOT running, UDFPS is NOT supported - - // GIVEN fp is locked out - when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), anyInt())) - .thenReturn(BIOMETRIC_LOCKOUT_TIMED); - mKeyguardUpdateMonitor.handleUserSwitchComplete(0); - assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(true); - - // WHEN the device wakes up - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - - // FACE detect is NOT triggered and face authenticate is NOT triggered - verifyFaceDetectNeverCalled(); - verifyFaceAuthenticateNeverCalled(); - - // WHEN bouncer becomes visible - setKeyguardBouncerVisibility(true); - clearInvocations(mFaceManager); - - // THEN face scanning is not run - mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN); - verifyFaceAuthenticateNeverCalled(); - verifyFaceDetectNeverCalled(); - } - - @Test public void noFaceDetect_whenStrongAuthRequiredAndBypass_faceDetectionUnsupported() { // GIVEN bypass is enabled, face detection is NOT supported and strong auth is required lockscreenBypassIsAllowed(); @@ -1100,11 +1074,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testBiometricsCleared_whenUserSwitches() throws Exception { - final IRemoteCallback reply = new IRemoteCallback.Stub() { - @Override - public void sendResult(Bundle data) { - } // do nothing - }; final BiometricAuthenticated dummyAuthentication = new BiometricAuthenticated(true /* authenticated */, true /* strong */); mKeyguardUpdateMonitor.mUserFaceAuthenticated.put(0 /* user */, dummyAuthentication); @@ -1112,18 +1081,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1); assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1); - mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, reply); + mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, new CountDownLatch(0)); assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0); assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0); } @Test public void testMultiUserJankMonitor_whenUserSwitches() throws Exception { - final IRemoteCallback reply = new IRemoteCallback.Stub() { - @Override - public void sendResult(Bundle data) { - } // do nothing - }; mKeyguardUpdateMonitor.handleUserSwitchComplete(10 /* user */); verify(mInteractionJankMonitor).end(InteractionJankMonitor.CUJ_USER_SWITCH); verify(mLatencyTracker).onActionEnd(LatencyTracker.ACTION_USER_SWITCH); @@ -1186,8 +1150,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // Fingerprint should be cancelled on lockout if going to lockout state, else // restarted if it's not assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState) - .isEqualTo(fpLocked - ? BIOMETRIC_STATE_CANCELLING : BIOMETRIC_STATE_CANCELLING_RESTARTING); + .isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING); } @Test @@ -1646,7 +1609,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); // Fingerprint is locked out. - fingerprintErrorLockedOut(); + fingerprintErrorTemporaryLockedOut(); assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); } @@ -2499,7 +2462,69 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { eq(false)); } + @Test + public void detectFingerprint_onTemporaryLockoutReset_authenticateFingerprint() { + ArgumentCaptor<FingerprintManager.LockoutResetCallback> fpLockoutResetCallbackCaptor = + ArgumentCaptor.forClass(FingerprintManager.LockoutResetCallback.class); + verify(mFingerprintManager).addLockoutResetCallback(fpLockoutResetCallbackCaptor.capture()); + + // GIVEN device is locked out + fingerprintErrorTemporaryLockedOut(); + + // GIVEN FP detection is running + givenDetectFingerprintWithClearingFingerprintManagerInvocations(); + verifyFingerprintDetectCall(); + verifyFingerprintAuthenticateNeverCalled(); + + // WHEN temporary lockout resets + fpLockoutResetCallbackCaptor.getValue().onLockoutReset(0); + mTestableLooper.processAllMessages(); + + // THEN fingerprint detect state should cancel & then restart (for authenticate call) + assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState) + .isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING); + } + + @Test + public void faceAuthenticateOptions_bouncerAuthenticateReason() { + // GIVEN the bouncer is fully visible + bouncerFullyVisible(); + + // WHEN authenticate is called + ArgumentCaptor<FaceAuthenticateOptions> captor = + ArgumentCaptor.forClass(FaceAuthenticateOptions.class); + verify(mFaceManager).authenticate(any(), any(), any(), any(), captor.capture()); + + // THEN the authenticate reason is attributed to the bouncer + assertThat(captor.getValue().getAuthenticateReason()) + .isEqualTo(AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN); + } + + @Test + public void faceAuthenticateOptions_wakingUpAuthenticateReason_powerButtonWakeReason() { + // GIVEN keyguard is visible + keyguardIsVisible(); + + // WHEN device wakes up from the power button + mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); + mTestableLooper.processAllMessages(); + + // THEN face auth is triggered + ArgumentCaptor<FaceAuthenticateOptions> captor = + ArgumentCaptor.forClass(FaceAuthenticateOptions.class); + verify(mFaceManager).authenticate(any(), any(), any(), any(), captor.capture()); + + // THEN the authenticate reason is attributed to the waking + assertThat(captor.getValue().getAuthenticateReason()) + .isEqualTo(AUTHENTICATE_REASON_STARTED_WAKING_UP); + + // THEN the wake reason is attributed to the power button + assertThat(captor.getValue().getWakeReason()) + .isEqualTo(PowerManager.WAKE_REASON_POWER_BUTTON); + } + private void verifyFingerprintAuthenticateNeverCalled() { + verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any()); verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(), anyInt(), anyInt()); } @@ -2518,11 +2543,12 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } private void verifyFaceAuthenticateNeverCalled() { + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), any()); verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); } private void verifyFaceAuthenticateCall() { - verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt()); + verify(mFaceManager).authenticate(any(), any(), any(), any(), any()); } private void verifyFaceDetectNeverCalled() { @@ -2602,7 +2628,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.setSwitchingUser(true); } - private void fingerprintErrorLockedOut() { + private void fingerprintErrorTemporaryLockedOut() { mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out"); } @@ -2630,7 +2656,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { any(), mAuthenticationCallbackCaptor.capture(), any(), - anyInt()); + any()); mAuthenticationCallbackCaptor.getValue() .onAuthenticationSucceeded( new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false)); @@ -2741,6 +2767,30 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { receiver.setPendingResult(pendingResult); } + private void givenDetectFingerprintWithClearingFingerprintManagerInvocations() { + // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks) + // will trigger updateBiometricListeningState(); + clearInvocations(mFingerprintManager); + mKeyguardUpdateMonitor.resetBiometricListeningState(); + + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); + mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */); + mTestableLooper.processAllMessages(); + } + + private void givenDetectFace() { + // GIVEN bypass is enabled, face detection is supported and strong auth is required + lockscreenBypassIsAllowed(); + supportsFaceDetection(); + strongAuthRequiredEncrypted(); + keyguardIsVisible(); + // fingerprint is NOT running, UDFPS is NOT supported + + // WHEN the device wakes up + mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); + mTestableLooper.processAllMessages(); + } + private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) { int subscription = simInited ? 1/* mock subid=1 */ : SubscriptionManager.PLACEHOLDER_SUBSCRIPTION_ID_BASE; diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt index 6333a68bb5e0..3ec49b263c54 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt @@ -354,7 +354,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_0 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_90() = @@ -362,7 +364,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_90 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_180() = @@ -370,7 +374,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_180 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarCollapsedDownForXAlignedSensor_180() = @@ -379,7 +385,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = false, { rotation = Surface.ROTATION_180 }, windowInsets = insetsForSmallNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun hidesSfpsIndicatorWhenOccludingTaskbarForXAlignedSensor_180() = @@ -388,7 +396,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = false, { rotation = Surface.ROTATION_180 }, windowInsets = insetsForLargeNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_270() = @@ -396,7 +406,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_270 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_0() = @@ -404,7 +416,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_0 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_90() = @@ -412,7 +426,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_90 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarCollapsedDownForXAlignedSensor_InReverseDefaultRotation_90() = @@ -421,7 +437,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = true, { rotation = Surface.ROTATION_90 }, windowInsets = insetsForSmallNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun hidesSfpsIndicatorWhenOccludingTaskbarForXAlignedSensor_InReverseDefaultRotation_90() = @@ -430,7 +448,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = true, { rotation = Surface.ROTATION_90 }, windowInsets = insetsForLargeNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_180() = @@ -438,7 +458,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_180 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_270() = @@ -446,7 +468,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_270 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_0() = @@ -454,7 +478,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.Y_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_0 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_90() = @@ -462,7 +488,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.Y_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_90 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_180() = @@ -480,7 +508,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.Y_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_270 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarCollapsedDownForYAlignedSensor_270() = @@ -489,7 +519,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = false, { rotation = Surface.ROTATION_270 }, windowInsets = insetsForSmallNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun hidesSfpsIndicatorWhenOccludingTaskbarForYAlignedSensor_270() = @@ -498,7 +530,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = false, { rotation = Surface.ROTATION_270 }, windowInsets = insetsForLargeNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) + } @Test fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_InReverseDefaultRotation_0() = @@ -506,7 +540,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.Y_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_0 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_InReverseDefaultRotation_90() = @@ -524,7 +560,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.Y_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_180 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarCollapsedDownForYAlignedSensor_InReverseDefaultRotation_180() = @@ -533,7 +571,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = true, { rotation = Surface.ROTATION_180 }, windowInsets = insetsForSmallNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun hidesSfpsIndicatorWhenOccludingTaskbarForYAlignedSensor_InReverseDefaultRotation_180() = @@ -542,7 +582,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = true, { rotation = Surface.ROTATION_180 }, windowInsets = insetsForLargeNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) + } @Test fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_InReverseDefaultRotation_270() = @@ -550,7 +592,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.Y_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_270 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_0() = diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt index e35b2a384bd0..28e80057a672 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt @@ -39,11 +39,9 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat -import java.io.File -import java.util.Optional -import java.util.function.Consumer import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -58,7 +56,9 @@ import org.mockito.ArgumentMatchers.anyString import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.`when` import org.mockito.Mockito.anyInt +import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.inOrder import org.mockito.Mockito.mock import org.mockito.Mockito.never @@ -66,9 +66,10 @@ import org.mockito.Mockito.reset import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions -import org.mockito.Mockito.`when` -import org.mockito.Mockito.clearInvocations import org.mockito.MockitoAnnotations +import java.io.File +import java.util.* +import java.util.function.Consumer @SmallTest @RunWith(AndroidTestingRunner::class) @@ -146,6 +147,7 @@ class ControlsControllerImplTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) + whenever(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf()) `when`(userTracker.userHandle).thenReturn(UserHandle.of(user)) delayableExecutor = FakeExecutor(FakeSystemClock()) @@ -945,6 +947,28 @@ class ControlsControllerImplTest : SysuiTestCase() { controller.bindComponentForPanel(TEST_COMPONENT) verify(bindingController).bindServiceForPanel(TEST_COMPONENT) } + + @Test + fun testRemoveFavoriteRemovesFavorite() { + val componentName = ComponentName(context, "test.Cls") + controller.addFavorite( + componentName, + "test structure", + ControlInfo( + controlId = "testId", + controlTitle = "Test Control", + controlSubtitle = "test control subtitle", + deviceType = DeviceTypes.TYPE_LIGHT, + ), + ) + + controller.removeFavorites(componentName) + delayableExecutor.runAllReady() + + verify(authorizedPanelsRepository) + .removeAuthorizedPanels(eq(setOf(componentName.packageName))) + assertThat(controller.getFavorites()).isEmpty() + } } private class DidRunRunnable() : Runnable { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt index b91a3fd4b28c..7ac1953ee495 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt @@ -115,6 +115,18 @@ class AuthorizedPanelsRepositoryImplTest : SysuiTestCase() { assertThat(sharedPrefs.getStringSet(KEY, null)).containsExactly(TEST_PACKAGE) } + @Test + fun testRemoveAuthorizedPackageRemovesIt() { + val sharedPrefs = FakeSharedPreferences() + val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs)) + val repository = createRepository(fileManager) + repository.addAuthorizedPanels(setOf(TEST_PACKAGE)) + + repository.removeAuthorizedPanels(setOf(TEST_PACKAGE)) + + assertThat(sharedPrefs.getStringSet(KEY, null)).isEmpty() + } + private fun createRepository(userFileManager: UserFileManager): AuthorizedPanelsRepositoryImpl { return AuthorizedPanelsRepositoryImpl(mContext, userFileManager, userTracker) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt index 0c9986d82447..5a613aa9225e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt @@ -104,7 +104,9 @@ class ControlsSettingsDialogManagerImplTest : SysuiTestCase() { controlsSettingsRepository, userTracker, activityStarter - ) { context, _ -> TestableAlertDialog(context).also { dialog = it } } + ) { context, _ -> + TestableAlertDialog(context).also { dialog = it } + } } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt new file mode 100644 index 000000000000..1e8cd4117688 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.controls.ui + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.FakeSystemUIDialogController +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.eq +import org.mockito.Mockito.verify + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class ControlsDialogsFactoryTest : SysuiTestCase() { + + private companion object { + const val APP_NAME = "Test App" + } + + private val fakeDialogController = FakeSystemUIDialogController() + + private lateinit var underTest: ControlsDialogsFactory + + @Before + fun setup() { + underTest = ControlsDialogsFactory { fakeDialogController.dialog } + } + + @Test + fun testCreatesRemoveAppDialog() { + val dialog = underTest.createRemoveAppDialog(context, APP_NAME) {} + + verify(dialog) + .setTitle( + eq(context.getString(R.string.controls_panel_remove_app_authorization, APP_NAME)) + ) + verify(dialog).setCanceledOnTouchOutside(eq(true)) + } + + @Test + fun testPositiveClickRemoveAppDialogWorks() { + var dialogResult: Boolean? = null + underTest.createRemoveAppDialog(context, APP_NAME) { dialogResult = it } + + fakeDialogController.clickPositive() + + assertThat(dialogResult).isTrue() + } + + @Test + fun testNeutralClickRemoveAppDialogWorks() { + var dialogResult: Boolean? = null + underTest.createRemoveAppDialog(context, APP_NAME) { dialogResult = it } + + fakeDialogController.clickNeutral() + + assertThat(dialogResult).isFalse() + } + + @Test + fun testCancelRemoveAppDialogWorks() { + var dialogResult: Boolean? = null + underTest.createRemoveAppDialog(context, APP_NAME) { dialogResult = it } + + fakeDialogController.cancel() + + assertThat(dialogResult).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt index aa90e2a45f10..23faa99c0b9d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt @@ -52,6 +52,7 @@ import com.android.systemui.shade.ShadeController import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.FakeSharedPreferences +import com.android.systemui.util.FakeSystemUIDialogController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor @@ -63,21 +64,20 @@ import com.android.systemui.util.time.FakeSystemClock import com.android.wm.shell.TaskView import com.android.wm.shell.TaskViewFactory import com.google.common.truth.Truth.assertThat -import dagger.Lazy -import java.util.Optional -import java.util.function.Consumer import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.`when` import org.mockito.Mockito.anyInt import org.mockito.Mockito.anyString import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import java.util.Optional +import java.util.function.Consumer @SmallTest @RunWith(AndroidTestingRunner::class) @@ -98,13 +98,15 @@ class ControlsUiControllerImplTest : SysuiTestCase() { @Mock lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository @Mock lateinit var featureFlags: FeatureFlags @Mock lateinit var packageManager: PackageManager - val sharedPreferences = FakeSharedPreferences() - lateinit var controlsSettingsRepository: FakeControlsSettingsRepository - var uiExecutor = FakeExecutor(FakeSystemClock()) - var bgExecutor = FakeExecutor(FakeSystemClock()) - lateinit var underTest: ControlsUiControllerImpl - lateinit var parent: FrameLayout + private val sharedPreferences = FakeSharedPreferences() + private val fakeDialogController = FakeSystemUIDialogController() + private val uiExecutor = FakeExecutor(FakeSystemClock()) + private val bgExecutor = FakeExecutor(FakeSystemClock()) + + private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository + private lateinit var parent: FrameLayout + private lateinit var underTest: ControlsUiControllerImpl @Before fun setup() { @@ -125,12 +127,12 @@ class ControlsUiControllerImplTest : SysuiTestCase() { underTest = ControlsUiControllerImpl( - Lazy { controlsController }, + { controlsController }, context, packageManager, uiExecutor, bgExecutor, - Lazy { controlsListingController }, + { controlsListingController }, controlActionCoordinator, activityStarter, iconCache, @@ -142,7 +144,8 @@ class ControlsUiControllerImplTest : SysuiTestCase() { controlsSettingsRepository, authorizedPanelsRepository, featureFlags, - dumpManager + ControlsDialogsFactory { fakeDialogController.dialog }, + dumpManager, ) `when`( userFileManager.getSharedPreferences( @@ -410,8 +413,45 @@ class ControlsUiControllerImplTest : SysuiTestCase() { verify(controlsListingController, never()).removeCallback(any()) } + @Test + fun testRemovingAppsRemovesFavorite() { + val componentName = ComponentName(context, "cls") + whenever(controlsController.removeFavorites(eq(componentName))).thenReturn(true) + val panel = SelectedItem.PanelItem("App name", componentName) + sharedPreferences + .edit() + .putString("controls_component", panel.componentName.flattenToString()) + .putString("controls_structure", panel.appName.toString()) + .putBoolean("controls_is_panel", true) + .commit() + underTest.show(parent, {}, context) + underTest.startRemovingApp(componentName, "Test App") + + fakeDialogController.clickPositive() + + verify(controlsController).removeFavorites(eq(componentName)) + assertThat(underTest.getPreferredSelectedItem(emptyList())) + .isEqualTo(SelectedItem.EMPTY_SELECTION) + with(sharedPreferences) { + assertThat(contains("controls_component")).isFalse() + assertThat(contains("controls_structure")).isFalse() + assertThat(contains("controls_is_panel")).isFalse() + } + } + + @Test + fun testHideCancelsTheRemoveAppDialog() { + val componentName = ComponentName(context, "cls") + underTest.show(parent, {}, context) + underTest.startRemovingApp(componentName, "Test App") + + underTest.hide(parent) + + verify(fakeDialogController.dialog).cancel() + } + private fun setUpPanel(panel: SelectedItem.PanelItem): ControlsServiceInfo { - val activity = ComponentName("pkg", "activity") + val activity = ComponentName(context, "activity") sharedPreferences .edit() .putString("controls_component", panel.componentName.flattenToString()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt index dbaf94f1018c..483ab3bae6f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt @@ -37,7 +37,9 @@ class OverflowMenuAdapterTest : SysuiTestCase() { context, layoutId = 0, labels.zip(ids).map { OverflowMenuAdapter.MenuItem(it.first, it.second) } - ) { true } + ) { + true + } ids.forEachIndexed { index, id -> assertThat(adapter.getItemId(index)).isEqualTo(id) } } @@ -51,7 +53,9 @@ class OverflowMenuAdapterTest : SysuiTestCase() { context, layoutId = 0, labels.zip(ids).map { OverflowMenuAdapter.MenuItem(it.first, it.second) } - ) { position -> position == 0 } + ) { position -> + position == 0 + } assertThat(adapter.isEnabled(0)).isTrue() assertThat(adapter.isEnabled(1)).isFalse() diff --git a/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt new file mode 100644 index 000000000000..820329119f8d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.devicepolicy + +import android.app.admin.DevicePolicyManager +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.settings.UserTracker +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class DevicePolicyManagerExtTest : SysuiTestCase() { + + @Mock lateinit var devicePolicyManager: DevicePolicyManager + @Mock private lateinit var userTracker: UserTracker + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + whenever(userTracker.userId).thenReturn(CURRENT_USER_ID) + } + + // region areKeyguardShortcutsDisabled + @Test + fun areKeyguardShortcutsDisabled_noDisabledKeyguardFeature_shouldReturnFalse() { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt())) + .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE) + + assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID)) + .isFalse() + } + + @Test + fun areKeyguardShortcutsDisabled_otherDisabledKeyguardFeatures_shouldReturnFalse() { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt())) + .thenReturn(KEYGUARD_DISABLE_SECURE_CAMERA or KEYGUARD_DISABLE_FACE) + + assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID)) + .isFalse() + } + + @Test + fun areKeyguardShortcutsDisabled_disabledShortcutsKeyguardFeature_shouldReturnTrue() { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt())) + .thenReturn(KEYGUARD_DISABLE_SHORTCUTS_ALL) + + assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID)) + .isTrue() + } + + @Test + fun areKeyguardShortcutsDisabled_disabledAllKeyguardFeatures_shouldReturnTrue() { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt())) + .thenReturn(KEYGUARD_DISABLE_FEATURES_ALL) + + assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID)) + .isTrue() + } + // endregion + + private companion object { + const val CURRENT_USER_ID = 123 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index 9e70c27f7881..aa17d4985f82 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -73,6 +73,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase { private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package", "lowlight"); private static final String DREAM_COMPONENT = "package/dream"; + private static final String WINDOW_NAME = "test"; private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); @@ -190,7 +191,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { mUiEventLogger, mTouchInsetManager, LOW_LIGHT_COMPONENT, - mDreamOverlayCallbackController); + mDreamOverlayCallbackController, + WINDOW_NAME); } public IDreamOverlayClient getClient() throws RemoteException { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java index ef62abfe36de..175da0b7a5c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java @@ -33,6 +33,8 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.condition.SelfExecutingMonitor; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.smartspace.DreamSmartspaceController; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.BcSmartspaceDataPlugin; import com.android.systemui.shared.condition.Condition; import com.android.systemui.shared.condition.Monitor; @@ -65,6 +67,9 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { @Mock private View mBcSmartspaceView; + @Mock + private FeatureFlags mFeatureFlags; + private Monitor mMonitor; private final Set<Condition> mPreconditions = new HashSet<>(); @@ -73,6 +78,8 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { public void setup() { MockitoAnnotations.initMocks(this); mMonitor = SelfExecutingMonitor.createInstance(); + + when(mFeatureFlags.isEnabled(Flags.HIDE_SMARTSPACE_ON_DREAM_OVERLAY)).thenReturn(false); } /** @@ -85,12 +92,22 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { verify(mDreamOverlayStateController, never()).addComplication(eq(mComplication)); } - private SmartSpaceComplication.Registrant getRegistrant() { - return new SmartSpaceComplication.Registrant( - mDreamOverlayStateController, - mComplication, - mSmartspaceController, - mMonitor); + @Test + public void testRegistrantStart_featureEnabled_addOverlayStateCallback() { + final SmartSpaceComplication.Registrant registrant = getRegistrant(); + registrant.start(); + + verify(mDreamOverlayStateController).addCallback(any()); + } + + @Test + public void testRegistrantStart_featureDisabled_doesNotAddOverlayStateCallback() { + when(mFeatureFlags.isEnabled(Flags.HIDE_SMARTSPACE_ON_DREAM_OVERLAY)).thenReturn(true); + + final SmartSpaceComplication.Registrant registrant = getRegistrant(); + registrant.start(); + + verify(mDreamOverlayStateController, never()).addCallback(any()); } @Test @@ -188,4 +205,13 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mBcSmartspaceView); assertEquals(viewHolder.getView(), viewHolder.getView()); } + + private SmartSpaceComplication.Registrant getRegistrant() { + return new SmartSpaceComplication.Registrant( + mDreamOverlayStateController, + mComplication, + mSmartspaceController, + mMonitor, + mFeatureFlags); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 7c20e3c9baff..c93e677071cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -29,7 +29,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -67,7 +66,6 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; @@ -136,7 +134,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock SysuiColorExtractor mColorExtractor; private @Mock AuthController mAuthController; private @Mock ShadeExpansionStateManager mShadeExpansionStateManager; - private @Mock FeatureFlags mFeatureFlags; private @Mock ShadeWindowLogger mShadeWindowLogger; private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -545,7 +542,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mScreenOnCoordinator, mInteractionJankMonitor, mDreamOverlayStateController, - mFeatureFlags, () -> mShadeController, () -> mNotificationShadeWindowController, () -> mActivityLaunchAnimator, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt index 21ad5e2cd311..5dc04f7efa63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt @@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.coroutines.collectLastValue @@ -38,11 +39,14 @@ import com.android.systemui.keyguard.data.repository.BiometricType.FACE import com.android.systemui.keyguard.data.repository.BiometricType.REAR_FINGERPRINT import com.android.systemui.keyguard.data.repository.BiometricType.SIDE_FINGERPRINT import com.android.systemui.keyguard.data.repository.BiometricType.UNDER_DISPLAY_FINGERPRINT +import com.android.systemui.keyguard.shared.model.DevicePosture +import com.android.systemui.statusbar.policy.DevicePostureController import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.TestScope @@ -62,6 +66,7 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidTestingRunner::class) @@ -78,6 +83,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { private lateinit var biometricManagerCallback: ArgumentCaptor<IBiometricEnabledOnKeyguardCallback.Stub> private lateinit var userRepository: FakeUserRepository + private lateinit var devicePostureRepository: FakeDevicePostureRepository private lateinit var testDispatcher: TestDispatcher private lateinit var testScope: TestScope @@ -90,6 +96,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { testDispatcher = StandardTestDispatcher() testScope = TestScope(testDispatcher) userRepository = FakeUserRepository() + devicePostureRepository = FakeDevicePostureRepository() } private suspend fun createBiometricSettingsRepository() { @@ -108,6 +115,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { looper = testableLooper!!.looper, dumpManager = dumpManager, biometricManager = biometricManager, + devicePostureRepository = devicePostureRepository, ) testScope.runCurrent() } @@ -299,6 +307,50 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { verify(biometricManager, times(1)).registerEnabledOnKeyguardCallback(any()) } + @Test + fun faceAuthIsAlwaysSupportedIfSpecificPostureIsNotConfigured() = + testScope.runTest { + overrideResource( + R.integer.config_face_auth_supported_posture, + DevicePostureController.DEVICE_POSTURE_UNKNOWN + ) + + createBiometricSettingsRepository() + + assertThat(collectLastValue(underTest.isFaceAuthSupportedInCurrentPosture)()).isTrue() + } + + @Test + fun faceAuthIsSupportedOnlyWhenDevicePostureMatchesConfigValue() = + testScope.runTest { + overrideResource( + R.integer.config_face_auth_supported_posture, + DevicePostureController.DEVICE_POSTURE_FLIPPED + ) + + createBiometricSettingsRepository() + + val isFaceAuthSupported = + collectLastValue(underTest.isFaceAuthSupportedInCurrentPosture) + + assertThat(isFaceAuthSupported()).isFalse() + + devicePostureRepository.setCurrentPosture(DevicePosture.CLOSED) + assertThat(isFaceAuthSupported()).isFalse() + + devicePostureRepository.setCurrentPosture(DevicePosture.HALF_OPENED) + assertThat(isFaceAuthSupported()).isFalse() + + devicePostureRepository.setCurrentPosture(DevicePosture.OPENED) + assertThat(isFaceAuthSupported()).isFalse() + + devicePostureRepository.setCurrentPosture(DevicePosture.UNKNOWN) + assertThat(isFaceAuthSupported()).isFalse() + + devicePostureRepository.setCurrentPosture(DevicePosture.FLIPPED) + assertThat(isFaceAuthSupported()).isTrue() + } + private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) { authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt new file mode 100644 index 000000000000..bd6b7a853dfc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.repository + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.shared.model.DevicePosture +import com.android.systemui.statusbar.policy.DevicePostureController +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class DevicePostureRepositoryTest : SysuiTestCase() { + private lateinit var underTest: DevicePostureRepository + private lateinit var testScope: TestScope + @Mock private lateinit var devicePostureController: DevicePostureController + @Captor private lateinit var callback: ArgumentCaptor<DevicePostureController.Callback> + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + testScope = TestScope() + underTest = DevicePostureRepositoryImpl(postureController = devicePostureController) + } + + @Test + fun postureChangesArePropagated() = + testScope.runTest { + whenever(devicePostureController.devicePosture) + .thenReturn(DevicePostureController.DEVICE_POSTURE_FLIPPED) + val currentPosture = collectLastValue(underTest.currentDevicePosture) + assertThat(currentPosture()).isEqualTo(DevicePosture.FLIPPED) + + verify(devicePostureController).addCallback(callback.capture()) + + callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_UNKNOWN) + assertThat(currentPosture()).isEqualTo(DevicePosture.UNKNOWN) + + callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_CLOSED) + assertThat(currentPosture()).isEqualTo(DevicePosture.CLOSED) + + callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED) + assertThat(currentPosture()).isEqualTo(DevicePosture.HALF_OPENED) + + callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED) + assertThat(currentPosture()).isEqualTo(DevicePosture.OPENED) + + callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_FLIPPED) + assertThat(currentPosture()).isEqualTo(DevicePosture.FLIPPED) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt index 0469e77ca991..0e6f8d4e0720 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt @@ -219,6 +219,29 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { } @Test + fun isKeyguardUnlocked() = + runTest(UnconfinedTestDispatcher()) { + whenever(keyguardStateController.isUnlocked).thenReturn(false) + var latest: Boolean? = null + val job = underTest.isKeyguardUnlocked.onEach { latest = it }.launchIn(this) + + assertThat(latest).isFalse() + + val captor = argumentCaptor<KeyguardStateController.Callback>() + verify(keyguardStateController).addCallback(captor.capture()) + + whenever(keyguardStateController.isUnlocked).thenReturn(true) + captor.value.onUnlockedChanged() + assertThat(latest).isTrue() + + whenever(keyguardStateController.isUnlocked).thenReturn(false) + captor.value.onUnlockedChanged() + assertThat(latest).isFalse() + + job.cancel() + } + + @Test fun isDozing() = runTest(UnconfinedTestDispatcher()) { var latest: Boolean? = null diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt index d2db910ad443..f9493d10ff61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt @@ -62,7 +62,9 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() { fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK) runCurrent() - values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },) + values.assertEffectsMatchPredicates( + { it == DEFAULT_REVEAL_EFFECT }, + ) // We got a source but still have no sensor locations, so should be sticking with // the default effect. @@ -71,14 +73,18 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() { ) runCurrent() - values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },) + values.assertEffectsMatchPredicates( + { it == DEFAULT_REVEAL_EFFECT }, + ) // We got a location for the face sensor, but we unlocked with fingerprint. val faceLocation = Point(250, 0) fakeKeyguardRepository.setFaceSensorLocation(faceLocation) runCurrent() - values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },) + values.assertEffectsMatchPredicates( + { it == DEFAULT_REVEAL_EFFECT }, + ) // Now we have fingerprint sensor locations, and wake and unlock via fingerprint. val fingerprintLocation = Point(500, 500) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index ae7a928cdb2c..fe9098fa5c25 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -19,6 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN import com.android.systemui.SysuiTestCase import com.android.systemui.animation.Interpolators import com.android.systemui.flags.FakeFeatureFlags @@ -40,6 +42,7 @@ import com.android.systemui.keyguard.util.KeyguardTransitionRunner import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.cancelChildren @@ -51,6 +54,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.reset import org.mockito.Mockito.verify @@ -77,6 +82,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // Used to verify transition requests for test output @Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository @Mock private lateinit var commandQueue: CommandQueue + @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor @@ -102,6 +108,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { transitionRepository = KeyguardTransitionRepositoryImpl() runner = KeyguardTransitionRunner(transitionRepository) + whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN) + val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) } fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor( @@ -173,16 +181,17 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { keyguardInteractor = createKeyguardInteractor(featureFlags), keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), + keyguardSecurityModel = keyguardSecurityModel, ) fromPrimaryBouncerTransitionInteractor.start() } @Test - fun `DREAMING to LOCKSCREEN - dreaming state changes first`() = + fun `DREAMING to LOCKSCREEN`() = testScope.runTest { - // GIVEN a device is dreaming and occluded + // GIVEN a device is dreaming keyguardRepository.setDreamingWithOverlay(true) - keyguardRepository.setKeyguardOccluded(true) + keyguardRepository.setWakefulnessModel(startingToWake()) runCurrent() // GIVEN a prior transition has run to DREAMING @@ -215,56 +224,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) - } - // THEN a transition to BOUNCER should occur - assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor") - assertThat(info.from).isEqualTo(KeyguardState.DREAMING) - assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN) - assertThat(info.animator).isNotNull() - - coroutineContext.cancelChildren() - } - - @Test - fun `DREAMING to LOCKSCREEN - occluded state changes first`() = - testScope.runTest { - // GIVEN a device is dreaming and occluded - keyguardRepository.setDreamingWithOverlay(true) - keyguardRepository.setKeyguardOccluded(true) - runCurrent() - - // GIVEN a prior transition has run to DREAMING - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.DREAMING, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) - - // WHEN doze is complete - keyguardRepository.setDozeTransitionModel( - DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH) - ) - // AND occluded has stopped - keyguardRepository.setKeyguardOccluded(false) - advanceUntilIdle() - // AND then dreaming has stopped - keyguardRepository.setDreamingWithOverlay(false) - advanceUntilIdle() - - val info = - withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to BOUNCER should occur assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor") @@ -304,7 +264,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to PRIMARY_BOUNCER should occur assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor") @@ -345,7 +305,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to DOZING should occur assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor") @@ -386,7 +346,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to DOZING should occur assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor") @@ -427,7 +387,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to DOZING should occur assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor") @@ -468,7 +428,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to DOZING should occur assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor") @@ -505,7 +465,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to DOZING should occur assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor") @@ -542,7 +502,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to DOZING should occur assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor") @@ -583,7 +543,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to DOZING should occur assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor") @@ -624,7 +584,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to AOD should occur assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor") @@ -661,7 +621,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to AOD should occur assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor") @@ -677,6 +637,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { testScope.runTest { // GIVEN a device that is not dreaming or dozing keyguardRepository.setDreamingWithOverlay(false) + keyguardRepository.setWakefulnessModel(startingToWake()) keyguardRepository.setDozeTransitionModel( DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH) ) @@ -704,7 +665,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to DREAMING should occur assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor") @@ -741,7 +702,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to PRIMARY_BOUNCER should occur assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor") @@ -784,7 +745,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to AOD should occur assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor") @@ -828,7 +789,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to DOZING should occur assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor") @@ -870,7 +831,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to LOCKSCREEN should occur assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor") @@ -912,7 +873,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to AOD should occur assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor") @@ -954,7 +915,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to DOZING should occur assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor") @@ -995,7 +956,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { val info = withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture()) + verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) } // THEN a transition to LOCKSCREEN should occur assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor") diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt index 8bd8be565eee..c727b3a6cd10 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt @@ -308,7 +308,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { TestConfig( isVisible = true, isClickable = false, - isActivated = true, + isActivated = false, icon = icon, canShowWhileLocked = false, intent = Intent("action"), @@ -363,7 +363,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { TestConfig( isVisible = true, isClickable = false, - isActivated = true, + isActivated = false, icon = icon, canShowWhileLocked = false, intent = Intent("action"), diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt index 8c54da1c3153..ab0669a28f04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt @@ -139,6 +139,7 @@ class MediaDataManagerTest : SysuiTestCase() { @Mock private lateinit var logger: MediaUiEventLogger lateinit var mediaDataManager: MediaDataManager lateinit var mediaNotification: StatusBarNotification + lateinit var remoteCastNotification: StatusBarNotification @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData> private val clock = FakeSystemClock() @Mock private lateinit var tunerService: TunerService @@ -207,6 +208,20 @@ class MediaDataManagerTest : SysuiTestCase() { } build() } + remoteCastNotification = + SbnBuilder().run { + setPkg(SYSTEM_PACKAGE_NAME) + modifyNotification(context).also { + it.setSmallIcon(android.R.drawable.ic_media_pause) + it.setStyle( + MediaStyle().apply { + setMediaSession(session.sessionToken) + setRemotePlaybackInfo("Remote device", 0, null) + } + ) + } + build() + } metadataBuilder = MediaMetadata.Builder().apply { putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST) @@ -247,6 +262,7 @@ class MediaDataManagerTest : SysuiTestCase() { whenever(mediaFlags.isExplicitIndicatorEnabled()).thenReturn(true) whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(false) whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false) + whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(false) whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId()) whenever(keyguardUpdateMonitor.isUserInLockdown(any())).thenReturn(false) } @@ -400,33 +416,8 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnNotificationAdded_isRcn_markedRemote() { - val rcn = - SbnBuilder().run { - setPkg(SYSTEM_PACKAGE_NAME) - modifyNotification(context).also { - it.setSmallIcon(android.R.drawable.ic_media_pause) - it.setStyle( - MediaStyle().apply { - setMediaSession(session.sessionToken) - setRemotePlaybackInfo("Remote device", 0, null) - } - ) - } - build() - } + addNotificationAndLoad(remoteCastNotification) - mediaDataManager.onNotificationAdded(KEY, rcn) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener) - .onMediaDataLoaded( - eq(KEY), - eq(null), - capture(mediaDataCaptor), - eq(true), - eq(0), - eq(false) - ) assertThat(mediaDataCaptor.value!!.playbackLocation) .isEqualTo(MediaData.PLAYBACK_CAST_REMOTE) verify(logger) @@ -710,6 +701,56 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test + fun testOnNotificationRemoved_withResumption_isRemoteAndRemoteAllowed() { + // With the flag enabled to allow remote media to resume + whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true) + + // GIVEN that the manager has a notification with a resume action, but is not local + whenever(controller.metadata).thenReturn(metadataBuilder.build()) + whenever(playbackInfo.playbackType) + .thenReturn(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) + addNotificationAndLoad() + val data = mediaDataCaptor.value + val dataRemoteWithResume = + data.copy(resumeAction = Runnable {}, playbackLocation = MediaData.PLAYBACK_CAST_LOCAL) + mediaDataManager.onMediaDataLoaded(KEY, null, dataRemoteWithResume) + + // WHEN the notification is removed + mediaDataManager.onNotificationRemoved(KEY) + + // THEN the media data is converted to a resume state + verify(listener) + .onMediaDataLoaded( + eq(PACKAGE_NAME), + eq(KEY), + capture(mediaDataCaptor), + eq(true), + eq(0), + eq(false) + ) + assertThat(mediaDataCaptor.value.resumption).isTrue() + } + + @Test + fun testOnNotificationRemoved_withResumption_isRcnAndRemoteAllowed() { + // With the flag enabled to allow remote media to resume + whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true) + + // GIVEN that the manager has a remote cast notification + addNotificationAndLoad(remoteCastNotification) + val data = mediaDataCaptor.value + assertThat(data.playbackLocation).isEqualTo(MediaData.PLAYBACK_CAST_REMOTE) + val dataRemoteWithResume = data.copy(resumeAction = Runnable {}) + mediaDataManager.onMediaDataLoaded(KEY, null, dataRemoteWithResume) + + // WHEN the RCN is removed + mediaDataManager.onNotificationRemoved(KEY) + + // THEN the media data is removed + verify(listener).onMediaDataRemoved(eq(KEY)) + } + + @Test fun testOnNotificationRemoved_withResumption_tooManyPlayers() { // Given the maximum number of resume controls already val desc = @@ -1654,22 +1695,7 @@ class MediaDataManagerTest : SysuiTestCase() { ) // update to remote cast - val rcn = - SbnBuilder().run { - setPkg(SYSTEM_PACKAGE_NAME) // System package - modifyNotification(context).also { - it.setSmallIcon(android.R.drawable.ic_media_pause) - it.setStyle( - MediaStyle().apply { - setMediaSession(session.sessionToken) - setRemotePlaybackInfo("Remote device", 0, null) - } - ) - } - build() - } - - mediaDataManager.onNotificationAdded(KEY, rcn) + mediaDataManager.onNotificationAdded(KEY, remoteCastNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) verify(logger) @@ -2038,9 +2064,14 @@ class MediaDataManagerTest : SysuiTestCase() { verify(listener).onMediaDataRemoved(eq(KEY)) } - /** Helper function to add a media notification and capture the resulting MediaData */ + /** Helper function to add a basic media notification and capture the resulting MediaData */ private fun addNotificationAndLoad() { - mediaDataManager.onNotificationAdded(KEY, mediaNotification) + addNotificationAndLoad(mediaNotification) + } + + /** Helper function to add the given notification and capture the resulting MediaData */ + private fun addNotificationAndLoad(sbn: StatusBarNotification) { + mediaDataManager.onNotificationAdded(KEY, sbn) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) verify(listener) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt index 136ace173795..4dfa6261b868 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.media.controls.models.player.MediaData import com.android.systemui.media.controls.models.player.MediaDeviceData import com.android.systemui.media.controls.pipeline.MediaDataManager import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT +import com.android.systemui.media.controls.util.MediaFlags import com.android.systemui.settings.UserTracker import com.android.systemui.tuner.TunerService import com.android.systemui.util.concurrency.FakeExecutor @@ -92,6 +93,7 @@ class MediaResumeListenerTest : SysuiTestCase() { @Mock private lateinit var mockContext: Context @Mock private lateinit var pendingIntent: PendingIntent @Mock private lateinit var dumpManager: DumpManager + @Mock private lateinit var mediaFlags: MediaFlags @Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback> @Captor lateinit var actionCaptor: ArgumentCaptor<Runnable> @@ -134,6 +136,7 @@ class MediaResumeListenerTest : SysuiTestCase() { whenever(mockContext.packageManager).thenReturn(context.packageManager) whenever(mockContext.contentResolver).thenReturn(context.contentResolver) whenever(mockContext.userId).thenReturn(context.userId) + whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(false) executor = FakeExecutor(clock) resumeListener = @@ -146,7 +149,8 @@ class MediaResumeListenerTest : SysuiTestCase() { tunerService, resumeBrowserFactory, dumpManager, - clock + clock, + mediaFlags, ) resumeListener.setManager(mediaDataManager) mediaDataManager.addListener(resumeListener) @@ -188,7 +192,8 @@ class MediaResumeListenerTest : SysuiTestCase() { tunerService, resumeBrowserFactory, dumpManager, - clock + clock, + mediaFlags, ) listener.setManager(mediaDataManager) verify(broadcastDispatcher, never()) @@ -244,6 +249,32 @@ class MediaResumeListenerTest : SysuiTestCase() { } @Test + fun testOnLoad_localCast_remoteResumeAllowed_doesCheck() { + // If local cast media is allowed to resume + whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true) + + // When media data is loaded that has not been checked yet, and is a local cast + val dataCast = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_LOCAL) + resumeListener.onMediaDataLoaded(KEY, null, dataCast) + + // Then we report back to the manager + verify(mediaDataManager).setResumeAction(KEY, null) + } + + @Test + fun testOnLoad_remoteCast_remoteResumeAllowed_doesCheck() { + // If local cast media is allowed to resume + whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true) + + // When media data is loaded that has not been checked yet, and is a remote cast + val dataRcn = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_REMOTE) + resumeListener.onMediaDataLoaded(KEY, null, dataRcn) + + // Then we do not take action + verify(mediaDataManager, never()).setResumeAction(any(), any()) + } + + @Test fun testOnLoad_checksForResume_hasService() { setUpMbsWithValidResolveInfo() @@ -389,7 +420,8 @@ class MediaResumeListenerTest : SysuiTestCase() { tunerService, resumeBrowserFactory, dumpManager, - clock + clock, + mediaFlags, ) resumeListener.setManager(mediaDataManager) mediaDataManager.addListener(resumeListener) @@ -421,7 +453,8 @@ class MediaResumeListenerTest : SysuiTestCase() { tunerService, resumeBrowserFactory, dumpManager, - clock + clock, + mediaFlags, ) resumeListener.setManager(mediaDataManager) mediaDataManager.addListener(resumeListener) @@ -463,7 +496,8 @@ class MediaResumeListenerTest : SysuiTestCase() { tunerService, resumeBrowserFactory, dumpManager, - clock + clock, + mediaFlags, ) resumeListener.setManager(mediaDataManager) mediaDataManager.addListener(resumeListener) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt index 4fc9ca71aeaa..85e8d072bd99 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt @@ -70,8 +70,7 @@ class MediaTttUtilsTest : SysuiTestCase() { context, appPackageName = null, isReceiver = false, - ) { - } + ) {} assertThat(iconInfo.isAppIcon).isFalse() assertThat(iconInfo.contentDescription.loadContentDescription(context)) @@ -86,8 +85,7 @@ class MediaTttUtilsTest : SysuiTestCase() { context, appPackageName = null, isReceiver = true, - ) { - } + ) {} assertThat(iconInfo.isAppIcon).isFalse() assertThat(iconInfo.contentDescription.loadContentDescription(context)) @@ -119,8 +117,7 @@ class MediaTttUtilsTest : SysuiTestCase() { context, appPackageName = "fakePackageName", isReceiver = false, - ) { - } + ) {} assertThat(iconInfo.isAppIcon).isFalse() assertThat(iconInfo.contentDescription.loadContentDescription(context)) @@ -135,8 +132,7 @@ class MediaTttUtilsTest : SysuiTestCase() { context, appPackageName = "fakePackageName", isReceiver = true, - ) { - } + ) {} assertThat(iconInfo.isAppIcon).isFalse() assertThat(iconInfo.contentDescription.loadContentDescription(context)) @@ -154,7 +150,9 @@ class MediaTttUtilsTest : SysuiTestCase() { context, appPackageName = "fakePackageName", isReceiver = false - ) { exceptionTriggered = true } + ) { + exceptionTriggered = true + } assertThat(exceptionTriggered).isTrue() } @@ -167,7 +165,9 @@ class MediaTttUtilsTest : SysuiTestCase() { context, appPackageName = "fakePackageName", isReceiver = true - ) { exceptionTriggered = true } + ) { + exceptionTriggered = true + } assertThat(exceptionTriggered).isTrue() } @@ -179,8 +179,7 @@ class MediaTttUtilsTest : SysuiTestCase() { context, PACKAGE_NAME, isReceiver = false, - ) { - } + ) {} assertThat(iconInfo.isAppIcon).isTrue() assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Loaded(appIconFromPackageName)) @@ -194,8 +193,7 @@ class MediaTttUtilsTest : SysuiTestCase() { context, PACKAGE_NAME, isReceiver = true, - ) { - } + ) {} assertThat(iconInfo.isAppIcon).isTrue() assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Loaded(appIconFromPackageName)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt index 0f0b7d7579c4..376b7cc70150 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt @@ -16,16 +16,17 @@ package com.android.systemui.notetask import android.app.KeyguardManager +import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.UserManager -import android.test.suitebuilder.annotation.SmallTest +import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.systemui.SysuiTestCase -import com.android.systemui.notetask.NoteTaskController.Companion.INTENT_EXTRA_USE_STYLUS_MODE import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity +import com.android.systemui.settings.UserTracker import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture @@ -38,6 +39,7 @@ import java.util.Optional import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions @@ -55,6 +57,8 @@ internal class NoteTaskControllerTest : SysuiTestCase() { @Mock lateinit var keyguardManager: KeyguardManager @Mock lateinit var userManager: UserManager @Mock lateinit var eventLogger: NoteTaskEventLogger + @Mock private lateinit var userTracker: UserTracker + @Mock private lateinit var devicePolicyManager: DevicePolicyManager private val noteTaskInfo = NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID) @@ -65,6 +69,13 @@ internal class NoteTaskControllerTest : SysuiTestCase() { whenever(context.packageManager).thenReturn(packageManager) whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(noteTaskInfo) whenever(userManager.isUserUnlocked).thenReturn(true) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE) } private fun createNoteTaskController( @@ -81,6 +92,8 @@ internal class NoteTaskControllerTest : SysuiTestCase() { optionalUserManager = Optional.ofNullable(userManager), optionalKeyguardManager = Optional.ofNullable(keyguardManager), isEnabled = isEnabled, + devicePolicyManager = devicePolicyManager, + userTracker = userTracker, ) // region onBubbleExpandChanged @@ -214,10 +227,10 @@ internal class NoteTaskControllerTest : SysuiTestCase() { val intentCaptor = argumentCaptor<Intent>() verify(context).startActivity(capture(intentCaptor)) intentCaptor.value.let { intent -> - assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) + assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE) assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) - assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue() + assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue() } verify(eventLogger).logNoteTaskOpened(expectedInfo) verifyZeroInteractions(bubbles) @@ -244,10 +257,10 @@ internal class NoteTaskControllerTest : SysuiTestCase() { val intentCaptor = argumentCaptor<Intent>() verify(bubbles).showOrHideAppBubble(capture(intentCaptor)) intentCaptor.value.let { intent -> - assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) + assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE) assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) - assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue() + assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue() } verifyZeroInteractions(eventLogger) } @@ -272,10 +285,10 @@ internal class NoteTaskControllerTest : SysuiTestCase() { val intentCaptor = argumentCaptor<Intent>() verify(context).startActivity(capture(intentCaptor)) intentCaptor.value.let { intent -> - assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) + assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE) assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) - assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue() + assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue() } verify(eventLogger).logNoteTaskOpened(expectedInfo) verifyZeroInteractions(bubbles) @@ -384,6 +397,102 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } // endregion + // region keyguard policy + @Test + fun showNoteTask_keyguardLocked_keyguardDisableShortcutsAll_shouldDoNothing() { + whenever(keyguardManager.isKeyguardLocked).thenReturn(true) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL) + + createNoteTaskController() + .showNoteTask( + isInMultiWindowMode = false, + entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE + ) + + verifyZeroInteractions(context, bubbles, eventLogger) + } + + @Test + fun showNoteTask_keyguardLocked_keyguardDisableFeaturesAll_shouldDoNothing() { + whenever(keyguardManager.isKeyguardLocked).thenReturn(true) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL) + + createNoteTaskController() + .showNoteTask( + isInMultiWindowMode = false, + entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE + ) + + verifyZeroInteractions(context, bubbles, eventLogger) + } + + @Test + fun showNoteTask_keyguardUnlocked_keyguardDisableShortcutsAll_shouldStartBubble() { + whenever(keyguardManager.isKeyguardLocked).thenReturn(false) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL) + + createNoteTaskController() + .showNoteTask( + isInMultiWindowMode = false, + entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE + ) + + val intentCaptor = argumentCaptor<Intent>() + verify(bubbles).showOrHideAppBubble(capture(intentCaptor)) + intentCaptor.value.let { intent -> + assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) + assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) + assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) + assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue() + } + } + + @Test + fun showNoteTask_keyguardUnlocked_keyguardDisableFeaturesAll_shouldStartBubble() { + whenever(keyguardManager.isKeyguardLocked).thenReturn(false) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL) + + createNoteTaskController() + .showNoteTask( + isInMultiWindowMode = false, + entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE + ) + + val intentCaptor = argumentCaptor<Intent>() + verify(bubbles).showOrHideAppBubble(capture(intentCaptor)) + intentCaptor.value.let { intent -> + assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) + assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) + assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) + assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue() + } + } + // endregion + private companion object { const val NOTES_PACKAGE_NAME = "com.android.note.app" const val NOTES_UID = 123456 diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt index d6495d8fe1b7..7f64f8a123e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt @@ -57,8 +57,9 @@ internal class NoteTaskInfoResolverTest : SysuiTestCase() { fun resolveInfo_shouldReturnInfo() { val packageName = "com.android.note.app" val uid = 123456 - whenever(roleManager.getRoleHoldersAsUser(NoteTaskInfoResolver.ROLE_NOTES, context.user)) - .then { listOf(packageName) } + whenever(roleManager.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, context.user)).then { + listOf(packageName) + } whenever( packageManager.getApplicationInfoAsUser( eq(packageName), @@ -78,8 +79,9 @@ internal class NoteTaskInfoResolverTest : SysuiTestCase() { @Test fun resolveInfo_packageManagerThrowsException_shouldReturnInfoWithZeroUid() { val packageName = "com.android.note.app" - whenever(roleManager.getRoleHoldersAsUser(NoteTaskInfoResolver.ROLE_NOTES, context.user)) - .then { listOf(packageName) } + whenever(roleManager.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, context.user)).then { + listOf(packageName) + } whenever( packageManager.getApplicationInfoAsUser( eq(packageName), @@ -98,8 +100,9 @@ internal class NoteTaskInfoResolverTest : SysuiTestCase() { @Test fun resolveInfo_noRoleHolderIsSet_shouldReturnNull() { - whenever(roleManager.getRoleHoldersAsUser(eq(NoteTaskInfoResolver.ROLE_NOTES), any())) - .then { listOf<String>() } + whenever(roleManager.getRoleHoldersAsUser(eq(RoleManager.ROLE_NOTES), any())).then { + emptyList<String>() + } val actual = underTest.resolveInfo() diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt index a8eab50e0798..46e02788b2df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt @@ -103,9 +103,7 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { // region handleSystemKey @Test fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() { - createNoteTaskInitializer() - .callbacks - .handleSystemKey(NoteTaskController.NOTE_TASK_KEY_EVENT) + createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) verify(noteTaskController).showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt index 030c59faa696..5e0190b65a12 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.util.settings.GlobalSettings import com.google.common.truth.Truth.assertThat import dagger.Lazy +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -94,6 +95,12 @@ class AirplaneModeTileTest : SysuiTestCase() { mUserTracker) } + @After + fun tearDown() { + mTile.destroy() + mTestableLooper.processAllMessages() + } + @Test fun testIcon_whenDisabled_showsOffState() { val state = QSTile.BooleanState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt index b4a662974d22..f1e3e8a71398 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt @@ -21,6 +21,7 @@ import com.android.systemui.statusbar.policy.NextAlarmController import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -88,6 +89,12 @@ class AlarmTileTest : SysuiTestCase() { testableLooper.processAllMessages() } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testAvailable() { assertThat(tile.isAvailable).isTrue() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt index 95e7ad9fad4d..a5c0004afe02 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt @@ -37,6 +37,7 @@ import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.SecureSettings import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test @@ -103,6 +104,12 @@ class BatterySaverTileTest : SysuiTestCase() { testableLooper.processAllMessages() } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testSettingWithCorrectUser() { assertEquals(USER, tile.mSetting.currentUser) 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 bf172f12a07f..75fd0000e0e1 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 @@ -22,6 +22,7 @@ import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.BluetoothController import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -79,6 +80,12 @@ class BluetoothTileTest : SysuiTestCase() { testableLooper.processAllMessages() } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testRestrictionChecked() { tile.refreshState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt index cfbb82f5f338..41938541124a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController import com.android.systemui.statusbar.policy.KeyguardStateController import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -90,6 +91,12 @@ class CameraToggleTileTest : SysuiTestCase() { keyguardStateController) } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testIcon_whenCameraAccessEnabled_isOnState() { val state = QSTile.BooleanState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java index 18f891c5ea58..64fd09d5f5d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -53,6 +53,7 @@ import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -141,6 +142,12 @@ public class CastTileTest extends SysuiTestCase { mHotspotCallback = hotspotCallbackArgumentCaptor.getValue(); } + @After + public void tearDown() { + mCastTile.destroy(); + mTestableLooper.processAllMessages(); + } + // ------------------------------------------------- // All these tests for enabled/disabled wifi have hotspot not enabled @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java index fdb63cacef2a..13c30e9ea9ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java @@ -43,6 +43,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -101,6 +102,12 @@ public class ColorCorrectionTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void longClick_expectedAction() { final ArgumentCaptor<Intent> IntentCaptor = ArgumentCaptor.forClass(Intent.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java index 60c1a33d70e3..ff27e0255aa3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java @@ -46,6 +46,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -106,6 +107,12 @@ public class ColorInversionTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void longClick_expectedAction() { final ArgumentCaptor<Intent> IntentCaptor = ArgumentCaptor.forClass(Intent.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt index ce62f2d1cf36..b048643aba84 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.DataSaverController import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -84,6 +85,12 @@ class DataSaverTileTest : SysuiTestCase() { ) } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testIcon_whenDataSaverEnabled_isOnState() { val state = QSTile.BooleanState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt index e0b3125fd62a..b51c378f6b6b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt @@ -67,6 +67,7 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions import java.util.Optional +import org.junit.After @SmallTest @RunWith(AndroidTestingRunner::class) @@ -129,6 +130,12 @@ class DeviceControlsTileTest : SysuiTestCase() { tile = createTile() } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + private fun setupControlsComponent() { `when`(controlsComponent.getControlsController()).thenAnswer { if (featureEnabled) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt index ce5edb147d87..6c0904eb9bfd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt @@ -136,7 +136,8 @@ class DndTileTest : SysuiTestCase() { @After fun tearDown() { - tile.handleSetListening(false) + tile.destroy() + testableLooper.processAllMessages() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java index 71d8aa4ace7f..7d41aa6c3548 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java @@ -54,6 +54,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -112,6 +113,12 @@ public class DreamTileTest extends SysuiTestCase { mTile.initialize(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testNotAvailable() throws RemoteException { // Should not be available if screensaver is disabled diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt index c7aba1a9f59f..692a64422a7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt @@ -18,6 +18,7 @@ import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.FlashlightController import com.google.common.truth.Truth +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -71,6 +72,12 @@ class FlashlightTileTest : SysuiTestCase() { ) } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testIcon_whenFlashlightEnabled_isOnState() { Mockito.`when`(flashlightController.isAvailable).thenReturn(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt index 9d62cfd3743a..bd99cd482859 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable import com.android.systemui.util.settings.FakeSettings import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -89,6 +90,12 @@ class FontScalingTileTest : SysuiTestCase() { testableLooper.processAllMessages() } + @After + fun tearDown() { + fontScalingTile.destroy() + testableLooper.processAllMessages() + } + @Test fun isAvailable_whenFlagIsFalse_returnsFalse() { featureFlags.set(Flags.ENABLE_FONT_SCALING_TILE, false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java index 4a2ac96e1df0..959e750ac5f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java @@ -43,6 +43,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.HotspotController; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -94,6 +95,12 @@ public class HotspotTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void handleUpdateState_wifiTetheringIsAllowed_stateIsNotUnavailable() { MockitoSession mockitoSession = ExtendedMockito.mockitoSession() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java index abd9094f2c35..adfd7f71e8f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.WifiIndicators; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -91,6 +92,12 @@ public class InternetTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void setConnectivityStatus_defaultNetworkNotExists_updateTile() { mTile.mSignalCallback.setConnectivityStatus( diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt index 08d10fda0538..33921c7c84b1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.LocationController import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -88,6 +89,12 @@ class LocationTileTest : SysuiTestCase() { keyguardStateController) } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testIcon_whenDisabled_isOffState() { val state = QSTile.BooleanState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt index 1ab601ce3ebe..e2f64b2cc226 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController import com.android.systemui.statusbar.policy.KeyguardStateController import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -90,6 +91,12 @@ class MicrophoneToggleTileTest : SysuiTestCase() { keyguardStateController) } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testIcon_whenMicrophoneAccessEnabled_isOnState() { val state = QSTile.BooleanState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java index 9638a45396a9..c7dae83e2056 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java @@ -39,6 +39,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.logging.QSLogger; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -97,6 +98,12 @@ public class NfcTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mNfcTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testIsAvailable_stockWithoutNfc_returnsFalse() { when(mMockContext.getString(R.string.quick_settings_tiles_stock)).thenReturn( diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt index 188c3a3d9e42..04af69c84cf8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt @@ -37,6 +37,7 @@ import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.LocationController import com.google.common.truth.Truth +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -49,32 +50,23 @@ import org.mockito.MockitoAnnotations @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest class NightDisplayTileTest : SysuiTestCase() { - @Mock - private lateinit var mHost: QSHost + @Mock private lateinit var mHost: QSHost - @Mock - private lateinit var mMetricsLogger: MetricsLogger + @Mock private lateinit var mMetricsLogger: MetricsLogger - @Mock - private lateinit var mStatusBarStateController: StatusBarStateController + @Mock private lateinit var mStatusBarStateController: StatusBarStateController - @Mock - private lateinit var mActivityStarter: ActivityStarter + @Mock private lateinit var mActivityStarter: ActivityStarter - @Mock - private lateinit var mQsLogger: QSLogger + @Mock private lateinit var mQsLogger: QSLogger - @Mock - private lateinit var mLocationController: LocationController + @Mock private lateinit var mLocationController: LocationController - @Mock - private lateinit var mColorDisplayManager: ColorDisplayManager + @Mock private lateinit var mColorDisplayManager: ColorDisplayManager - @Mock - private lateinit var mNightDisplayListenerBuilder: NightDisplayListenerModule.Builder + @Mock private lateinit var mNightDisplayListenerBuilder: NightDisplayListenerModule.Builder - @Mock - private lateinit var mNightDisplayListener: NightDisplayListener + @Mock private lateinit var mNightDisplayListener: NightDisplayListener private lateinit var mTestableLooper: TestableLooper private lateinit var mTile: NightDisplayTile @@ -88,24 +80,30 @@ class NightDisplayTileTest : SysuiTestCase() { whenever(mHost.context).thenReturn(mContext) whenever(mHost.uiEventLogger).thenReturn(mUiEventLogger) whenever(mHost.userContext).thenReturn(mContext) - whenever(mNightDisplayListenerBuilder.setUser(anyInt())).thenReturn( - mNightDisplayListenerBuilder - ) + whenever(mNightDisplayListenerBuilder.setUser(anyInt())) + .thenReturn(mNightDisplayListenerBuilder) whenever(mNightDisplayListenerBuilder.build()).thenReturn(mNightDisplayListener) - mTile = NightDisplayTile( - mHost, - mTestableLooper.looper, - Handler(mTestableLooper.looper), - FalsingManagerFake(), - mMetricsLogger, - mStatusBarStateController, - mActivityStarter, - mQsLogger, - mLocationController, - mColorDisplayManager, - mNightDisplayListenerBuilder - ) + mTile = + NightDisplayTile( + mHost, + mTestableLooper.looper, + Handler(mTestableLooper.looper), + FalsingManagerFake(), + mMetricsLogger, + mStatusBarStateController, + mActivityStarter, + mQsLogger, + mLocationController, + mColorDisplayManager, + mNightDisplayListenerBuilder + ) + } + + @After + fun tearDown() { + mTile.destroy() + mTestableLooper.processAllMessages() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java index 3344a17064e4..652c138f6478 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java @@ -37,6 +37,7 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.SecureSettings; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -91,6 +92,12 @@ public class OneHandedModeTileTest extends SysuiTestCase { mTile.initialize(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testIsAvailable_unsupportOneHandedProperty_shouldReturnsFalse() { when(mTile.isSupportOneHandedMode()).thenReturn(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java index 24287eae8a03..3125d455acfb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java @@ -43,6 +43,7 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -91,6 +92,12 @@ public class QRCodeScannerTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testNewTile() { assertFalse(mTile.newTileState().handlesLongClick); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index 4722c8d4208f..596df7856ee1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -73,6 +73,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.wallet.controller.QuickAccessWalletController; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -161,6 +162,12 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testNewTile() { assertFalse(mTile.newTileState().handlesLongClick); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java index 99e5564051e1..7913628c5693 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java @@ -44,8 +44,8 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.UserTracker; +import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -54,7 +54,6 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest -@Ignore("b/269171747") public class ReduceBrightColorsTileTest extends SysuiTestCase { @Mock private QSHost mHost; @@ -99,6 +98,12 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testNotActive() { when(mReduceBrightColorsController.isReduceBrightColorsActivated()).thenReturn(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java index c7eb2f1c39c9..5b94cfedaedf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java @@ -48,6 +48,7 @@ import com.android.systemui.statusbar.policy.RotationLockControllerImpl; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.wrapper.RotationPolicyWrapper; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -139,6 +140,12 @@ public class RotationLockTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mLockTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testSecondaryString_cameraRotateOn_returnsFaceBased() { assertEquals(mContext.getString(R.string.rotation_lock_camera_rotation_on), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java index 21acc08ba143..5aef75832fac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java @@ -50,6 +50,7 @@ import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -114,6 +115,12 @@ public class ScreenRecordTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + // Test that the tile is inactive and labeled correctly when the controller is neither starting // or recording, and that clicking on the tile in this state brings up the record prompt @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt index 3d9f6506e7e7..b55657163382 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt @@ -39,6 +39,7 @@ import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.LocationController import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -98,6 +99,12 @@ class UiModeNightTileTest : SysuiTestCase() { ) } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testIcon_whenNightModeOn_isOnState() { val state = QSTile.BooleanState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java new file mode 100644 index 000000000000..4f469f753bdf --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -0,0 +1,754 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade; + +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; + +import static com.android.keyguard.KeyguardClockSwitch.LARGE; + +import static com.google.common.truth.Truth.assertThat; + +import static kotlinx.coroutines.flow.FlowKt.emptyFlow; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.annotation.IdRes; +import android.content.ContentResolver; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.UserManager; +import android.util.DisplayMetrics; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.ViewPropertyAnimator; +import android.view.ViewStub; +import android.view.ViewTreeObserver; +import android.view.accessibility.AccessibilityManager; + +import androidx.constraintlayout.widget.ConstraintSet; + +import com.android.internal.jank.InteractionJankMonitor; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.testing.UiEventLoggerFake; +import com.android.internal.util.LatencyTracker; +import com.android.keyguard.KeyguardClockSwitch; +import com.android.keyguard.KeyguardClockSwitchController; +import com.android.keyguard.KeyguardStatusView; +import com.android.keyguard.KeyguardStatusViewController; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.LockIconViewController; +import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; +import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; +import com.android.keyguard.dagger.KeyguardStatusViewComponent; +import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.biometrics.AuthController; +import com.android.systemui.classifier.FalsingCollectorFake; +import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.common.ui.view.LongPressHandlingView; +import com.android.systemui.doze.DozeLog; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.fragments.FragmentHostManager; +import com.android.systemui.fragments.FragmentService; +import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository; +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; +import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; +import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel; +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel; +import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel; +import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel; +import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel; +import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel; +import com.android.systemui.media.controls.pipeline.MediaDataManager; +import com.android.systemui.media.controls.ui.KeyguardMediaController; +import com.android.systemui.media.controls.ui.MediaHierarchyManager; +import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationBarController; +import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QS; +import com.android.systemui.qs.QSFragment; +import com.android.systemui.screenrecord.RecordingController; +import com.android.systemui.shade.transition.ShadeTransitionController; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.LockscreenShadeTransitionController; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.NotificationShelfController; +import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.QsFrameTranslateController; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.notification.ConversationNotificationManager; +import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.stack.AmbientState; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator; +import com.android.systemui.statusbar.phone.CentralSurfaces; +import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; +import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; +import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; +import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController; +import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.KeyguardStatusBarView; +import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController; +import com.android.systemui.statusbar.phone.LockscreenGestureLogger; +import com.android.systemui.statusbar.phone.ScreenOffAnimationController; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; +import com.android.systemui.statusbar.phone.TapAgainViewController; +import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; +import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; +import com.android.systemui.statusbar.window.StatusBarWindowStateController; +import com.android.systemui.unfold.SysUIUnfoldComponent; +import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.util.time.SystemClock; +import com.android.wm.shell.animation.FlingAnimationUtils; + +import org.junit.After; +import org.junit.Before; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; + +import java.util.List; +import java.util.Optional; + +import dagger.Lazy; +import kotlinx.coroutines.CoroutineDispatcher; + +public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { + + protected static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400; + protected static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50; + protected static final int PANEL_WIDTH = 500; // Random value just for the test. + + @Mock protected CentralSurfaces mCentralSurfaces; + @Mock protected NotificationStackScrollLayout mNotificationStackScrollLayout; + @Mock protected KeyguardBottomAreaView mKeyguardBottomArea; + @Mock protected KeyguardBottomAreaViewController mKeyguardBottomAreaViewController; + @Mock protected KeyguardBottomAreaView mQsFrame; + @Mock protected HeadsUpManagerPhone mHeadsUpManager; + @Mock protected NotificationShelfController mNotificationShelfController; + @Mock protected NotificationGutsManager mGutsManager; + @Mock protected KeyguardStatusBarView mKeyguardStatusBar; + @Mock protected KeyguardUserSwitcherView mUserSwitcherView; + @Mock protected ViewStub mUserSwitcherStubView; + @Mock protected HeadsUpTouchHelper.Callback mHeadsUpCallback; + @Mock protected KeyguardUpdateMonitor mUpdateMonitor; + @Mock protected KeyguardBypassController mKeyguardBypassController; + @Mock protected DozeParameters mDozeParameters; + @Mock protected ScreenOffAnimationController mScreenOffAnimationController; + @Mock protected NotificationPanelView mView; + @Mock protected LayoutInflater mLayoutInflater; + @Mock protected FeatureFlags mFeatureFlags; + @Mock protected DynamicPrivacyController mDynamicPrivacyController; + @Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; + @Mock protected KeyguardStateController mKeyguardStateController; + @Mock protected DozeLog mDozeLog; + @Mock protected ShadeLogger mShadeLog; + @Mock protected ShadeHeightLogger mShadeHeightLogger; + @Mock protected CommandQueue mCommandQueue; + @Mock protected VibratorHelper mVibratorHelper; + @Mock protected LatencyTracker mLatencyTracker; + @Mock protected PowerManager mPowerManager; + @Mock protected AccessibilityManager mAccessibilityManager; + @Mock protected MetricsLogger mMetricsLogger; + @Mock protected Resources mResources; + @Mock protected Configuration mConfiguration; + @Mock protected KeyguardClockSwitch mKeyguardClockSwitch; + @Mock protected MediaHierarchyManager mMediaHierarchyManager; + @Mock protected ConversationNotificationManager mConversationNotificationManager; + @Mock protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock protected KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; + @Mock protected KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory; + @Mock protected KeyguardQsUserSwitchComponent mKeyguardQsUserSwitchComponent; + @Mock protected KeyguardQsUserSwitchController mKeyguardQsUserSwitchController; + @Mock protected KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory; + @Mock protected KeyguardUserSwitcherComponent mKeyguardUserSwitcherComponent; + @Mock protected KeyguardUserSwitcherController mKeyguardUserSwitcherController; + @Mock protected KeyguardStatusViewComponent mKeyguardStatusViewComponent; + @Mock protected KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; + @Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent; + @Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController; + @Mock protected KeyguardStatusViewController mKeyguardStatusViewController; + @Mock protected KeyguardStatusBarViewController mKeyguardStatusBarViewController; + @Mock protected NotificationStackScrollLayoutController + mNotificationStackScrollLayoutController; + @Mock protected NotificationShadeDepthController mNotificationShadeDepthController; + @Mock protected LockscreenShadeTransitionController mLockscreenShadeTransitionController; + @Mock protected AuthController mAuthController; + @Mock protected ScrimController mScrimController; + @Mock protected MediaDataManager mMediaDataManager; + @Mock protected AmbientState mAmbientState; + @Mock protected UserManager mUserManager; + @Mock protected UiEventLogger mUiEventLogger; + @Mock protected LockIconViewController mLockIconViewController; + @Mock protected KeyguardMediaController mKeyguardMediaController; + @Mock protected NavigationModeController mNavigationModeController; + @Mock protected NavigationBarController mNavigationBarController; + @Mock protected QuickSettingsController mQsController; + @Mock protected ShadeHeaderController mShadeHeaderController; + @Mock protected ContentResolver mContentResolver; + @Mock protected TapAgainViewController mTapAgainViewController; + @Mock protected KeyguardIndicationController mKeyguardIndicationController; + @Mock protected FragmentService mFragmentService; + @Mock protected FragmentHostManager mFragmentHostManager; + @Mock protected NotificationRemoteInputManager mNotificationRemoteInputManager; + @Mock protected RecordingController mRecordingController; + @Mock protected LockscreenGestureLogger mLockscreenGestureLogger; + @Mock protected DumpManager mDumpManager; + @Mock protected InteractionJankMonitor mInteractionJankMonitor; + @Mock protected NotificationsQSContainerController mNotificationsQSContainerController; + @Mock protected QsFrameTranslateController mQsFrameTranslateController; + @Mock protected StatusBarWindowStateController mStatusBarWindowStateController; + @Mock protected KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + @Mock protected NotificationShadeWindowController mNotificationShadeWindowController; + @Mock protected SysUiState mSysUiState; + @Mock protected NotificationListContainer mNotificationListContainer; + @Mock protected NotificationStackSizeCalculator mNotificationStackSizeCalculator; + @Mock protected UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; + @Mock protected ShadeTransitionController mShadeTransitionController; + @Mock protected QS mQs; + @Mock protected QSFragment mQSFragment; + @Mock protected ViewGroup mQsHeader; + @Mock protected ViewParent mViewParent; + @Mock protected ViewTreeObserver mViewTreeObserver; + @Mock protected KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel; + @Mock protected DreamingToLockscreenTransitionViewModel + mDreamingToLockscreenTransitionViewModel; + @Mock protected OccludedToLockscreenTransitionViewModel + mOccludedToLockscreenTransitionViewModel; + @Mock protected LockscreenToDreamingTransitionViewModel + mLockscreenToDreamingTransitionViewModel; + @Mock protected LockscreenToOccludedTransitionViewModel + mLockscreenToOccludedTransitionViewModel; + @Mock protected GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel; + + @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor; + @Mock protected KeyguardLongPressViewModel mKeyuardLongPressViewModel; + @Mock protected AlternateBouncerInteractor mAlternateBouncerInteractor; + @Mock protected MotionEvent mDownMotionEvent; + @Mock protected CoroutineDispatcher mMainDispatcher; + @Captor + protected ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener> + mEmptySpaceClickListenerCaptor; + + protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor; + protected KeyguardInteractor mKeyguardInteractor; + protected NotificationPanelViewController.TouchHandler mTouchHandler; + protected ConfigurationController mConfigurationController; + protected SysuiStatusBarStateController mStatusBarStateController; + protected NotificationPanelViewController mNotificationPanelViewController; + protected View.AccessibilityDelegate mAccessibilityDelegate; + protected NotificationsQuickSettingsContainer mNotificationContainerParent; + protected List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners; + protected Handler mMainHandler; + protected View.OnLayoutChangeListener mLayoutChangeListener; + + protected final FalsingManagerFake mFalsingManager = new FalsingManagerFake(); + protected final Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty(); + protected final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + protected final ShadeExpansionStateManager mShadeExpansionStateManager = + new ShadeExpansionStateManager(); + + protected QuickSettingsController mQuickSettingsController; + @Mock protected Lazy<NotificationPanelViewController> mNotificationPanelViewControllerLazy; + + protected FragmentHostManager.FragmentListener mFragmentListener; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mMainDispatcher = getMainDispatcher(); + mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor( + new FakeKeyguardRepository()); + mKeyguardInteractor = new KeyguardInteractor(new FakeKeyguardRepository(), mCommandQueue, + mFeatureFlags, new FakeKeyguardBouncerRepository()); + SystemClock systemClock = new FakeSystemClock(); + mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager, + mInteractionJankMonitor, mShadeExpansionStateManager); + + KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext); + keyguardStatusView.setId(R.id.keyguard_status_view); + + when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false); + when(mHeadsUpCallback.getContext()).thenReturn(mContext); + when(mView.getResources()).thenReturn(mResources); + when(mView.getWidth()).thenReturn(PANEL_WIDTH); + when(mResources.getConfiguration()).thenReturn(mConfiguration); + mConfiguration.orientation = ORIENTATION_PORTRAIT; + when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); + mDisplayMetrics.density = 100; + when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true); + when(mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade)) + .thenReturn(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE); + when(mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal)) + .thenReturn(10); + when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance)) + .thenReturn(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); + when(mView.getContext()).thenReturn(getContext()); + when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar); + when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView); + when(mView.findViewById(R.id.keyguard_user_switcher_stub)).thenReturn( + mUserSwitcherStubView); + when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch); + when(mView.findViewById(R.id.notification_stack_scroller)) + .thenReturn(mNotificationStackScrollLayout); + when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000); + when(mNotificationStackScrollLayoutController.getHeadsUpCallback()) + .thenReturn(mHeadsUpCallback); + when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea); + when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); + when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class)); + when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); + when(mView.findViewById(R.id.keyguard_status_view)) + .thenReturn(mock(KeyguardStatusView.class)); + mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null); + mNotificationContainerParent.addView(keyguardStatusView); + mNotificationContainerParent.onFinishInflate(); + when(mView.findViewById(R.id.notification_container_parent)) + .thenReturn(mNotificationContainerParent); + when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager); + FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder( + mDisplayMetrics); + when(mKeyguardQsUserSwitchComponentFactory.build(any())) + .thenReturn(mKeyguardQsUserSwitchComponent); + when(mKeyguardQsUserSwitchComponent.getKeyguardQsUserSwitchController()) + .thenReturn(mKeyguardQsUserSwitchController); + when(mKeyguardUserSwitcherComponentFactory.build(any())) + .thenReturn(mKeyguardUserSwitcherComponent); + when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController()) + .thenReturn(mKeyguardUserSwitcherController); + when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true); + when(mQs.getView()).thenReturn(mView); + when(mQSFragment.getView()).thenReturn(mView); + doAnswer(invocation -> { + mFragmentListener = invocation.getArgument(1); + return null; + }).when(mFragmentHostManager).addTagListener(eq(QS.TAG), any()); + doAnswer((Answer<Void>) invocation -> { + mTouchHandler = invocation.getArgument(0); + return null; + }).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class)); + + // Dreaming->Lockscreen + when(mKeyguardTransitionInteractor.getDreamingToLockscreenTransition()) + .thenReturn(emptyFlow()); + when(mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha()) + .thenReturn(emptyFlow()); + when(mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY(anyInt())) + .thenReturn(emptyFlow()); + + // Occluded->Lockscreen + when(mKeyguardTransitionInteractor.getOccludedToLockscreenTransition()) + .thenReturn(emptyFlow()); + when(mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha()) + .thenReturn(emptyFlow()); + when(mOccludedToLockscreenTransitionViewModel.lockscreenTranslationY(anyInt())) + .thenReturn(emptyFlow()); + + // Lockscreen->Dreaming + when(mKeyguardTransitionInteractor.getLockscreenToDreamingTransition()) + .thenReturn(emptyFlow()); + when(mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha()) + .thenReturn(emptyFlow()); + when(mLockscreenToDreamingTransitionViewModel.lockscreenTranslationY(anyInt())) + .thenReturn(emptyFlow()); + + // Gone->Dreaming + when(mKeyguardTransitionInteractor.getGoneToDreamingTransition()) + .thenReturn(emptyFlow()); + when(mGoneToDreamingTransitionViewModel.getLockscreenAlpha()) + .thenReturn(emptyFlow()); + when(mGoneToDreamingTransitionViewModel.lockscreenTranslationY(anyInt())) + .thenReturn(emptyFlow()); + + // Lockscreen->Occluded + when(mKeyguardTransitionInteractor.getLockscreenToOccludedTransition()) + .thenReturn(emptyFlow()); + when(mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha()) + .thenReturn(emptyFlow()); + when(mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY(anyInt())) + .thenReturn(emptyFlow()); + + NotificationWakeUpCoordinator coordinator = + new NotificationWakeUpCoordinator( + mDumpManager, + mock(HeadsUpManagerPhone.class), + new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager, + mInteractionJankMonitor, mShadeExpansionStateManager), + mKeyguardBypassController, + mDozeParameters, + mScreenOffAnimationController, + mock(NotificationWakeUpCoordinatorLogger.class)); + mConfigurationController = new ConfigurationControllerImpl(mContext); + PulseExpansionHandler expansionHandler = new PulseExpansionHandler( + mContext, + coordinator, + mKeyguardBypassController, mHeadsUpManager, + mock(NotificationRoundnessManager.class), + mConfigurationController, + mStatusBarStateController, + mFalsingManager, + mShadeExpansionStateManager, + mLockscreenShadeTransitionController, + new FalsingCollectorFake(), + mDumpManager); + when(mKeyguardStatusViewComponentFactory.build(any())) + .thenReturn(mKeyguardStatusViewComponent); + when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) + .thenReturn(mKeyguardClockSwitchController); + when(mKeyguardStatusViewComponent.getKeyguardStatusViewController()) + .thenReturn(mKeyguardStatusViewController); + when(mKeyguardStatusBarViewComponentFactory.build(any(), any())) + .thenReturn(mKeyguardStatusBarViewComponent); + when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController()) + .thenReturn(mKeyguardStatusBarViewController); + when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean())) + .thenReturn(keyguardStatusView); + when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean())) + .thenReturn(mUserSwitcherView); + when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean())) + .thenReturn(mKeyguardBottomArea); + when(mNotificationRemoteInputManager.isRemoteInputActive()) + .thenReturn(false); + when(mInteractionJankMonitor.begin(any(), anyInt())) + .thenReturn(true); + when(mInteractionJankMonitor.end(anyInt())) + .thenReturn(true); + doAnswer(invocation -> { + ((Runnable) invocation.getArgument(0)).run(); + return null; + }).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any()); + doAnswer(invocation -> { + mLayoutChangeListener = invocation.getArgument(0); + return null; + }).when(mView).addOnLayoutChangeListener(any()); + + when(mView.getViewTreeObserver()).thenReturn(mViewTreeObserver); + when(mView.getParent()).thenReturn(mViewParent); + when(mQs.getHeader()).thenReturn(mQsHeader); + when(mDownMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN); + when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState); + + mMainHandler = new Handler(Looper.getMainLooper()); + + when(mView.requireViewById(R.id.keyguard_long_press)) + .thenReturn(mock(LongPressHandlingView.class)); + + mNotificationPanelViewController = new NotificationPanelViewController( + mView, + mMainHandler, + mLayoutInflater, + mFeatureFlags, + coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, + mFalsingManager, new FalsingCollectorFake(), + mKeyguardStateController, + mStatusBarStateController, + mStatusBarWindowStateController, + mNotificationShadeWindowController, + mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper, + mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, + mMetricsLogger, + mShadeLog, + mShadeHeightLogger, + mConfigurationController, + () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, + mConversationNotificationManager, mMediaHierarchyManager, + mStatusBarKeyguardViewManager, + mGutsManager, + mNotificationsQSContainerController, + mNotificationStackScrollLayoutController, + mKeyguardStatusViewComponentFactory, + mKeyguardQsUserSwitchComponentFactory, + mKeyguardUserSwitcherComponentFactory, + mKeyguardStatusBarViewComponentFactory, + mLockscreenShadeTransitionController, + mAuthController, + mScrimController, + mUserManager, + mMediaDataManager, + mNotificationShadeDepthController, + mAmbientState, + mLockIconViewController, + mKeyguardMediaController, + mTapAgainViewController, + mNavigationModeController, + mNavigationBarController, + mQsController, + mFragmentService, + mContentResolver, + mRecordingController, + mShadeHeaderController, + mScreenOffAnimationController, + mLockscreenGestureLogger, + mShadeExpansionStateManager, + mNotificationRemoteInputManager, + mSysUIUnfoldComponent, + mSysUiState, + () -> mKeyguardBottomAreaViewController, + mKeyguardUnlockAnimationController, + mKeyguardIndicationController, + mNotificationListContainer, + mNotificationStackSizeCalculator, + mUnlockedScreenOffAnimationController, + mShadeTransitionController, + mInteractionJankMonitor, + systemClock, + mKeyguardBottomAreaViewModel, + mKeyguardBottomAreaInteractor, + mAlternateBouncerInteractor, + mDreamingToLockscreenTransitionViewModel, + mOccludedToLockscreenTransitionViewModel, + mLockscreenToDreamingTransitionViewModel, + mGoneToDreamingTransitionViewModel, + mLockscreenToOccludedTransitionViewModel, + mMainDispatcher, + mKeyguardTransitionInteractor, + mDumpManager, + mKeyuardLongPressViewModel, + mKeyguardInteractor); + mNotificationPanelViewController.initDependencies( + mCentralSurfaces, + null, + () -> {}, + mNotificationShelfController); + mNotificationPanelViewController.setTrackingStartedListener(() -> {}); + mNotificationPanelViewController.setOpenCloseListener( + new NotificationPanelViewController.OpenCloseListener() { + @Override + public void onClosingFinished() {} + + @Override + public void onOpenStarted() {} + }); + mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); + ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); + verify(mView, atLeast(1)).addOnAttachStateChangeListener( + onAttachStateChangeListenerArgumentCaptor.capture()); + mOnAttachStateChangeListeners = onAttachStateChangeListenerArgumentCaptor.getAllValues(); + + ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor = + ArgumentCaptor.forClass(View.AccessibilityDelegate.class); + verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture()); + mAccessibilityDelegate = accessibilityDelegateArgumentCaptor.getValue(); + mNotificationPanelViewController.getStatusBarStateController() + .addCallback(mNotificationPanelViewController.getStatusBarStateListener()); + mNotificationPanelViewController + .setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class)); + verify(mNotificationStackScrollLayoutController) + .setOnEmptySpaceClickListener(mEmptySpaceClickListenerCaptor.capture()); + verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true); + reset(mKeyguardStatusViewController); + + when(mNotificationPanelViewControllerLazy.get()) + .thenReturn(mNotificationPanelViewController); + mQuickSettingsController = new QuickSettingsController( + mNotificationPanelViewControllerLazy, + mView, + mQsFrameTranslateController, + mShadeTransitionController, + expansionHandler, + mNotificationRemoteInputManager, + mShadeExpansionStateManager, + mStatusBarKeyguardViewManager, + mNotificationStackScrollLayoutController, + mLockscreenShadeTransitionController, + mNotificationShadeDepthController, + mShadeHeaderController, + mStatusBarTouchableRegionManager, + mKeyguardStateController, + mKeyguardBypassController, + mUpdateMonitor, + mScrimController, + mMediaDataManager, + mMediaHierarchyManager, + mAmbientState, + mRecordingController, + mFalsingManager, + new FalsingCollectorFake(), + mAccessibilityManager, + mLockscreenGestureLogger, + mMetricsLogger, + mFeatureFlags, + mInteractionJankMonitor, + mShadeLog + ); + } + + @After + public void tearDown() { + mNotificationPanelViewController.mBottomAreaShadeAlphaAnimator.cancel(); + mNotificationPanelViewController.cancelHeightAnimator(); + mMainHandler.removeCallbacksAndMessages(null); + } + + protected void setBottomPadding(int stackBottom, int lockIconPadding, int indicationPadding, + int ambientPadding) { + + when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0); + when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom); + when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom); + when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding)); + + when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding)) + .thenReturn(indicationPadding); + mNotificationPanelViewController.loadDimens(); + + mNotificationPanelViewController.setAmbientIndicationTop( + /* ambientIndicationTop= */ stackBottom - ambientPadding, + /* ambientTextVisible= */ true); + } + + protected void triggerPositionClockAndNotifications() { + mNotificationPanelViewController.onQsSetExpansionHeightCalled(false); + } + + protected FalsingManager.FalsingTapListener getFalsingTapListener() { + for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { + listener.onViewAttachedToWindow(mView); + } + assertThat(mFalsingManager.getTapListeners().size()).isEqualTo(1); + return mFalsingManager.getTapListeners().get(0); + } + + protected void givenViewAttached() { + for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { + listener.onViewAttachedToWindow(mView); + } + } + + protected ConstraintSet.Layout getConstraintSetLayout(@IdRes int id) { + ConstraintSet constraintSet = new ConstraintSet(); + constraintSet.clone(mNotificationContainerParent); + return constraintSet.getConstraint(id).layout; + } + + protected void enableSplitShade(boolean enabled) { + when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled); + mNotificationPanelViewController.updateResources(); + } + + protected void updateMultiUserSetting(boolean enabled) { + when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false); + when(mUserManager.isUserSwitcherEnabled(false)).thenReturn(enabled); + final ArgumentCaptor<ContentObserver> observerCaptor = + ArgumentCaptor.forClass(ContentObserver.class); + verify(mContentResolver) + .registerContentObserver(any(), anyBoolean(), observerCaptor.capture()); + observerCaptor.getValue().onChange(/* selfChange */ false); + } + + protected void updateSmallestScreenWidth(int smallestScreenWidthDp) { + Configuration configuration = new Configuration(); + configuration.smallestScreenWidthDp = smallestScreenWidthDp; + mConfigurationController.onConfigurationChanged(configuration); + } + + protected void onTouchEvent(MotionEvent ev) { + mTouchHandler.onTouch(mView, ev); + } + + protected void setDozing(boolean dozing, boolean dozingAlwaysOn) { + when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn); + mNotificationPanelViewController.setDozing( + /* dozing= */ dozing, + /* animate= */ false + ); + } + + protected void assertKeyguardStatusViewCentered() { + mNotificationPanelViewController.updateResources(); + assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isAnyOf( + ConstraintSet.PARENT_ID, ConstraintSet.UNSET); + } + + protected void assertKeyguardStatusViewNotCentered() { + mNotificationPanelViewController.updateResources(); + assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isEqualTo( + R.id.qs_edge_guideline); + } + + protected void setIsFullWidth(boolean fullWidth) { + float nsslWidth = fullWidth ? PANEL_WIDTH : PANEL_WIDTH / 2f; + when(mNotificationStackScrollLayoutController.getWidth()).thenReturn(nsslWidth); + triggerLayoutChange(); + } + + protected void triggerLayoutChange() { + mLayoutChangeListener.onLayoutChange( + mView, + /* left= */ 0, + /* top= */ 0, + /* right= */ 0, + /* bottom= */ 0, + /* oldLeft= */ 0, + /* oldTop= */ 0, + /* oldRight= */ 0, + /* oldBottom= */ 0 + ); + } + + protected CoroutineDispatcher getMainDispatcher() { + return mMainDispatcher; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 8f6653f1d601..d86ff671a222 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -16,8 +16,6 @@ package com.android.systemui.shade; -import static android.content.res.Configuration.ORIENTATION_PORTRAIT; - import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED; import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; @@ -31,592 +29,72 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.annotation.IdRes; -import android.content.ContentResolver; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.os.Handler; -import android.os.Looper; -import android.os.PowerManager; -import android.os.UserManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.util.DisplayMetrics; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.ViewPropertyAnimator; -import android.view.ViewStub; -import android.view.ViewTreeObserver; -import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import androidx.constraintlayout.widget.ConstraintSet; import androidx.test.filters.SmallTest; -import com.android.internal.jank.InteractionJankMonitor; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.testing.UiEventLoggerFake; -import com.android.internal.util.CollectionUtils; -import com.android.internal.util.LatencyTracker; import com.android.keyguard.FaceAuthApiRequestReason; -import com.android.keyguard.KeyguardClockSwitch; -import com.android.keyguard.KeyguardClockSwitchController; -import com.android.keyguard.KeyguardStatusView; -import com.android.keyguard.KeyguardStatusViewController; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.LockIconViewController; -import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; -import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; -import com.android.keyguard.dagger.KeyguardStatusViewComponent; -import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.systemui.DejankUtils; import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.biometrics.AuthController; -import com.android.systemui.classifier.FalsingCollectorFake; -import com.android.systemui.classifier.FalsingManagerFake; -import com.android.systemui.common.ui.view.LongPressHandlingView; -import com.android.systemui.doze.DozeLog; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.fragments.FragmentHostManager; -import com.android.systemui.fragments.FragmentService; -import com.android.systemui.keyguard.KeyguardUnlockAnimationController; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; -import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel; -import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel; -import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel; -import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel; -import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel; -import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel; -import com.android.systemui.media.controls.pipeline.MediaDataManager; -import com.android.systemui.media.controls.ui.KeyguardMediaController; -import com.android.systemui.media.controls.ui.MediaHierarchyManager; -import com.android.systemui.model.SysUiState; -import com.android.systemui.navigationbar.NavigationBarController; -import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.qs.QSFragment; -import com.android.systemui.screenrecord.RecordingController; -import com.android.systemui.shade.transition.ShadeTransitionController; -import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.KeyguardIndicationController; -import com.android.systemui.statusbar.LockscreenShadeTransitionController; -import com.android.systemui.statusbar.NotificationRemoteInputManager; -import com.android.systemui.statusbar.NotificationShadeDepthController; -import com.android.systemui.statusbar.NotificationShadeWindowController; -import com.android.systemui.statusbar.NotificationShelfController; -import com.android.systemui.statusbar.PulseExpansionHandler; -import com.android.systemui.statusbar.QsFrameTranslateController; -import com.android.systemui.statusbar.StatusBarStateControllerImpl; -import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.notification.ConversationNotificationManager; -import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.AmbientState; -import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator; -import com.android.systemui.statusbar.phone.CentralSurfaces; -import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; -import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; -import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; -import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; -import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController; -import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.KeyguardStatusBarView; -import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController; -import com.android.systemui.statusbar.phone.LockscreenGestureLogger; -import com.android.systemui.statusbar.phone.ScreenOffAnimationController; -import com.android.systemui.statusbar.phone.ScrimController; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; -import com.android.systemui.statusbar.phone.TapAgainViewController; -import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; -import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; -import com.android.systemui.statusbar.window.StatusBarWindowStateController; -import com.android.systemui.unfold.SysUIUnfoldComponent; -import com.android.systemui.util.time.FakeSystemClock; -import com.android.systemui.util.time.SystemClock; -import com.android.wm.shell.animation.FlingAnimationUtils; - -import org.junit.After; + import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.Captor; import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; import java.util.List; -import java.util.Optional; - -import dagger.Lazy; -import kotlinx.coroutines.CoroutineDispatcher; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) -public class NotificationPanelViewControllerTest extends SysuiTestCase { - - private static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400; - private static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50; - private static final int PANEL_WIDTH = 500; // Random value just for the test. - - @Mock private CentralSurfaces mCentralSurfaces; - @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; - @Mock private KeyguardBottomAreaView mKeyguardBottomArea; - @Mock private KeyguardBottomAreaViewController mKeyguardBottomAreaViewController; - @Mock private KeyguardBottomAreaView mQsFrame; - @Mock private HeadsUpManagerPhone mHeadsUpManager; - @Mock private NotificationShelfController mNotificationShelfController; - @Mock private NotificationGutsManager mGutsManager; - @Mock private KeyguardStatusBarView mKeyguardStatusBar; - @Mock private KeyguardUserSwitcherView mUserSwitcherView; - @Mock private ViewStub mUserSwitcherStubView; - @Mock private HeadsUpTouchHelper.Callback mHeadsUpCallback; - @Mock private KeyguardUpdateMonitor mUpdateMonitor; - @Mock private KeyguardBypassController mKeyguardBypassController; - @Mock private DozeParameters mDozeParameters; - @Mock private ScreenOffAnimationController mScreenOffAnimationController; - @Mock private NotificationPanelView mView; - @Mock private LayoutInflater mLayoutInflater; - @Mock private FeatureFlags mFeatureFlags; - @Mock private DynamicPrivacyController mDynamicPrivacyController; - @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; - @Mock private KeyguardStateController mKeyguardStateController; - @Mock private DozeLog mDozeLog; - @Mock private ShadeLogger mShadeLog; - @Mock private ShadeHeightLogger mShadeHeightLogger; - @Mock private CommandQueue mCommandQueue; - @Mock private VibratorHelper mVibratorHelper; - @Mock private LatencyTracker mLatencyTracker; - @Mock private PowerManager mPowerManager; - @Mock private AccessibilityManager mAccessibilityManager; - @Mock private MetricsLogger mMetricsLogger; - @Mock private Resources mResources; - @Mock private Configuration mConfiguration; - @Mock private KeyguardClockSwitch mKeyguardClockSwitch; - @Mock private MediaHierarchyManager mMediaHierarchyManager; - @Mock private ConversationNotificationManager mConversationNotificationManager; - @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - @Mock private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; - @Mock private KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory; - @Mock private KeyguardQsUserSwitchComponent mKeyguardQsUserSwitchComponent; - @Mock private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController; - @Mock private KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory; - @Mock private KeyguardUserSwitcherComponent mKeyguardUserSwitcherComponent; - @Mock private KeyguardUserSwitcherController mKeyguardUserSwitcherController; - @Mock private KeyguardStatusViewComponent mKeyguardStatusViewComponent; - @Mock private KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; - @Mock private KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent; - @Mock private KeyguardClockSwitchController mKeyguardClockSwitchController; - @Mock private KeyguardStatusViewController mKeyguardStatusViewController; - @Mock private KeyguardStatusBarViewController mKeyguardStatusBarViewController; - @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; - @Mock private NotificationShadeDepthController mNotificationShadeDepthController; - @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController; - @Mock private AuthController mAuthController; - @Mock private ScrimController mScrimController; - @Mock private MediaDataManager mMediaDataManager; - @Mock private AmbientState mAmbientState; - @Mock private UserManager mUserManager; - @Mock private UiEventLogger mUiEventLogger; - @Mock private LockIconViewController mLockIconViewController; - @Mock private KeyguardMediaController mKeyguardMediaController; - @Mock private NavigationModeController mNavigationModeController; - @Mock private NavigationBarController mNavigationBarController; - @Mock private QuickSettingsController mQsController; - @Mock private ShadeHeaderController mShadeHeaderController; - @Mock private ContentResolver mContentResolver; - @Mock private TapAgainViewController mTapAgainViewController; - @Mock private KeyguardIndicationController mKeyguardIndicationController; - @Mock private FragmentService mFragmentService; - @Mock private FragmentHostManager mFragmentHostManager; - @Mock private NotificationRemoteInputManager mNotificationRemoteInputManager; - @Mock private RecordingController mRecordingController; - @Mock private LockscreenGestureLogger mLockscreenGestureLogger; - @Mock private DumpManager mDumpManager; - @Mock private InteractionJankMonitor mInteractionJankMonitor; - @Mock private NotificationsQSContainerController mNotificationsQSContainerController; - @Mock private QsFrameTranslateController mQsFrameTranslateController; - @Mock private StatusBarWindowStateController mStatusBarWindowStateController; - @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; - @Mock private NotificationShadeWindowController mNotificationShadeWindowController; - @Mock private SysUiState mSysUiState; - @Mock private NotificationListContainer mNotificationListContainer; - @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator; - @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; - @Mock private ShadeTransitionController mShadeTransitionController; - @Mock private QS mQs; - @Mock private QSFragment mQSFragment; - @Mock private ViewGroup mQsHeader; - @Mock private ViewParent mViewParent; - @Mock private ViewTreeObserver mViewTreeObserver; - @Mock private KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel; - @Mock private KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor; - @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor; - @Mock private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel; - @Mock private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel; - @Mock private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel; - @Mock private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel; - @Mock private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel; - - @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; - @Mock private KeyguardInteractor mKeyguardInteractor; - @Mock private KeyguardLongPressViewModel mKeyuardLongPressViewModel; - @Mock private CoroutineDispatcher mMainDispatcher; - @Mock private MotionEvent mDownMotionEvent; - @Captor - private ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener> - mEmptySpaceClickListenerCaptor; - - private NotificationPanelViewController.TouchHandler mTouchHandler; - private ConfigurationController mConfigurationController; - private SysuiStatusBarStateController mStatusBarStateController; - private NotificationPanelViewController mNotificationPanelViewController; - private View.AccessibilityDelegate mAccessibilityDelegate; - private NotificationsQuickSettingsContainer mNotificationContainerParent; - private List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners; - private Handler mMainHandler; - private View.OnLayoutChangeListener mLayoutChangeListener; - - private final FalsingManagerFake mFalsingManager = new FalsingManagerFake(); - private final Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty(); - private final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); - private final ShadeExpansionStateManager mShadeExpansionStateManager = - new ShadeExpansionStateManager(); - - private QuickSettingsController mQuickSettingsController; - @Mock private Lazy<NotificationPanelViewController> mNotificationPanelViewControllerLazy; - - private FragmentHostManager.FragmentListener mFragmentListener; +public class NotificationPanelViewControllerTest extends NotificationPanelViewControllerBaseTest { @Before - public void setup() { - MockitoAnnotations.initMocks(this); - SystemClock systemClock = new FakeSystemClock(); - mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager, - mInteractionJankMonitor, mShadeExpansionStateManager); - - KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext); - keyguardStatusView.setId(R.id.keyguard_status_view); + public void before() { DejankUtils.setImmediate(true); + } - when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false); - when(mHeadsUpCallback.getContext()).thenReturn(mContext); - when(mView.getResources()).thenReturn(mResources); - when(mView.getWidth()).thenReturn(PANEL_WIDTH); - when(mResources.getConfiguration()).thenReturn(mConfiguration); - mConfiguration.orientation = ORIENTATION_PORTRAIT; - when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); - mDisplayMetrics.density = 100; - when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true); - when(mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade)) - .thenReturn(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE); - when(mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal)) - .thenReturn(10); - when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance)) - .thenReturn(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); - when(mView.getContext()).thenReturn(getContext()); - when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar); - when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView); - when(mView.findViewById(R.id.keyguard_user_switcher_stub)).thenReturn( - mUserSwitcherStubView); - when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch); - when(mView.findViewById(R.id.notification_stack_scroller)) - .thenReturn(mNotificationStackScrollLayout); - when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000); - when(mNotificationStackScrollLayoutController.getHeadsUpCallback()) - .thenReturn(mHeadsUpCallback); - when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea); - when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); - when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class)); - when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); - when(mView.findViewById(R.id.keyguard_status_view)) - .thenReturn(mock(KeyguardStatusView.class)); - mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null); - mNotificationContainerParent.addView(keyguardStatusView); - mNotificationContainerParent.onFinishInflate(); - when(mView.findViewById(R.id.notification_container_parent)) - .thenReturn(mNotificationContainerParent); - when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager); - FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder( - mDisplayMetrics); - when(mKeyguardQsUserSwitchComponentFactory.build(any())) - .thenReturn(mKeyguardQsUserSwitchComponent); - when(mKeyguardQsUserSwitchComponent.getKeyguardQsUserSwitchController()) - .thenReturn(mKeyguardQsUserSwitchController); - when(mKeyguardUserSwitcherComponentFactory.build(any())) - .thenReturn(mKeyguardUserSwitcherComponent); - when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController()) - .thenReturn(mKeyguardUserSwitcherController); - when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true); - when(mQs.getView()).thenReturn(mView); - when(mQSFragment.getView()).thenReturn(mView); - doAnswer(invocation -> { - mFragmentListener = invocation.getArgument(1); - return null; - }).when(mFragmentHostManager).addTagListener(eq(QS.TAG), any()); - doAnswer((Answer<Void>) invocation -> { - mTouchHandler = invocation.getArgument(0); - return null; - }).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class)); - - NotificationWakeUpCoordinator coordinator = - new NotificationWakeUpCoordinator( - mDumpManager, - mock(HeadsUpManagerPhone.class), - new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager, - mInteractionJankMonitor, mShadeExpansionStateManager), - mKeyguardBypassController, - mDozeParameters, - mScreenOffAnimationController, - mock(NotificationWakeUpCoordinatorLogger.class)); - mConfigurationController = new ConfigurationControllerImpl(mContext); - PulseExpansionHandler expansionHandler = new PulseExpansionHandler( - mContext, - coordinator, - mKeyguardBypassController, mHeadsUpManager, - mock(NotificationRoundnessManager.class), - mConfigurationController, - mStatusBarStateController, - mFalsingManager, - mShadeExpansionStateManager, - mLockscreenShadeTransitionController, - new FalsingCollectorFake(), - mDumpManager); - when(mKeyguardStatusViewComponentFactory.build(any())) - .thenReturn(mKeyguardStatusViewComponent); - when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) - .thenReturn(mKeyguardClockSwitchController); - when(mKeyguardStatusViewComponent.getKeyguardStatusViewController()) - .thenReturn(mKeyguardStatusViewController); - when(mKeyguardStatusBarViewComponentFactory.build(any(), any())) - .thenReturn(mKeyguardStatusBarViewComponent); - when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController()) - .thenReturn(mKeyguardStatusBarViewController); - when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean())) - .thenReturn(keyguardStatusView); - when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean())) - .thenReturn(mUserSwitcherView); - when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean())) - .thenReturn(mKeyguardBottomArea); - when(mNotificationRemoteInputManager.isRemoteInputActive()) - .thenReturn(false); - when(mInteractionJankMonitor.begin(any(), anyInt())) - .thenReturn(true); - when(mInteractionJankMonitor.end(anyInt())) - .thenReturn(true); - doAnswer(invocation -> { - ((Runnable) invocation.getArgument(0)).run(); - return null; - }).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any()); - doAnswer(invocation -> { - mLayoutChangeListener = invocation.getArgument(0); - return null; - }).when(mView).addOnLayoutChangeListener(any()); - - when(mView.getViewTreeObserver()).thenReturn(mViewTreeObserver); - when(mView.getParent()).thenReturn(mViewParent); - when(mQs.getHeader()).thenReturn(mQsHeader); - when(mDownMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN); - when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState); - - mMainHandler = new Handler(Looper.getMainLooper()); - - when(mView.requireViewById(R.id.keyguard_long_press)) - .thenReturn(mock(LongPressHandlingView.class)); - - mNotificationPanelViewController = new NotificationPanelViewController( - mView, - mMainHandler, - mLayoutInflater, - mFeatureFlags, - coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, - mFalsingManager, new FalsingCollectorFake(), - mKeyguardStateController, - mStatusBarStateController, - mStatusBarWindowStateController, - mNotificationShadeWindowController, - mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper, - mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, - mMetricsLogger, - mShadeLog, - mShadeHeightLogger, - mConfigurationController, - () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, - mConversationNotificationManager, mMediaHierarchyManager, - mStatusBarKeyguardViewManager, - mGutsManager, - mNotificationsQSContainerController, - mNotificationStackScrollLayoutController, - mKeyguardStatusViewComponentFactory, - mKeyguardQsUserSwitchComponentFactory, - mKeyguardUserSwitcherComponentFactory, - mKeyguardStatusBarViewComponentFactory, - mLockscreenShadeTransitionController, - mAuthController, - mScrimController, - mUserManager, - mMediaDataManager, - mNotificationShadeDepthController, - mAmbientState, - mLockIconViewController, - mKeyguardMediaController, - mTapAgainViewController, - mNavigationModeController, - mNavigationBarController, - mQsController, - mFragmentService, - mContentResolver, - mRecordingController, - mShadeHeaderController, - mScreenOffAnimationController, - mLockscreenGestureLogger, - mShadeExpansionStateManager, - mNotificationRemoteInputManager, - mSysUIUnfoldComponent, - mSysUiState, - () -> mKeyguardBottomAreaViewController, - mKeyguardUnlockAnimationController, - mKeyguardIndicationController, - mNotificationListContainer, - mNotificationStackSizeCalculator, - mUnlockedScreenOffAnimationController, - mShadeTransitionController, - systemClock, - mKeyguardBottomAreaViewModel, - mKeyguardBottomAreaInteractor, - mAlternateBouncerInteractor, - mDreamingToLockscreenTransitionViewModel, - mOccludedToLockscreenTransitionViewModel, - mLockscreenToDreamingTransitionViewModel, - mGoneToDreamingTransitionViewModel, - mLockscreenToOccludedTransitionViewModel, - mMainDispatcher, - mKeyguardTransitionInteractor, - mDumpManager, - mKeyuardLongPressViewModel, - mKeyguardInteractor); - mNotificationPanelViewController.initDependencies( - mCentralSurfaces, - null, - () -> {}, - mNotificationShelfController); - mNotificationPanelViewController.setTrackingStartedListener(() -> {}); - mNotificationPanelViewController.setOpenCloseListener( - new NotificationPanelViewController.OpenCloseListener() { - @Override - public void onClosingFinished() {} - - @Override - public void onOpenStarted() {} - }); - mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); - ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor = - ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); - verify(mView, atLeast(1)).addOnAttachStateChangeListener( - onAttachStateChangeListenerArgumentCaptor.capture()); - mOnAttachStateChangeListeners = onAttachStateChangeListenerArgumentCaptor.getAllValues(); - - ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor = - ArgumentCaptor.forClass(View.AccessibilityDelegate.class); - verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture()); - mAccessibilityDelegate = accessibilityDelegateArgumentCaptor.getValue(); - mNotificationPanelViewController.getStatusBarStateController() - .addCallback(mNotificationPanelViewController.getStatusBarStateListener()); - mNotificationPanelViewController - .setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class)); - verify(mNotificationStackScrollLayoutController) - .setOnEmptySpaceClickListener(mEmptySpaceClickListenerCaptor.capture()); - verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true); - reset(mKeyguardStatusViewController); - - when(mNotificationPanelViewControllerLazy.get()) - .thenReturn(mNotificationPanelViewController); - mQuickSettingsController = new QuickSettingsController( - mNotificationPanelViewControllerLazy, - mView, - mQsFrameTranslateController, - mShadeTransitionController, - expansionHandler, - mNotificationRemoteInputManager, - mShadeExpansionStateManager, - mStatusBarKeyguardViewManager, - mNotificationStackScrollLayoutController, - mLockscreenShadeTransitionController, - mNotificationShadeDepthController, - mShadeHeaderController, - mStatusBarTouchableRegionManager, - mKeyguardStateController, - mKeyguardBypassController, - mUpdateMonitor, - mScrimController, - mMediaDataManager, - mMediaHierarchyManager, - mAmbientState, - mRecordingController, - mFalsingManager, - new FalsingCollectorFake(), - mAccessibilityManager, - mLockscreenGestureLogger, - mMetricsLogger, - mFeatureFlags, - mInteractionJankMonitor, - mShadeLog - ); + /** + * When the Back gesture starts (progress 0%), the scrim will stay at 100% scale (1.0f). + */ + @Test + public void testBackGesture_min_scrimAtMaxScale() { + mNotificationPanelViewController.onBackProgressed(0.0f); + verify(mScrimController).applyBackScaling(1.0f); } - @After - public void tearDown() { - mNotificationPanelViewController.cancelHeightAnimator(); - mMainHandler.removeCallbacksAndMessages(null); + /** + * When the Back gesture is at max (progress 100%), the scrim will be scaled to its minimum. + */ + @Test + public void testBackGesture_max_scrimAtMinScale() { + mNotificationPanelViewController.onBackProgressed(1.0f); + verify(mScrimController).applyBackScaling( + NotificationPanelViewController.SHADE_BACK_ANIM_MIN_SCALE); } @Test @@ -671,23 +149,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { .isNotEqualTo(-1); } - private void setBottomPadding(int stackBottom, int lockIconPadding, int indicationPadding, - int ambientPadding) { - - when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0); - when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom); - when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom); - when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding)); - - when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding)) - .thenReturn(indicationPadding); - mNotificationPanelViewController.loadDimens(); - - mNotificationPanelViewController.setAmbientIndicationTop( - /* ambientIndicationTop= */ stackBottom - ambientPadding, - /* ambientTextVisible= */ true); - } - @Test @Ignore("b/261472011 - Test appears inconsistent across environments") public void getVerticalSpaceForLockscreenNotifications_useLockIconBottomPadding_returnsSpaceAvailable() { @@ -992,68 +453,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test - public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() { - givenViewAttached(); - when(mResources.getBoolean( - com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true); - updateMultiUserSetting(true); - clearInvocations(mView); - - updateMultiUserSetting(false); - - ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class); - verify(mView, atLeastOnce()).addView(captor.capture(), anyInt()); - final View userSwitcherStub = CollectionUtils.find(captor.getAllValues(), - view -> view.getId() == R.id.keyguard_user_switcher_stub); - assertThat(userSwitcherStub).isNotNull(); - assertThat(userSwitcherStub).isInstanceOf(ViewStub.class); - } - - @Test - public void testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() { - givenViewAttached(); - when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(null); - updateSmallestScreenWidth(300); - when(mResources.getBoolean( - com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true); - when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false); - when(mUserManager.isUserSwitcherEnabled(false)).thenReturn(true); - - updateSmallestScreenWidth(800); - - verify(mUserSwitcherStubView).inflate(); - } - - @Test - public void testFinishInflate_userSwitcherDisabled_doNotInflateUserSwitchView_initClock() { - givenViewAttached(); - when(mResources.getBoolean( - com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true); - when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false); - when(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */)) - .thenReturn(false); - - mNotificationPanelViewController.onFinishInflate(); - - verify(mUserSwitcherStubView, never()).inflate(); - verify(mKeyguardStatusViewController, times(3)).displayClock(LARGE, /* animate */ true); - } - - @Test - public void testReInflateViews_userSwitcherDisabled_doNotInflateUserSwitchView() { - givenViewAttached(); - when(mResources.getBoolean( - com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true); - when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false); - when(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */)) - .thenReturn(false); - - mNotificationPanelViewController.reInflateViews(); - - verify(mUserSwitcherStubView, never()).inflate(); - } - - @Test public void testCanCollapsePanelOnTouch_trueForKeyGuard() { mStatusBarStateController.setState(KEYGUARD); @@ -1129,26 +528,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test - public void testDoubleTapRequired_Keyguard() { - FalsingManager.FalsingTapListener listener = getFalsingTapListener(); - mStatusBarStateController.setState(KEYGUARD); - - listener.onAdditionalTapRequired(); - - verify(mKeyguardIndicationController).showTransientIndication(anyInt()); - } - - @Test - public void testDoubleTapRequired_ShadeLocked() { - FalsingManager.FalsingTapListener listener = getFalsingTapListener(); - mStatusBarStateController.setState(SHADE_LOCKED); - - listener.onAdditionalTapRequired(); - - verify(mTapAgainViewController).show(); - } - - @Test public void testRotatingToSplitShadeWithQsExpanded_transitionsToShadeLocked() { mStatusBarStateController.setState(KEYGUARD); when(mQsController.getExpanded()).thenReturn(true); @@ -1423,19 +802,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test - public void testOnAttachRefreshStatusBarState() { - mStatusBarStateController.setState(KEYGUARD); - when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(false); - for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { - listener.onViewAttachedToWindow(mView); - } - verify(mKeyguardStatusViewController).setKeyguardStatusViewVisibility( - KEYGUARD/*statusBarState*/, - false/*keyguardFadingAway*/, - false/*goingToFullShade*/, SHADE/*oldStatusBarState*/); - } - - @Test public void getMaxPanelTransitionDistance_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance() { enableSplitShade(true); mNotificationPanelViewController.expandWithQs(); @@ -1635,98 +1001,4 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mStatusBarStateController.setState(SHADE_LOCKED); assertThat(mNotificationPanelViewController.isShadeFullyOpen()).isTrue(); } - - private static MotionEvent createMotionEvent(int x, int y, int action) { - return MotionEvent.obtain( - /* downTime= */ 0, /* eventTime= */ 0, action, x, y, /* metaState= */ 0); - } - - private void triggerPositionClockAndNotifications() { - mNotificationPanelViewController.onQsSetExpansionHeightCalled(false); - } - - private FalsingManager.FalsingTapListener getFalsingTapListener() { - for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { - listener.onViewAttachedToWindow(mView); - } - assertThat(mFalsingManager.getTapListeners().size()).isEqualTo(1); - return mFalsingManager.getTapListeners().get(0); - } - - private void givenViewAttached() { - for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { - listener.onViewAttachedToWindow(mView); - } - } - - private ConstraintSet.Layout getConstraintSetLayout(@IdRes int id) { - ConstraintSet constraintSet = new ConstraintSet(); - constraintSet.clone(mNotificationContainerParent); - return constraintSet.getConstraint(id).layout; - } - - private void enableSplitShade(boolean enabled) { - when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled); - mNotificationPanelViewController.updateResources(); - } - - private void updateMultiUserSetting(boolean enabled) { - when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false); - when(mUserManager.isUserSwitcherEnabled(false)).thenReturn(enabled); - final ArgumentCaptor<ContentObserver> observerCaptor = - ArgumentCaptor.forClass(ContentObserver.class); - verify(mContentResolver) - .registerContentObserver(any(), anyBoolean(), observerCaptor.capture()); - observerCaptor.getValue().onChange(/* selfChange */ false); - } - - private void updateSmallestScreenWidth(int smallestScreenWidthDp) { - Configuration configuration = new Configuration(); - configuration.smallestScreenWidthDp = smallestScreenWidthDp; - mConfigurationController.onConfigurationChanged(configuration); - } - - private void onTouchEvent(MotionEvent ev) { - mTouchHandler.onTouch(mView, ev); - } - - private void setDozing(boolean dozing, boolean dozingAlwaysOn) { - when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn); - mNotificationPanelViewController.setDozing( - /* dozing= */ dozing, - /* animate= */ false - ); - } - - private void assertKeyguardStatusViewCentered() { - mNotificationPanelViewController.updateResources(); - assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isAnyOf( - ConstraintSet.PARENT_ID, ConstraintSet.UNSET); - } - - private void assertKeyguardStatusViewNotCentered() { - mNotificationPanelViewController.updateResources(); - assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isEqualTo( - R.id.qs_edge_guideline); - } - - private void setIsFullWidth(boolean fullWidth) { - float nsslWidth = fullWidth ? PANEL_WIDTH : PANEL_WIDTH / 2f; - when(mNotificationStackScrollLayoutController.getWidth()).thenReturn(nsslWidth); - triggerLayoutChange(); - } - - private void triggerLayoutChange() { - mLayoutChangeListener.onLayoutChange( - mView, - /* left= */ 0, - /* top= */ 0, - /* right= */ 0, - /* bottom= */ 0, - /* oldLeft= */ 0, - /* oldTop= */ 0, - /* oldRight= */ 0, - /* oldBottom= */ 0 - ); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt new file mode 100644 index 000000000000..0c046e93ee20 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.View +import android.view.ViewStub +import androidx.test.filters.SmallTest +import com.android.internal.util.CollectionUtils +import com.android.keyguard.KeyguardClockSwitch.LARGE +import com.android.systemui.R +import com.android.systemui.statusbar.StatusBarState.KEYGUARD +import com.android.systemui.statusbar.StatusBarState.SHADE +import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Captor +import org.mockito.Mockito.atLeastOnce +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify + +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +class NotificationPanelViewControllerWithCoroutinesTest : + NotificationPanelViewControllerBaseTest() { + + @Captor private lateinit var viewCaptor: ArgumentCaptor<View> + + override fun getMainDispatcher() = Dispatchers.Main.immediate + + @Test + fun testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() = runTest { + launch(Dispatchers.Main.immediate) { givenViewAttached() } + advanceUntilIdle() + + whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher)) + .thenReturn(true) + updateMultiUserSetting(true) + clearInvocations(mView) + + updateMultiUserSetting(false) + + verify(mView, atLeastOnce()).addView(viewCaptor.capture(), anyInt()) + val userSwitcherStub = + CollectionUtils.find( + viewCaptor.getAllValues(), + { view -> view.getId() == R.id.keyguard_user_switcher_stub } + ) + assertThat(userSwitcherStub).isNotNull() + assertThat(userSwitcherStub).isInstanceOf(ViewStub::class.java) + } + + @Test + fun testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() = runTest { + launch(Dispatchers.Main.immediate) { givenViewAttached() } + advanceUntilIdle() + + whenever(mView.findViewById<View>(R.id.keyguard_user_switcher_view)).thenReturn(null) + updateSmallestScreenWidth(300) + whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher)) + .thenReturn(true) + whenever(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)) + .thenReturn(false) + whenever(mUserManager.isUserSwitcherEnabled(false)).thenReturn(true) + + updateSmallestScreenWidth(800) + + verify(mUserSwitcherStubView).inflate() + } + + @Test + fun testFinishInflate_userSwitcherDisabled_doNotInflateUserSwitchView_initClock() = runTest { + launch(Dispatchers.Main.immediate) { givenViewAttached() } + advanceUntilIdle() + + whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher)) + .thenReturn(true) + whenever(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)) + .thenReturn(false) + whenever(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */)) + .thenReturn(false) + + mNotificationPanelViewController.onFinishInflate() + + verify(mUserSwitcherStubView, never()).inflate() + verify(mKeyguardStatusViewController, times(3)).displayClock(LARGE, /* animate */ true) + + coroutineContext.cancelChildren() + } + + @Test + fun testReInflateViews_userSwitcherDisabled_doNotInflateUserSwitchView() = runTest { + launch(Dispatchers.Main.immediate) { givenViewAttached() } + advanceUntilIdle() + + whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher)) + .thenReturn(true) + whenever(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)) + .thenReturn(false) + whenever(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */)) + .thenReturn(false) + + mNotificationPanelViewController.reInflateViews() + + verify(mUserSwitcherStubView, never()).inflate() + + coroutineContext.cancelChildren() + } + + @Test + fun testDoubleTapRequired_Keyguard() = runTest { + launch(Dispatchers.Main.immediate) { + val listener = getFalsingTapListener() + mStatusBarStateController.setState(KEYGUARD) + + listener.onAdditionalTapRequired() + + verify(mKeyguardIndicationController).showTransientIndication(anyInt()) + } + advanceUntilIdle() + } + + @Test + fun testDoubleTapRequired_ShadeLocked() = runTest { + launch(Dispatchers.Main.immediate) { + val listener = getFalsingTapListener() + mStatusBarStateController.setState(SHADE_LOCKED) + + listener.onAdditionalTapRequired() + + verify(mTapAgainViewController).show() + } + advanceUntilIdle() + } + + @Test + fun testOnAttachRefreshStatusBarState() = runTest { + launch(Dispatchers.Main.immediate) { + mStatusBarStateController.setState(KEYGUARD) + whenever(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(false) + mOnAttachStateChangeListeners.forEach { it.onViewAttachedToWindow(mView) } + verify(mKeyguardStatusViewController) + .setKeyguardStatusViewVisibility( + KEYGUARD /*statusBarState*/, + false /*keyguardFadingAway*/, + false /*goingToFullShade*/, + SHADE /*oldStatusBarState*/ + ) + } + advanceUntilIdle() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index d229a08ad7c4..82a57438052f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -28,11 +28,12 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.dock.DockManager -import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel +import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.NotificationInsetsController @@ -47,6 +48,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.emptyFlow import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -64,50 +66,32 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) class NotificationShadeWindowViewControllerTest : SysuiTestCase() { - @Mock - private lateinit var view: NotificationShadeWindowView - @Mock - private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController - @Mock - private lateinit var centralSurfaces: CentralSurfaces - @Mock - private lateinit var dockManager: DockManager - @Mock - private lateinit var notificationPanelViewController: NotificationPanelViewController - @Mock - private lateinit var notificationShadeDepthController: NotificationShadeDepthController - @Mock - private lateinit var notificationShadeWindowController: NotificationShadeWindowController - @Mock - private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController - @Mock - private lateinit var featureFlags: FeatureFlags - @Mock - private lateinit var ambientState: AmbientState - @Mock - private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel - @Mock - private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController - @Mock - private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager - @Mock - private lateinit var statusBarWindowStateController: StatusBarWindowStateController + @Mock private lateinit var view: NotificationShadeWindowView + @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController + @Mock private lateinit var centralSurfaces: CentralSurfaces + @Mock private lateinit var dockManager: DockManager + @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController + @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController + @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController + @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController + @Mock private lateinit var ambientState: AmbientState + @Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel + @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController + @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager + @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController @Mock private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController - @Mock - private lateinit var lockIconViewController: LockIconViewController - @Mock - private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController - @Mock - private lateinit var pulsingGestureListener: PulsingGestureListener - @Mock - private lateinit var notificationInsetsController: NotificationInsetsController - @Mock - private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor + @Mock private lateinit var lockIconViewController: LockIconViewController + @Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController + @Mock private lateinit var pulsingGestureListener: PulsingGestureListener + @Mock private lateinit var notificationInsetsController: NotificationInsetsController + @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor + @Mock + lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler> private lateinit var interactionEventHandler: InteractionEventHandler @@ -119,42 +103,44 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) whenever(view.bottom).thenReturn(VIEW_BOTTOM) whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container)) - .thenReturn(mock(ViewGroup::class.java)) + .thenReturn(mock(ViewGroup::class.java)) whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java))) - .thenReturn(keyguardBouncerComponent) + .thenReturn(keyguardBouncerComponent) whenever(keyguardBouncerComponent.securityContainerController) - .thenReturn(keyguardSecurityContainerController) - underTest = NotificationShadeWindowViewController( - lockscreenShadeTransitionController, - FalsingCollectorFake(), - sysuiStatusBarStateController, - dockManager, - notificationShadeDepthController, - view, - notificationPanelViewController, - ShadeExpansionStateManager(), - stackScrollLayoutController, - statusBarKeyguardViewManager, - statusBarWindowStateController, - lockIconViewController, - centralSurfaces, - notificationShadeWindowController, - keyguardUnlockAnimationController, - notificationInsetsController, - ambientState, - pulsingGestureListener, - featureFlags, - keyguardBouncerViewModel, - keyguardBouncerComponentFactory, - alternateBouncerInteractor, - keyguardTransitionInteractor, - ) + .thenReturn(keyguardSecurityContainerController) + whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition) + .thenReturn(emptyFlow<TransitionStep>()) + underTest = + NotificationShadeWindowViewController( + lockscreenShadeTransitionController, + FalsingCollectorFake(), + sysuiStatusBarStateController, + dockManager, + notificationShadeDepthController, + view, + notificationPanelViewController, + ShadeExpansionStateManager(), + stackScrollLayoutController, + statusBarKeyguardViewManager, + statusBarWindowStateController, + lockIconViewController, + centralSurfaces, + notificationShadeWindowController, + keyguardUnlockAnimationController, + notificationInsetsController, + ambientState, + pulsingGestureListener, + keyguardBouncerViewModel, + keyguardBouncerComponentFactory, + alternateBouncerInteractor, + keyguardTransitionInteractor, + primaryBouncerToGoneTransitionViewModel, + ) underTest.setupExpandedStatusBar() - interactionEventHandlerCaptor = - ArgumentCaptor.forClass(InteractionEventHandler::class.java) + interactionEventHandlerCaptor = ArgumentCaptor.forClass(InteractionEventHandler::class.java) verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture()) - interactionEventHandler = interactionEventHandlerCaptor.value + interactionEventHandler = interactionEventHandlerCaptor.value } // Note: So far, these tests only cover interactions with the status bar view controller. More @@ -184,14 +170,11 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Test fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() { underTest.setStatusBarViewController(phoneStatusBarViewController) - val downEvBelow = MotionEvent.obtain( - 0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0 - ) + val downEvBelow = + MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0) interactionEventHandler.handleDispatchTouchEvent(downEvBelow) - val nextEvent = MotionEvent.obtain( - 0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0 - ) + val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0) whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true) val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java index 5e9c2199897d..faa6221b675c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java @@ -25,6 +25,8 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static kotlinx.coroutines.flow.FlowKt.emptyFlow; + import android.os.SystemClock; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -40,11 +42,11 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.dock.DockManager; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel; +import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationInsetsController; @@ -93,7 +95,6 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; @Mock private AmbientState mAmbientState; @Mock private PulsingGestureListener mPulsingGestureListener; - @Mock private FeatureFlags mFeatureFlags; @Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel; @Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory; @Mock private KeyguardBouncerComponent mKeyguardBouncerComponent; @@ -101,6 +102,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private NotificationInsetsController mNotificationInsetsController; @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor; @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; + @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler> mInteractionEventHandlerCaptor; @@ -125,6 +127,9 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { when(mDockManager.isDocked()).thenReturn(false); + when(mKeyguardTransitionInteractor.getLockscreenToDreamingTransition()) + .thenReturn(emptyFlow()); + mController = new NotificationShadeWindowViewController( mLockscreenShadeTransitionController, new FalsingCollectorFake(), @@ -144,11 +149,11 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mNotificationInsetsController, mAmbientState, mPulsingGestureListener, - mFeatureFlags, mKeyguardBouncerViewModel, mKeyguardBouncerComponentFactory, mAlternateBouncerInteractor, - mKeyguardTransitionInteractor + mKeyguardTransitionInteractor, + mPrimaryBouncerToGoneTransitionViewModel ); mController.setupExpandedStatusBar(); mController.setDragDownHelper(mDragDownHelper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt index 7a74b1229c5c..56fc0c74dafa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt @@ -36,21 +36,26 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { private val progressProvider = TestUnfoldTransitionProvider() - @Mock private lateinit var parent: ViewGroup + @Mock + private lateinit var parent: ViewGroup + + @Mock + private lateinit var shouldBeAnimated: () -> Boolean private lateinit var animator: UnfoldConstantTranslateAnimator - private val viewsIdToRegister = - setOf( - ViewIdToTranslate(START_VIEW_ID, Direction.START), - ViewIdToTranslate(END_VIEW_ID, Direction.END)) + private val viewsIdToRegister + get() = + setOf( + ViewIdToTranslate(START_VIEW_ID, Direction.START, shouldBeAnimated), + ViewIdToTranslate(END_VIEW_ID, Direction.END, shouldBeAnimated) + ) @Before fun setup() { MockitoAnnotations.initMocks(this) - - animator = - UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider) + whenever(shouldBeAnimated.invoke()).thenReturn(true) + animator = UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider) animator.init(parent, MAX_TRANSLATION) } @@ -96,6 +101,20 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { moveAndValidate(listOf(leftView to START, rightView to END), View.LAYOUT_DIRECTION_LTR) } + @Test + fun onTransition_completeStartedTranslation() { + // GIVEN + val leftView = View(context) + whenever(parent.findViewById<View>(START_VIEW_ID)).thenReturn(leftView) + // To start animation, shouldBeAnimated should return true. + // There is a possibility for shouldBeAnimated to return false during the animation. + whenever(shouldBeAnimated.invoke()).thenReturn(true).thenReturn(false) + + // shouldBeAnimated state may change during the animation. + // However, started animation should be completed. + moveAndValidate(listOf(leftView to START), View.LAYOUT_DIRECTION_LTR) + } + private fun moveAndValidate(list: List<Pair<View, Int>>, layoutDirection: Int) { // Compare values as ints because -0f != 0f diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index 26eff61066ee..1fdb3647fcb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -35,7 +35,6 @@ import junit.framework.Assert.fail import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope -import org.json.JSONException import org.junit.Before import org.junit.Rule import org.junit.Test @@ -271,10 +270,14 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun jsonDeserialization_gotExpectedObject() { - val expected = ClockSettings("ID", null).apply { _applied_timestamp = 500 } + val expected = ClockSettings("ID", null).apply { + metadata.put("appliedTimestamp", 500) + } val actual = ClockSettings.deserialize("""{ "clockId":"ID", - "_applied_timestamp":500 + "metadata": { + "appliedTimestamp":500 + } }""") assertEquals(expected, actual) } @@ -291,29 +294,32 @@ class ClockRegistryTest : SysuiTestCase() { val expected = ClockSettings("ID", null) val actual = ClockSettings.deserialize("""{ "clockId":"ID", - "_applied_timestamp":null + "metadata":null }""") assertEquals(expected, actual) } - @Test(expected = JSONException::class) - fun jsonDeserialization_noId_threwException() { - val expected = ClockSettings(null, null).apply { _applied_timestamp = 500 } - val actual = ClockSettings.deserialize("{\"_applied_timestamp\":500}") + @Test + fun jsonDeserialization_noId_deserializedEmpty() { + val expected = ClockSettings(null, null).apply { + metadata.put("appliedTimestamp", 500) + } + val actual = ClockSettings.deserialize("{\"metadata\":{\"appliedTimestamp\":500}}") assertEquals(expected, actual) } @Test fun jsonSerialization_gotExpectedString() { - val expected = "{\"clockId\":\"ID\",\"_applied_timestamp\":500}" - val actual = ClockSettings.serialize(ClockSettings("ID", null) - .apply { _applied_timestamp = 500 }) + val expected = "{\"clockId\":\"ID\",\"metadata\":{\"appliedTimestamp\":500}}" + val actual = ClockSettings.serialize(ClockSettings("ID", null).apply { + metadata.put("appliedTimestamp", 500) + }) assertEquals(expected, actual) } @Test fun jsonSerialization_noTimestamp_gotExpectedString() { - val expected = "{\"clockId\":\"ID\"}" + val expected = "{\"clockId\":\"ID\",\"metadata\":{}}" val actual = ClockSettings.serialize(ClockSettings("ID", null)) assertEquals(expected, actual) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt index cd2efc061b72..7fa27f34cd9f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt @@ -26,6 +26,7 @@ import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.ClockSettings import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -40,7 +41,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyFloat -import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.notNull import org.mockito.Mock import org.mockito.Mockito.never @@ -97,13 +97,14 @@ class DefaultClockProviderTest : SysuiTestCase() { @Test fun defaultClock_initialize() { val clock = provider.createClock(DEFAULT_CLOCK_ID) - verify(mockSmallClockView).setColors(Color.MAGENTA, Color.MAGENTA) - verify(mockLargeClockView).setColors(Color.MAGENTA, Color.MAGENTA) + verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA) + verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA) clock.initialize(resources, 0f, 0f) - verify(mockSmallClockView).setColors(eq(DOZE_COLOR), anyInt()) - verify(mockLargeClockView).setColors(eq(DOZE_COLOR), anyInt()) + val expectedColor = 0 + verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor) + verify(mockLargeClockView).setColors(DOZE_COLOR, expectedColor) verify(mockSmallClockView).onTimeZoneChanged(notNull()) verify(mockLargeClockView).onTimeZoneChanged(notNull()) verify(mockSmallClockView).refreshTime() @@ -159,15 +160,31 @@ class DefaultClockProviderTest : SysuiTestCase() { @Test fun defaultClock_events_onColorPaletteChanged() { + val expectedColor = 0 val clock = provider.createClock(DEFAULT_CLOCK_ID) - verify(mockSmallClockView).setColors(Color.MAGENTA, Color.MAGENTA) - verify(mockLargeClockView).setColors(Color.MAGENTA, Color.MAGENTA) + verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA) + verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA) clock.events.onColorPaletteChanged(resources) - verify(mockSmallClockView).setColors(eq(DOZE_COLOR), anyInt()) - verify(mockLargeClockView).setColors(eq(DOZE_COLOR), anyInt()) + verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor) + verify(mockLargeClockView).setColors(DOZE_COLOR, expectedColor) + } + + @Test + fun defaultClock_events_onSeedColorChanged() { + val initSeedColor = 10 + val newSeedColor = 20 + val clock = provider.createClock(ClockSettings(DEFAULT_CLOCK_ID, initSeedColor)) + + verify(mockSmallClockView).setColors(DOZE_COLOR, initSeedColor) + verify(mockLargeClockView).setColors(DOZE_COLOR, initSeedColor) + + clock.events.onSeedColorChanged(newSeedColor) + + verify(mockSmallClockView).setColors(DOZE_COLOR, newSeedColor) + verify(mockLargeClockView).setColors(DOZE_COLOR, newSeedColor) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt new file mode 100644 index 000000000000..7a6779684fc5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.log.LogBuffer +import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.statusbar.StatusBarState +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class NotificationWakeUpCoordinatorLoggerTest : SysuiTestCase() { + + private val logBufferCounter = LogBufferCounter() + private lateinit var logger: NotificationWakeUpCoordinatorLogger + + private fun verifyDidLog(times: Int) { + logBufferCounter.verifyDidLog(times) + } + + @Before + fun setup() { + logger = NotificationWakeUpCoordinatorLogger(logBufferCounter.logBuffer) + } + + @Test + fun setDozeAmountWillThrottleFractionalUpdates() { + logger.logSetDozeAmount(0f, 0f, "source1", StatusBarState.SHADE, changed = false) + verifyDidLog(1) + logger.logSetDozeAmount(0.1f, 0.1f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(1) + logger.logSetDozeAmount(0.2f, 0.2f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.3f, 0.3f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.4f, 0.4f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.5f, 0.5f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(0) + logger.logSetDozeAmount(1f, 1f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(1) + } + + @Test + fun setDozeAmountWillIncludeFractionalUpdatesWhenStateChanges() { + logger.logSetDozeAmount(0f, 0f, "source1", StatusBarState.SHADE, changed = false) + verifyDidLog(1) + logger.logSetDozeAmount(0.1f, 0.1f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(1) + logger.logSetDozeAmount(0.2f, 0.2f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.3f, 0.3f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.4f, 0.4f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.5f, 0.5f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(0) + logger.logSetDozeAmount(0.5f, 0.5f, "source1", StatusBarState.KEYGUARD, changed = false) + verifyDidLog(1) + } + + @Test + fun setDozeAmountWillIncludeFractionalUpdatesWhenSourceChanges() { + logger.logSetDozeAmount(0f, 0f, "source1", StatusBarState.SHADE, changed = false) + verifyDidLog(1) + logger.logSetDozeAmount(0.1f, 0.1f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(1) + logger.logSetDozeAmount(0.2f, 0.2f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.3f, 0.3f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.4f, 0.4f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.5f, 0.5f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(0) + logger.logSetDozeAmount(0.5f, 0.5f, "source2", StatusBarState.SHADE, changed = false) + verifyDidLog(1) + } + + class LogBufferCounter { + val recentLogs = mutableListOf<Pair<String, LogLevel>>() + val tracker = + object : LogcatEchoTracker { + override val logInBackgroundThread: Boolean = false + override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean = false + override fun isTagLoggable(tagName: String, level: LogLevel): Boolean { + recentLogs.add(tagName to level) + return true + } + } + val logBuffer = + LogBuffer(name = "test", maxSize = 1, logcatEchoTracker = tracker, systrace = false) + + fun verifyDidLog(times: Int) { + assertThat(recentLogs).hasSize(times) + recentLogs.clear() + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt new file mode 100644 index 000000000000..95591a4b321c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dump.DumpManager +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.statusbar.policy.HeadsUpManager +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.anyFloat +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.verify + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class NotificationWakeUpCoordinatorTest : SysuiTestCase() { + + private val dumpManager: DumpManager = mock() + private val headsUpManager: HeadsUpManager = mock() + private val statusBarStateController: StatusBarStateController = mock() + private val bypassController: KeyguardBypassController = mock() + private val dozeParameters: DozeParameters = mock() + private val screenOffAnimationController: ScreenOffAnimationController = mock() + private val logger: NotificationWakeUpCoordinatorLogger = mock() + private val stackScrollerController: NotificationStackScrollLayoutController = mock() + + private lateinit var notificationWakeUpCoordinator: NotificationWakeUpCoordinator + private lateinit var statusBarStateCallback: StatusBarStateController.StateListener + private lateinit var bypassChangeCallback: KeyguardBypassController.OnBypassStateChangedListener + + private var bypassEnabled: Boolean = false + private var statusBarState: Int = StatusBarState.KEYGUARD + private var dozeAmount: Float = 0f + + private fun setBypassEnabled(enabled: Boolean) { + bypassEnabled = enabled + bypassChangeCallback.onBypassStateChanged(enabled) + } + + private fun setStatusBarState(state: Int) { + statusBarState = state + statusBarStateCallback.onStateChanged(state) + } + + private fun setDozeAmount(dozeAmount: Float) { + this.dozeAmount = dozeAmount + statusBarStateCallback.onDozeAmountChanged(dozeAmount, dozeAmount) + } + + @Before + fun setup() { + whenever(bypassController.bypassEnabled).then { bypassEnabled } + whenever(statusBarStateController.state).then { statusBarState } + notificationWakeUpCoordinator = + NotificationWakeUpCoordinator( + dumpManager, + headsUpManager, + statusBarStateController, + bypassController, + dozeParameters, + screenOffAnimationController, + logger, + ) + statusBarStateCallback = withArgCaptor { + verify(statusBarStateController).addCallback(capture()) + } + bypassChangeCallback = withArgCaptor { + verify(bypassController).registerOnBypassStateChangedListener(capture()) + } + notificationWakeUpCoordinator.setStackScroller(stackScrollerController) + } + + @Test + fun setDozeToOneWillFullyHideNotifications() { + setDozeAmount(1f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() + } + + @Test + fun setDozeToZeroWillFullyShowNotifications() { + setDozeAmount(0f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0f, hideAmount = 0f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + } + + @Test + fun setDozeToOneThenZeroWillFullyShowNotifications() { + setDozeToOneWillFullyHideNotifications() + clearInvocations(stackScrollerController) + setDozeToZeroWillFullyShowNotifications() + } + + @Test + fun setDozeToHalfWillHalfShowNotifications() { + setDozeAmount(0.5f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0.5f, hideAmount = 0.5f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + } + + @Test + fun setDozeToZeroWithBypassWillFullyHideNotifications() { + bypassEnabled = true + setDozeAmount(0f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 01f, hideAmount = 1f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() + } + + @Test + fun disablingBypassWillShowNotifications() { + setDozeToZeroWithBypassWillFullyHideNotifications() + clearInvocations(stackScrollerController) + setBypassEnabled(false) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0f, hideAmount = 0f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + } + + @Test + fun switchingToShadeWithBypassEnabledWillShowNotifications() { + setDozeToZeroWithBypassWillFullyHideNotifications() + clearInvocations(stackScrollerController) + setStatusBarState(StatusBarState.SHADE) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0f, hideAmount = 0f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + assertThat(notificationWakeUpCoordinator.statusBarState).isEqualTo(StatusBarState.SHADE) + } + + private fun verifyStackScrollerDozeAndHideAmount(dozeAmount: Float, hideAmount: Float) { + // First verify that we did in-fact receive the correct values + verify(stackScrollerController).setDozeAmount(dozeAmount) + verify(stackScrollerController).setHideAmount(hideAmount, hideAmount) + // Now verify that there was just this ONE call to each of these methods + verify(stackScrollerController).setDozeAmount(anyFloat()) + verify(stackScrollerController).setHideAmount(anyFloat(), anyFloat()) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt deleted file mode 100644 index a6a9e51aa555..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.notification.fsi - -import android.R -import android.app.Notification -import android.app.NotificationManager -import android.app.PendingIntent -import android.content.pm.ApplicationInfo -import android.content.pm.PackageManager -import android.graphics.drawable.Drawable -import android.os.UserHandle -import android.service.dreams.IDreamManager -import android.service.notification.StatusBarNotification -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper.RunWithLooper -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.statusbar.notification.collection.NotificationEntry -import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider -import com.android.systemui.statusbar.phone.CentralSurfaces -import java.util.concurrent.Executor -import junit.framework.Assert.assertEquals -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyString -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.never -import org.mockito.Mockito.times -import org.mockito.Mockito.`when` as whenever -import org.mockito.MockitoAnnotations - -@SmallTest -@RunWith(AndroidTestingRunner::class) -@RunWithLooper(setAsMainLooper = true) -class FsiChromeRepoTest : SysuiTestCase() { - - @Mock lateinit var centralSurfaces: CentralSurfaces - @Mock lateinit var fsiChromeRepo: FsiChromeRepo - @Mock lateinit var packageManager: PackageManager - - var keyguardRepo = FakeKeyguardRepository() - @Mock private lateinit var applicationInfo: ApplicationInfo - - @Mock lateinit var launchFullScreenIntentProvider: LaunchFullScreenIntentProvider - var featureFlags = FakeFeatureFlags() - @Mock lateinit var dreamManager: IDreamManager - - // Execute all foreground & background requests immediately - private val uiBgExecutor = Executor { r -> r.run() } - - private val appName: String = "appName" - private val appIcon: Drawable = context.getDrawable(com.android.systemui.R.drawable.ic_android) - private val fsi: PendingIntent = Mockito.mock(PendingIntent::class.java) - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - - // Set up package manager mocks - whenever(packageManager.getApplicationIcon(anyString())).thenReturn(appIcon) - whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java))) - .thenReturn(appIcon) - whenever(packageManager.getApplicationLabel(any())).thenReturn(appName) - mContext.setMockPackageManager(packageManager) - - fsiChromeRepo = - FsiChromeRepo( - mContext, - packageManager, - keyguardRepo, - launchFullScreenIntentProvider, - featureFlags, - uiBgExecutor, - dreamManager, - centralSurfaces - ) - } - - private fun createFsiEntry(fsi: PendingIntent): NotificationEntry { - val nb = - Notification.Builder(mContext, "a") - .setContentTitle("foo") - .setSmallIcon(R.drawable.sym_def_app_icon) - .setFullScreenIntent(fsi, /* highPriority= */ true) - - val sbn = - StatusBarNotification( - "pkg", - "opPkg", - /* id= */ 0, - "tag" + System.currentTimeMillis(), - /* uid= */ 0, - /* initialPid */ 0, - nb.build(), - UserHandle(0), - /* overrideGroupKey= */ null, - /* postTime= */ 0 - ) - - val entry = Mockito.mock(NotificationEntry::class.java) - whenever(entry.importance).thenReturn(NotificationManager.IMPORTANCE_HIGH) - whenever(entry.sbn).thenReturn(sbn) - return entry - } - - @Test - fun testLaunchFullscreenIntent_flagNotEnabled_noLaunch() { - // Setup - featureFlags.set(Flags.FSI_CHROME, false) - - // Test - val entry = createFsiEntry(fsi) - fsiChromeRepo.launchFullscreenIntent(entry) - - // Verify - Mockito.verify(centralSurfaces, never()).wakeUpForFullScreenIntent() - } - - @Test - fun testLaunchFullscreenIntent_notOnKeyguard_noLaunch() { - // Setup - featureFlags.set(Flags.FSI_CHROME, true) - keyguardRepo.setKeyguardShowing(false) - - // Test - val entry = createFsiEntry(fsi) - fsiChromeRepo.launchFullscreenIntent(entry) - - // Verify - Mockito.verify(centralSurfaces, never()).wakeUpForFullScreenIntent() - } - - @Test - fun testLaunchFullscreenIntent_stopsScreensaver() { - // Setup - featureFlags.set(Flags.FSI_CHROME, true) - keyguardRepo.setKeyguardShowing(true) - - // Test - val entry = createFsiEntry(fsi) - fsiChromeRepo.launchFullscreenIntent(entry) - - // Verify - Mockito.verify(dreamManager, times(1)).awaken() - } - - @Test - fun testLaunchFullscreenIntent_updatesFsiInfoFlow() { - // Setup - featureFlags.set(Flags.FSI_CHROME, true) - keyguardRepo.setKeyguardShowing(true) - - // Test - val entry = createFsiEntry(fsi) - fsiChromeRepo.launchFullscreenIntent(entry) - - // Verify - val expectedFsiInfo = FsiChromeRepo.FSIInfo(appName, appIcon, fsi) - assertEquals(expectedFsiInfo, fsiChromeRepo.infoFlow.value) - } - - @Test - fun testLaunchFullscreenIntent_notifyFsiLaunched() { - // Setup - featureFlags.set(Flags.FSI_CHROME, true) - keyguardRepo.setKeyguardShowing(true) - - // Test - val entry = createFsiEntry(fsi) - fsiChromeRepo.launchFullscreenIntent(entry) - - // Verify - Mockito.verify(entry, times(1)).notifyFullScreenIntentLaunched() - } - - @Test - fun testLaunchFullscreenIntent_wakesUpDevice() { - // Setup - featureFlags.set(Flags.FSI_CHROME, true) - keyguardRepo.setKeyguardShowing(true) - - // Test - val entry = createFsiEntry(fsi) - fsiChromeRepo.launchFullscreenIntent(entry) - - // Verify - Mockito.verify(centralSurfaces, times(1)).wakeUpForFullScreenIntent() - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactoryTest.kt deleted file mode 100644 index 5cee9e377dfb..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactoryTest.kt +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.notification.fsi - -import android.app.PendingIntent -import android.graphics.drawable.Drawable -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper.RunWithLooper -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.whenever -import com.android.systemui.util.mockito.withArgCaptor -import com.android.systemui.util.time.FakeSystemClock -import com.android.wm.shell.TaskView -import com.android.wm.shell.TaskViewFactory -import com.google.common.truth.Truth.assertThat -import java.util.Optional -import java.util.function.Consumer -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -@SmallTest -@RunWith(AndroidTestingRunner::class) -@RunWithLooper(setAsMainLooper = true) -class FsiChromeViewModelFactoryTest : SysuiTestCase() { - @Mock private lateinit var taskViewFactoryOptional: Optional<TaskViewFactory> - @Mock private lateinit var taskViewFactory: TaskViewFactory - @Mock lateinit var taskView: TaskView - - @Main var mainExecutor = FakeExecutor(FakeSystemClock()) - lateinit var viewModelFactory: FsiChromeViewModelFactory - - private val fakeInfoFlow = MutableStateFlow<FsiChromeRepo.FSIInfo?>(null) - private var fsiChromeRepo: FsiChromeRepo = - mock<FsiChromeRepo>().apply { whenever(infoFlow).thenReturn(fakeInfoFlow) } - - private val appName = "appName" - private val appIcon: Drawable = context.getDrawable(com.android.systemui.R.drawable.ic_android) - private val fsi: PendingIntent = Mockito.mock(PendingIntent::class.java) - private val fsiInfo = FsiChromeRepo.FSIInfo(appName, appIcon, fsi) - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - - whenever(taskViewFactoryOptional.get()).thenReturn(taskViewFactory) - - viewModelFactory = - FsiChromeViewModelFactory(fsiChromeRepo, taskViewFactoryOptional, context, mainExecutor) - } - - @Test - fun testViewModelFlow_update_createsTaskView() { - runTest { - val latestViewModel = - viewModelFactory.viewModelFlow - .onStart { FsiDebug.log("viewModelFactory.viewModelFlow.onStart") } - .stateIn( - backgroundScope, // stateIn runs forever, don't count it as test coroutine - SharingStarted.Eagerly, - null - ) - runCurrent() // Drain queued backgroundScope operations - - // Test: emit the fake FSIInfo - fakeInfoFlow.emit(fsiInfo) - runCurrent() - - val taskViewFactoryCallback: Consumer<TaskView> = withArgCaptor { - verify(taskViewFactory).create(any(), any(), capture()) - } - taskViewFactoryCallback.accept(taskView) // this will call k.resume - runCurrent() - - // Verify that the factory has produced a new ViewModel - // containing the relevant data from FsiInfo - val expectedViewModel = - FsiChromeViewModel(appName, appIcon, taskView, fsi, fsiChromeRepo) - - assertThat(latestViewModel.value).isEqualTo(expectedViewModel) - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 5b8305dbbd33..89c399bbf204 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -415,6 +415,37 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { verify(mNotificationStackScrollLayout).updateEmptyShadeView(anyBoolean(), anyBoolean()); } + @Test + public void testAttach_updatesViewStatusBarState() { + // GIVEN: Controller is attached + mController.attach(mNotificationStackScrollLayout); + ArgumentCaptor<StatusBarStateController.StateListener> captor = + ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); + verify(mSysuiStatusBarStateController).addCallback(captor.capture(), anyInt()); + StatusBarStateController.StateListener stateListener = captor.getValue(); + + // WHEN: StatusBarState changes to SHADE + when(mSysuiStatusBarStateController.getState()).thenReturn(SHADE); + stateListener.onStateChanged(SHADE); + + // THEN: NSSL is updated with the current state + verify(mNotificationStackScrollLayout).setStatusBarState(SHADE); + + // WHEN: Controller is detached + mController.mOnAttachStateChangeListener + .onViewDetachedFromWindow(mNotificationStackScrollLayout); + + // WHEN: StatusBarState changes to KEYGUARD + when(mSysuiStatusBarStateController.getState()).thenReturn(KEYGUARD); + + // WHEN: Controller is re-attached + mController.mOnAttachStateChangeListener + .onViewAttachedToWindow(mNotificationStackScrollLayout); + + // THEN: NSSL is updated with the current state + verify(mNotificationStackScrollLayout).setStatusBarState(KEYGUARD); + } + private LogMaker logMatcher(int category, int type) { return argThat(new LogMatcher(category, type)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java index 680a32375988..78da78269ac4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java @@ -20,6 +20,7 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -227,14 +228,154 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { } @Test - public void testHandleUpEvent_menuRow() { - when(mSwipeHelper.getCurrentMenuRow()).thenReturn(mMenuRow); - doNothing().when(mSwipeHelper).handleMenuRowSwipe(mEvent, mView, 0, mMenuRow); + public void testHandleUpEvent_menuRowWithoutMenu_dismiss() { + doNothing().when(mSwipeHelper).dismiss(any(), anyFloat()); + doReturn(true).when(mSwipeHelper).isDismissGesture(any()); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + when(mMenuRow.shouldShowMenu()).thenReturn(true); + mSwipeHelper.setCurrentMenuRow(mMenuRow); + + assertTrue("Menu row exists", + mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); + verify(mMenuRow, times(1)).onTouchEnd(); + verify(mSwipeHelper, times(1)).isDismissGesture(mEvent); + verify(mSwipeHelper, times(1)).dismiss(mView, 0); + verify(mSwipeHelper, never()).isFalseGesture(); + } + + @Test + public void testHandleUpEvent_menuRowWithoutMenu_snapback() { + doNothing().when(mSwipeHelper).snapChild(any(), anyInt(), anyFloat()); + doReturn(false).when(mSwipeHelper).isDismissGesture(any()); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + when(mMenuRow.shouldShowMenu()).thenReturn(true); + mSwipeHelper.setCurrentMenuRow(mMenuRow); + + assertTrue("Menu row exists", + mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); + verify(mMenuRow, times(1)).onTouchEnd(); + verify(mSwipeHelper, times(1)).isDismissGesture(mEvent); + verify(mSwipeHelper, times(1)).snapClosed(mView, 0); + verify(mMenuRow, times(1)).onSnapClosed(); + verify(mSwipeHelper, never()).isFalseGesture(); + } + + @Test + public void testHandleUpEvent_menuRowWithOpenMenu_dismissed() { + doNothing().when(mSwipeHelper).dismiss(any(), anyFloat()); + doReturn(true).when(mSwipeHelper).isDismissGesture(any()); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + when(mMenuRow.shouldShowMenu()).thenReturn(true); + when(mMenuRow.isSnappedAndOnSameSide()).thenReturn(true); + mSwipeHelper.setCurrentMenuRow(mMenuRow); + + assertTrue("Menu row exists", + mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); + verify(mMenuRow, times(1)).onTouchEnd(); + verify(mSwipeHelper, times(1)).isDismissGesture(mEvent); + verify(mSwipeHelper, times(1)).dismiss(mView, 0); + verify(mSwipeHelper, never()).isFalseGesture(); + } + + @Test + public void testHandleUpEvent_menuRowWithOpenMenu_snapback() { + doNothing().when(mSwipeHelper).snapChild(any(), anyInt(), anyFloat()); + doReturn(false).when(mSwipeHelper).isDismissGesture(any()); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + when(mMenuRow.shouldShowMenu()).thenReturn(true); + when(mMenuRow.isSnappedAndOnSameSide()).thenReturn(true); + mSwipeHelper.setCurrentMenuRow(mMenuRow); + + assertTrue("Menu row exists", + mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); + verify(mMenuRow, times(1)).onTouchEnd(); + verify(mSwipeHelper, times(1)).isDismissGesture(mEvent); + verify(mSwipeHelper, times(1)).snapClosed(mView, 0); + verify(mMenuRow, times(1)).onSnapClosed(); + verify(mSwipeHelper, never()).isFalseGesture(); + } + + @Test + public void testHandleUpEvent_menuRowWithClosedMenu_dismissed() { + doNothing().when(mSwipeHelper).dismiss(any(), anyFloat()); + doReturn(true).when(mSwipeHelper).isDismissGesture(any()); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + when(mMenuRow.shouldShowMenu()).thenReturn(true); + when(mMenuRow.isSnappedAndOnSameSide()).thenReturn(false); + mSwipeHelper.setCurrentMenuRow(mMenuRow); assertTrue("Menu row exists", mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); verify(mMenuRow, times(1)).onTouchEnd(); - verify(mSwipeHelper, times(1)).handleMenuRowSwipe(mEvent, mView, 0, mMenuRow); + verify(mSwipeHelper, times(1)).isDismissGesture(mEvent); + verify(mSwipeHelper, times(1)).dismiss(mView, 0); + verify(mSwipeHelper, never()).isFalseGesture(); + } + + @Test + public void testHandleUpEvent_menuRowWithClosedMenu_snapback() { + doNothing().when(mSwipeHelper).snapChild(any(), anyInt(), anyFloat()); + doReturn(false).when(mSwipeHelper).isDismissGesture(any()); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + when(mMenuRow.shouldShowMenu()).thenReturn(true); + when(mMenuRow.isSnappedAndOnSameSide()).thenReturn(false); + mSwipeHelper.setCurrentMenuRow(mMenuRow); + + assertTrue("Menu row exists", + mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); + verify(mMenuRow, times(1)).onTouchEnd(); + verify(mSwipeHelper, times(1)).isDismissGesture(mEvent); + verify(mSwipeHelper, times(1)).snapClosed(mView, 0); + verify(mMenuRow, times(1)).onSnapClosed(); + verify(mSwipeHelper, never()).isFalseGesture(); + } + + @Test + public void testIsDismissGesture() { + doReturn(false).when(mSwipeHelper).isFalseGesture(); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + doReturn(true).when(mSwipeHelper).swipedFastEnough(); + when(mCallback.canChildBeDismissedInDirection(any(), anyBoolean())).thenReturn(true); + when(mEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_UP); + + assertTrue("Should be a dismiss gesture", mSwipeHelper.isDismissGesture(mEvent)); + verify(mSwipeHelper, times(1)).isFalseGesture(); + } + + @Test + public void testIsDismissGesture_falseGesture() { + doReturn(true).when(mSwipeHelper).isFalseGesture(); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + doReturn(true).when(mSwipeHelper).swipedFastEnough(); + when(mCallback.canChildBeDismissedInDirection(any(), anyBoolean())).thenReturn(true); + when(mEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_UP); + + assertFalse("False gesture should stop dismissal", mSwipeHelper.isDismissGesture(mEvent)); + verify(mSwipeHelper, times(1)).isFalseGesture(); + } + + @Test + public void testIsDismissGesture_farEnough() { + doReturn(false).when(mSwipeHelper).isFalseGesture(); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + doReturn(false).when(mSwipeHelper).swipedFastEnough(); + when(mCallback.canChildBeDismissedInDirection(any(), anyBoolean())).thenReturn(true); + when(mEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_UP); + + assertTrue("Should be a dismissal", mSwipeHelper.isDismissGesture(mEvent)); + verify(mSwipeHelper, times(1)).isFalseGesture(); + } + + @Test + public void testIsDismissGesture_notFarOrFastEnough() { + doReturn(false).when(mSwipeHelper).isFalseGesture(); + doReturn(false).when(mSwipeHelper).swipedFarEnough(); + doReturn(false).when(mSwipeHelper).swipedFastEnough(); + when(mCallback.canChildBeDismissedInDirection(any(), anyBoolean())).thenReturn(true); + when(mEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_UP); + + assertFalse("Should not be a dismissal", mSwipeHelper.isDismissGesture(mEvent)); + verify(mSwipeHelper, times(1)).isFalseGesture(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index ec294b12a11a..68d67ca20007 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -517,6 +517,26 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { verify(mVibratorHelper).vibrateAuthError(anyString()); } + @Test + public void onFingerprintDetect_showBouncer() { + // WHEN fingerprint detect occurs + mBiometricUnlockController.onBiometricDetected(UserHandle.USER_CURRENT, + BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */); + + // THEN shows primary bouncer + verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean()); + } + + @Test + public void onFaceDetect_showBouncer() { + // WHEN face detect occurs + mBiometricUnlockController.onBiometricDetected(UserHandle.USER_CURRENT, + BiometricSourceType.FACE, false /* isStrongBiometric */); + + // THEN shows primary bouncer + verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean()); + } + private void givenFingerprintModeUnlockCollapsing() { when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index db4bb45f75e3..c4ee32666d74 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -77,6 +77,8 @@ import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewRootImpl; import android.view.WindowManager; +import android.window.BackEvent; +import android.window.OnBackAnimationCallback; import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedDispatcher; import android.window.WindowOnBackInvokedDispatcher; @@ -182,6 +184,8 @@ import com.android.systemui.volume.VolumeComponent; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.startingsurface.StartingSurface; +import dagger.Lazy; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -194,8 +198,6 @@ import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.util.Optional; -import dagger.Lazy; - @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) @@ -333,6 +335,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false); // Set default value to avoid IllegalStateException. mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false); + // For the Shade to respond to Back gesture, we must enable the event routing + mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true); + // For the Shade to animate during the Back gesture, we must enable the animation flag. + mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true); IThermalService thermalService = mock(IThermalService.class); mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService, @@ -855,6 +861,50 @@ public class CentralSurfacesImplTest extends SysuiTestCase { verify(mShadeController).animateCollapseShade(); } + /** + * When back progress is at 100%, the onBackProgressed animation driver inside + * NotificationPanelViewController should be invoked appropriately (with 1.0f passed in). + */ + @Test + public void testPredictiveBackAnimation_progressMaxScalesPanel() { + mCentralSurfaces.setNotificationShadeWindowViewController( + mNotificationShadeWindowViewController); + mCentralSurfaces.handleVisibleToUserChanged(true); + verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( + eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), + mOnBackInvokedCallback.capture()); + + OnBackAnimationCallback onBackAnimationCallback = + (OnBackAnimationCallback) (mOnBackInvokedCallback.getValue()); + when(mNotificationPanelViewController.canPanelBeCollapsed()).thenReturn(true); + + BackEvent fakeSwipeInFromLeftEdge = new BackEvent(20.0f, 100.0f, 1.0f, BackEvent.EDGE_LEFT); + onBackAnimationCallback.onBackProgressed(fakeSwipeInFromLeftEdge); + verify(mNotificationPanelViewController).onBackProgressed(eq(1.0f)); + } + + /** + * When back progress is at 0%, the onBackProgressed animation driver inside + * NotificationPanelViewController should be invoked appropriately (with 0.0f passed in). + */ + @Test + public void testPredictiveBackAnimation_progressMinScalesPanel() { + mCentralSurfaces.setNotificationShadeWindowViewController( + mNotificationShadeWindowViewController); + mCentralSurfaces.handleVisibleToUserChanged(true); + verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( + eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), + mOnBackInvokedCallback.capture()); + + OnBackAnimationCallback onBackAnimationCallback = + (OnBackAnimationCallback) (mOnBackInvokedCallback.getValue()); + when(mNotificationPanelViewController.canPanelBeCollapsed()).thenReturn(true); + + BackEvent fakeSwipeInFromLeftEdge = new BackEvent(20.0f, 10.0f, 0.0f, BackEvent.EDGE_LEFT); + onBackAnimationCallback.onBackProgressed(fakeSwipeInFromLeftEdge); + verify(mNotificationPanelViewController).onBackProgressed(eq(0.0f)); + } + @Test public void testPanelOpenForHeadsUp() { when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); @@ -1030,6 +1080,17 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test + public void testSetDozingNotUnlocking_transitionToAuthScrimmed_cancelKeyguardFadingAway() { + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); + when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true); + + mCentralSurfaces.updateScrimController(); + + verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE)); + verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway(); + } + + @Test public void testShowKeyguardImplementation_setsState() { when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt index 305b9fea7569..6b18169bcd86 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone import android.app.AlarmManager -import android.app.IActivityManager import android.app.admin.DevicePolicyManager import android.content.SharedPreferences import android.os.UserManager @@ -87,7 +86,6 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var locationController: LocationController @Mock private lateinit var sensorPrivacyController: SensorPrivacyController - @Mock private lateinit var iActivityManager: IActivityManager @Mock private lateinit var alarmManager: AlarmManager @Mock private lateinit var userManager: UserManager @Mock private lateinit var userTracker: UserTracker @@ -176,6 +174,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { commandQueue, broadcastDispatcher, executor, + executor, testableLooper.looper, context.resources, castController, @@ -190,7 +189,6 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { keyguardStateController, locationController, sensorPrivacyController, - iActivityManager, alarmManager, userManager, userTracker, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index c0537a6dc4cf..180d9f8956c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -24,6 +24,8 @@ import static com.android.systemui.statusbar.phone.ScrimState.SHADE_LOCKED; import static com.google.common.truth.Truth.assertThat; +import static kotlinx.coroutines.flow.FlowKt.emptyFlow; + import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; @@ -58,7 +60,12 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants; +import com.android.systemui.keyguard.shared.model.KeyguardState; +import com.android.systemui.keyguard.shared.model.TransitionState; +import com.android.systemui.keyguard.shared.model.TransitionStep; +import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; import com.android.systemui.scrim.ScrimView; import com.android.systemui.statusbar.policy.FakeConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -85,8 +92,10 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import kotlinx.coroutines.CoroutineDispatcher; + @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper +@TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class ScrimControllerTest extends SysuiTestCase { @@ -115,6 +124,10 @@ public class ScrimControllerTest extends SysuiTestCase { @Mock private DockManager mDockManager; @Mock private ScreenOffAnimationController mScreenOffAnimationController; @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; + @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; + @Mock private CoroutineDispatcher mMainDispatcher; + // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The // event-dispatch-on-registration pattern caused some of these unit tests to fail.) @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -225,13 +238,20 @@ public class ScrimControllerTest extends SysuiTestCase { when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock); when(mDockManager.isDocked()).thenReturn(false); + when(mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition()) + .thenReturn(emptyFlow()); + when(mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha()).thenReturn(emptyFlow()); + mScrimController = new ScrimController(mLightBarController, mDozeParameters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder, new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()), mScreenOffAnimationController, mKeyguardUnlockAnimationController, - mStatusBarKeyguardViewManager); + mStatusBarKeyguardViewManager, + mPrimaryBouncerToGoneTransitionViewModel, + mKeyguardTransitionInteractor, + mMainDispatcher); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); @@ -861,7 +881,10 @@ public class ScrimControllerTest extends SysuiTestCase { mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()), mScreenOffAnimationController, mKeyguardUnlockAnimationController, - mStatusBarKeyguardViewManager); + mStatusBarKeyguardViewManager, + mPrimaryBouncerToGoneTransitionViewModel, + mKeyguardTransitionInteractor, + mMainDispatcher); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); @@ -1356,33 +1379,10 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test - public void notificationAlpha_unnocclusionAnimating_bouncerActive_usesKeyguardNotifAlpha() { - when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true); - mScrimController.setClipsQsScrim(true); - - mScrimController.transitionTo(ScrimState.KEYGUARD); - mScrimController.setUnocclusionAnimationRunning(true); - - assertAlphaAfterExpansion( - mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 0f); - assertAlphaAfterExpansion( - mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 0.4f); - assertAlphaAfterExpansion( - mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 1.0f); - - // Verify normal behavior after - mScrimController.setUnocclusionAnimationRunning(false); - float expansion = 0.4f; - float alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); - assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); - } - - @Test public void notificationAlpha_unnocclusionAnimating_bouncerNotActive_usesKeyguardNotifAlpha() { when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false); mScrimController.transitionTo(ScrimState.KEYGUARD); - mScrimController.setUnocclusionAnimationRunning(true); assertAlphaAfterExpansion( mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 0f); @@ -1392,7 +1392,6 @@ public class ScrimControllerTest extends SysuiTestCase { mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 1.0f); // Verify normal behavior after - mScrimController.setUnocclusionAnimationRunning(false); float expansion = 0.4f; float alpha = 1 - ShadeInterpolation.getNotificationScrimAlpha(expansion); assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); @@ -1598,7 +1597,6 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void setUnOccludingAnimationKeyguard() { - mScrimController.setUnocclusionAnimationRunning(true); mScrimController.transitionTo(ScrimState.KEYGUARD); finishAnimationsImmediately(); assertThat(mNotificationsScrim.getViewAlpha()) @@ -1654,6 +1652,18 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(mScrimBehind, 0); } + @Test + public void ignoreTransitionRequestWhileKeyguardTransitionRunning() { + mScrimController.transitionTo(ScrimState.UNLOCKED); + mScrimController.mPrimaryBouncerToGoneTransition.accept( + new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f, + TransitionState.RUNNING, "ScrimControllerTest")); + + // This request should not happen + mScrimController.transitionTo(ScrimState.BOUNCER); + assertThat(mScrimController.getState()).isEqualTo(ScrimState.UNLOCKED); + } + private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) { mScrimController.setRawPanelExpansionFraction(expansion); finishAnimationsImmediately(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 1aad83eb73ae..158e9adcff43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -368,17 +368,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() { - mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */); - verify(mCentralSurfaces).animateKeyguardUnoccluding(); - - when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true); - clearInvocations(mCentralSurfaces); - mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */); - verify(mCentralSurfaces, never()).animateKeyguardUnoccluding(); - } - - @Test public void setOccluded_onKeyguardOccludedChangedCalled() { clearInvocations(mKeyguardStateController); clearInvocations(mKeyguardUpdateMonitor); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt index 00ce412f2a65..b072deedb9c9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt @@ -239,6 +239,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC * list2 = [false, true] * list3 = [a, b, c] * ``` + * * We'll generate test cases for: * * Test (1, false, a) Test (2, false, a) Test (3, false, a) Test (1, true, a) Test (1, diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt index 5288608a202d..0413d92b6abb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt @@ -25,7 +25,6 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING -import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE import com.android.systemui.unfold.util.TestFoldStateProvider import org.junit.Before import org.junit.Test @@ -50,7 +49,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { runOnMainThreadWithInterval( { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) }, { foldStateProvider.sendHingeAngleUpdate(10f) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, { foldStateProvider.sendHingeAngleUpdate(90f) }, { foldStateProvider.sendHingeAngleUpdate(180f) }, { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }, @@ -67,7 +66,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { runOnMainThreadWithInterval( { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) }, { foldStateProvider.sendHingeAngleUpdate(10f) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, { foldStateProvider.sendHingeAngleUpdate(90f) }, { foldStateProvider.sendHingeAngleUpdate(180f) }, { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }, @@ -84,7 +83,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { { foldStateProvider.sendHingeAngleUpdate(90f) }, { foldStateProvider.sendHingeAngleUpdate(180f) }, { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, ) with(listener.ensureTransitionFinished()) { @@ -113,7 +112,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { fun testUnfoldAndStopUnfolding_finishesTheUnfoldTransition() { runOnMainThreadWithInterval( { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, { foldStateProvider.sendHingeAngleUpdate(10f) }, { foldStateProvider.sendHingeAngleUpdate(90f) }, { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN) }, @@ -129,7 +128,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { fun testFoldImmediatelyAfterUnfold_runsFoldAnimation() { runOnMainThreadWithInterval( { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, { foldStateProvider.sendHingeAngleUpdate(10f) }, { foldStateProvider.sendHingeAngleUpdate(90f) }, { diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt index 6086e16fb49a..8476d0d45603 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider import com.android.systemui.unfold.updates.FoldProvider.FoldCallback import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener +import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES import com.android.systemui.unfold.updates.hinge.HingeAngleProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener @@ -71,6 +72,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { private val foldUpdates: MutableList<Int> = arrayListOf() private val hingeAngleUpdates: MutableList<Float> = arrayListOf() + private val unfoldedScreenAvailabilityUpdates: MutableList<Unit> = arrayListOf() private var scheduledRunnable: Runnable? = null private var scheduledRunnableDelay: Long? = null @@ -106,6 +108,10 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { override fun onFoldUpdate(update: Int) { foldUpdates.add(update) } + + override fun onUnfoldedScreenAvailable() { + unfoldedScreenAvailabilityUpdates.add(Unit) + } }) foldStateProvider.start() @@ -156,8 +162,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent(10) screenOnStatusProvider.notifyScreenTurnedOn() - assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING, - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING) + assertThat(unfoldedScreenAvailabilityUpdates).hasSize(1) } @Test @@ -174,8 +180,9 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent(40) sendHingeAngleEvent(10) - assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING, - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE, FOLD_UPDATE_START_CLOSING) + assertThat(foldUpdates) + .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) + assertThat(unfoldedScreenAvailabilityUpdates).hasSize(1) } @Test @@ -223,7 +230,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { fireScreenOnEvent() - assertThat(foldUpdates).containsExactly(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) + assertThat(unfoldedScreenAvailabilityUpdates).hasSize(1) } @Test @@ -277,7 +284,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_afterTimeout_finishHalfOpenEventEmitted() { - sendHingeAngleEvent(90) + setInitialHingeAngle(90) sendHingeAngleEvent(80) simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS) @@ -288,7 +295,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_beforeTimeout_abortNotEmitted() { - sendHingeAngleEvent(90) + setInitialHingeAngle(90) sendHingeAngleEvent(80) simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1) @@ -298,7 +305,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_eventBeforeTimeout_oneEventEmitted() { - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(90) simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1) @@ -309,7 +316,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_timeoutAfterTimeoutRescheduled_finishHalfOpenStateEmitted() { - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(90) // The timeout should not trigger here. @@ -323,7 +330,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_shortTimeBetween_emitsOnlyOneEvents() { - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(90) sendHingeAngleEvent(80) @@ -334,20 +341,19 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileClosing_emittedDespiteInitialAngle() { val maxAngle = 180 - FULLY_OPEN_THRESHOLD_DEGREES.toInt() - for (i in 1..maxAngle) { - foldUpdates.clear() - - simulateFolding(startAngle = i) + val minAngle = Math.ceil(HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toDouble()).toInt() + 1 + for (startAngle in minAngle..maxAngle) { + setInitialHingeAngle(startAngle) + sendHingeAngleEvent(startAngle - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) - simulateTimeout() // Timeout to set the state to aborted. } } @Test fun startClosingEvent_whileNotOnLauncher_doesNotTriggerBeforeThreshold() { setupForegroundActivityType(isHomeActivity = false) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -357,7 +363,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileActivityTypeNotAvailable_triggerBeforeThreshold() { setupForegroundActivityType(isHomeActivity = null) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -367,7 +373,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileOnLauncher_doesTriggerBeforeThreshold() { setupForegroundActivityType(isHomeActivity = true) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -377,9 +383,11 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileNotOnLauncher_triggersAfterThreshold() { setupForegroundActivityType(isHomeActivity = false) - sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) + setInitialHingeAngle(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) - sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 1) + sendHingeAngleEvent( + START_CLOSING_ON_APPS_THRESHOLD_DEGREES - + HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } @@ -388,7 +396,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { fun startClosingEvent_whileNotOnKeyguardAndNotOnLauncher_doesNotTriggerBeforeThreshold() { setKeyguardVisibility(visible = false) setupForegroundActivityType(isHomeActivity = false) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -398,7 +406,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileKeyguardStateNotAvailable_triggerBeforeThreshold() { setKeyguardVisibility(visible = null) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -408,7 +416,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileonKeyguard_doesTriggerBeforeThreshold() { setKeyguardVisibility(visible = true) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -418,9 +426,59 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileNotOnKeyguard_triggersAfterThreshold() { setKeyguardVisibility(visible = false) - sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) + setInitialHingeAngle(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) - sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 1) + sendHingeAngleEvent( + START_CLOSING_ON_APPS_THRESHOLD_DEGREES - + HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startClosingEvent_doesNotTriggerBelowThreshold() { + val thresholdAngle = (FULLY_OPEN_DEGREES - FULLY_OPEN_THRESHOLD_DEGREES).toInt() + setInitialHingeAngle(180) + sendHingeAngleEvent(thresholdAngle + 1) + + assertThat(foldUpdates).isEmpty() + } + + @Test + fun startClosingEvent_triggersAfterThreshold() { + val thresholdAngle = (FULLY_OPEN_DEGREES - FULLY_OPEN_THRESHOLD_DEGREES).toInt() + setInitialHingeAngle(180) + sendHingeAngleEvent(thresholdAngle + 1) + sendHingeAngleEvent(thresholdAngle - 1) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startClosingEvent_triggersAfterThreshold_fromHalfOpen() { + setInitialHingeAngle(120) + sendHingeAngleEvent((120 - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES + 1).toInt()) + assertThat(foldUpdates).isEmpty() + sendHingeAngleEvent((120 - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES - 1).toInt()) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startOpeningAndClosingEvents_triggerWithOpenAndClose() { + setInitialHingeAngle(120) + sendHingeAngleEvent(130) + sendHingeAngleEvent(120) + assertThat(foldUpdates) + .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startClosingEvent_notInterrupted_whenAngleIsSlightlyIncreased() { + setInitialHingeAngle(120) + sendHingeAngleEvent(110) + sendHingeAngleEvent(111) + sendHingeAngleEvent(100) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } @@ -504,11 +562,6 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { } } - private fun simulateFolding(startAngle: Int) { - sendHingeAngleEvent(startAngle) - sendHingeAngleEvent(startAngle - 1) - } - private fun setFoldState(folded: Boolean) { foldProvider.notifyFolded(folded) } @@ -521,6 +574,17 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { testHingeAngleProvider.notifyAngle(angle.toFloat()) } + private fun setInitialHingeAngle(angle: Int) { + setFoldState(angle == 0) + sendHingeAngleEvent(angle) + if (scheduledRunnableDelay != null) { + simulateTimeout() + } + hingeAngleUpdates.clear() + foldUpdates.clear() + unfoldedScreenAvailabilityUpdates.clear() + } + private class TestFoldProvider : FoldProvider { private val callbacks = arrayListOf<FoldCallback>() diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt index a064e8c81076..fbb0e5a72cd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt @@ -57,4 +57,8 @@ class TestFoldStateProvider : FoldStateProvider { fun sendHingeAngleUpdate(angle: Float) { listeners.forEach { it.onHingeAngleUpdate(angle) } } + + fun sendUnfoldedScreenAvailable() { + listeners.forEach { it.onUnfoldedScreenAvailable() } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt index 9312643a1453..e2f3cf7e085f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt @@ -17,10 +17,7 @@ package com.android.systemui.user.data.repository -import android.app.IActivityManager -import android.app.UserSwitchObserver import android.content.pm.UserInfo -import android.os.IRemoteCallback import android.os.UserHandle import android.os.UserManager import android.provider.Settings @@ -44,14 +41,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import org.mockito.ArgumentCaptor -import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.any -import org.mockito.Mockito.anyString import org.mockito.Mockito.mock -import org.mockito.Mockito.times -import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @@ -60,8 +51,6 @@ import org.mockito.MockitoAnnotations class UserRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var manager: UserManager - @Mock private lateinit var activityManager: IActivityManager - @Captor private lateinit var userSwitchObserver: ArgumentCaptor<UserSwitchObserver> private lateinit var underTest: UserRepositoryImpl @@ -235,30 +224,31 @@ class UserRepositoryImplTest : SysuiTestCase() { } @Test - fun userSwitchingInProgress_registersOnlyOneUserSwitchObserver() = runSelfCancelingTest { + fun userSwitchingInProgress_registersUserTrackerCallback() = runSelfCancelingTest { underTest = create(this) underTest.userSwitchingInProgress.launchIn(this) underTest.userSwitchingInProgress.launchIn(this) underTest.userSwitchingInProgress.launchIn(this) - verify(activityManager, times(1)).registerUserSwitchObserver(any(), anyString()) + // Two callbacks registered - one for observing user switching and one for observing the + // selected user + assertThat(tracker.callbacks.size).isEqualTo(2) } @Test - fun userSwitchingInProgress_propagatesStateFromActivityManager() = runSelfCancelingTest { + fun userSwitchingInProgress_propagatesStateFromUserTracker() = runSelfCancelingTest { underTest = create(this) - verify(activityManager) - .registerUserSwitchObserver(userSwitchObserver.capture(), anyString()) + assertThat(tracker.callbacks.size).isEqualTo(2) - userSwitchObserver.value.onUserSwitching(0, mock(IRemoteCallback::class.java)) + tracker.onUserChanging(0) var mostRecentSwitchingValue = false underTest.userSwitchingInProgress.onEach { mostRecentSwitchingValue = it }.launchIn(this) assertThat(mostRecentSwitchingValue).isTrue() - userSwitchObserver.value.onUserSwitchComplete(0) + tracker.onUserChanged(0) assertThat(mostRecentSwitchingValue).isFalse() } @@ -338,7 +328,6 @@ class UserRepositoryImplTest : SysuiTestCase() { backgroundDispatcher = IMMEDIATE, globalSettings = globalSettings, tracker = tracker, - activityManager = activityManager, featureFlags = featureFlags, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt index 9b4f4969f9e9..c2947b42f56d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt @@ -29,6 +29,7 @@ import kotlinx.coroutines.test.runCurrent /** * Collect [flow] in a new [Job] and return a getter for the last collected value. + * * ``` * fun myTest() = runTest { * // ... diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt index 01dac362432d..d4b1701892c7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flowOf class FakeBiometricSettingsRepository : BiometricSettingsRepository { @@ -42,6 +43,9 @@ class FakeBiometricSettingsRepository : BiometricSettingsRepository { override val isFingerprintEnabledByDevicePolicy = _isFingerprintEnabledByDevicePolicy.asStateFlow() + override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean> + get() = flowOf(true) + fun setFingerprintEnrolled(isFingerprintEnrolled: Boolean) { _isFingerprintEnrolled.value = isFingerprintEnrolled } diff --git a/core/java/android/window/IWindowlessStartingSurfaceCallback.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDevicePostureRepository.kt index a0813565b256..914c786a1c7f 100644 --- a/core/java/android/window/IWindowlessStartingSurfaceCallback.aidl +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDevicePostureRepository.kt @@ -11,19 +11,21 @@ * 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 + * limitations under the License. */ -package android.window; +package com.android.systemui.keyguard.data.repository -import android.view.SurfaceControl; +import com.android.systemui.keyguard.shared.model.DevicePosture +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow -/** - * Interface to be invoked when a windowless starting surface added. - * - * @param addedSurface The starting surface. - * {@hide} - */ -interface IWindowlessStartingSurfaceCallback { - void onSurfaceAdded(in SurfaceControl addedSurface); +class FakeDevicePostureRepository : DevicePostureRepository { + private val _currentDevicePosture = MutableStateFlow(DevicePosture.UNKNOWN) + override val currentDevicePosture: Flow<DevicePosture> + get() = _currentDevicePosture + + fun setCurrentPosture(posture: DevicePosture) { + _currentDevicePosture.value = posture + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index 1a371c73550c..194ed02712b2 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -47,6 +47,9 @@ class FakeKeyguardRepository : KeyguardRepository { private val _isKeyguardShowing = MutableStateFlow(false) override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing + private val _isKeyguardUnlocked = MutableStateFlow(false) + override val isKeyguardUnlocked: Flow<Boolean> = _isKeyguardUnlocked + private val _isKeyguardOccluded = MutableStateFlow(false) override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt index eac1bd145033..16442bb525b6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt @@ -37,7 +37,7 @@ class FakeKeyguardTransitionRepository : KeyguardTransitionRepository { _transitions.emit(step) } - override fun startTransition(info: TransitionInfo): UUID? { + override fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean): UUID? { return null } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt index 251014fc50b3..4242c1635468 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt @@ -22,6 +22,7 @@ import android.content.pm.UserInfo import android.os.UserHandle import android.test.mock.MockContentResolver import com.android.systemui.util.mockito.mock +import java.util.concurrent.CountDownLatch import java.util.concurrent.Executor /** A fake [UserTracker] to be used in tests. */ @@ -66,11 +67,19 @@ class FakeUserTracker( _userId = _userInfo.id _userHandle = UserHandle.of(_userId) + onUserChanging() + onUserChanged() + } + + fun onUserChanging(userId: Int = _userId) { + val copy = callbacks.toList() + val latch = CountDownLatch(copy.size) + copy.forEach { it.onUserChanging(userId, userContext, latch) } + } + + fun onUserChanged(userId: Int = _userId) { val copy = callbacks.toList() - copy.forEach { - it.onUserChanging(_userId, userContext) - it.onUserChanged(_userId, userContext) - } + copy.forEach { it.onUserChanged(userId, userContext) } } fun onProfileChanged() { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt new file mode 100644 index 000000000000..0c9ce0f145f1 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.util + +import android.content.DialogInterface +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import org.mockito.ArgumentCaptor +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.verify +import org.mockito.stubbing.Stubber + +class FakeSystemUIDialogController { + + val dialog: SystemUIDialog = mock() + + private val clickListeners: MutableMap<Int, DialogInterface.OnClickListener> = mutableMapOf() + + init { + saveListener(DialogInterface.BUTTON_POSITIVE) + .whenever(dialog) + .setPositiveButton(any(), any()) + saveListener(DialogInterface.BUTTON_POSITIVE) + .whenever(dialog) + .setPositiveButton(any(), any(), any()) + + saveListener(DialogInterface.BUTTON_NEGATIVE) + .whenever(dialog) + .setNegativeButton(any(), any()) + saveListener(DialogInterface.BUTTON_NEGATIVE) + .whenever(dialog) + .setNegativeButton(any(), any(), any()) + + saveListener(DialogInterface.BUTTON_NEUTRAL).whenever(dialog).setNeutralButton(any(), any()) + saveListener(DialogInterface.BUTTON_NEUTRAL) + .whenever(dialog) + .setNeutralButton(any(), any(), any()) + } + + fun clickNegative() { + performClick(DialogInterface.BUTTON_NEGATIVE, "This dialog has no negative button") + } + + fun clickPositive() { + performClick(DialogInterface.BUTTON_POSITIVE, "This dialog has no positive button") + } + + fun clickNeutral() { + performClick(DialogInterface.BUTTON_NEUTRAL, "This dialog has no neutral button") + } + + fun cancel() { + val captor = ArgumentCaptor.forClass(DialogInterface.OnCancelListener::class.java) + verify(dialog).setOnCancelListener(captor.capture()) + captor.value.onCancel(dialog) + } + + private fun performClick(which: Int, errorMessage: String) { + clickListeners + .getOrElse(which) { throw IllegalAccessException(errorMessage) } + .onClick(dialog, which) + } + + private fun saveListener(which: Int): Stubber = doAnswer { + val listener = it.getArgument<DialogInterface.OnClickListener>(1) + clickListeners[which] = listener + Unit + } +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt index 4622464b204d..c437e5c23d1b 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt @@ -21,7 +21,6 @@ import android.util.FloatProperty import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED -import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate import javax.inject.Inject @@ -59,12 +58,15 @@ constructor(private val foldStateProvider: FoldStateProvider) : } override fun onFoldUpdate(@FoldUpdate update: Int) { - when (update) { - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> animator.start() - FOLD_UPDATE_FINISH_CLOSED -> animator.cancel() + if (update == FOLD_UPDATE_FINISH_CLOSED) { + animator.cancel() } } + override fun onUnfoldedScreenAvailable() { + animator.start() + } + override fun addCallback(listener: TransitionProgressListener) { listeners.add(listener) } @@ -73,8 +75,6 @@ constructor(private val foldStateProvider: FoldStateProvider) : listeners.remove(listener) } - override fun onHingeAngleUpdate(angle: Float) {} - private object AnimationProgressProperty : FloatProperty<FixedTimingTransitionProgressProvider>("animation_progress") { diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt index 6ffbe5aa25c0..d19b414cb963 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt @@ -28,7 +28,6 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING -import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener @@ -78,21 +77,11 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( override fun onFoldUpdate(@FoldUpdate update: Int) { when (update) { - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> { - startTransition(startValue = 0f) - - // Stop the animation if the device has already opened by the time when - // the display is available as we won't receive the full open event anymore - if (foldStateProvider.isFinishedOpening) { - cancelTransition(endValue = 1f, animate = true) - } - } FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_FINISH_HALF_OPEN -> { // Do not cancel if we haven't started the transition yet. // This could happen when we fully unfolded the device before the screen // became available. In this case we start and immediately cancel the animation - // in FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE event handler, so we don't need to - // cancel it here. + // in onUnfoldedScreenAvailable event handler, so we don't need to cancel it here. if (isTransitionRunning) { cancelTransition(endValue = 1f, animate = true) } @@ -125,6 +114,16 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( } } + override fun onUnfoldedScreenAvailable() { + startTransition(startValue = 0f) + + // Stop the animation if the device has already opened by the time when + // the display is available as we won't receive the full open event anymore + if (foldStateProvider.isFinishedOpening) { + cancelTransition(endValue = 1f, animate = true) + } + } + private fun cancelTransition(endValue: Float, animate: Boolean) { if (isTransitionRunning && animate) { if (endValue == 1.0f && !isAnimatedCancelRunning) { diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt index 97c9ba99f096..82fd2258120a 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt @@ -54,6 +54,7 @@ constructor( @FoldUpdate private var lastFoldUpdate: Int? = null @FloatRange(from = 0.0, to = 180.0) private var lastHingeAngle: Float = 0f + @FloatRange(from = 0.0, to = 180.0) private var lastHingeAngleBeforeTransition: Float = 0f private val hingeAngleListener = HingeAngleListener() private val screenListener = ScreenStatusListener() @@ -112,29 +113,45 @@ constructor( private fun onHingeAngle(angle: Float) { if (DEBUG) { - Log.d(TAG, "Hinge angle: $angle, lastHingeAngle: $lastHingeAngle") + Log.d( + TAG, + "Hinge angle: $angle, " + + "lastHingeAngle: $lastHingeAngle, " + + "lastHingeAngleBeforeTransition: $lastHingeAngleBeforeTransition" + ) Trace.traceCounter(Trace.TRACE_TAG_APP, "hinge_angle", angle.toInt()) } - val isClosing = angle < lastHingeAngle + val currentDirection = + if (angle < lastHingeAngle) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING + if (isTransitionInProgress && currentDirection != lastFoldUpdate) { + lastHingeAngleBeforeTransition = lastHingeAngle + } + + val isClosing = angle < lastHingeAngleBeforeTransition + val transitionUpdate = + if (isClosing) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING + val angleChangeSurpassedThreshold = + Math.abs(angle - lastHingeAngleBeforeTransition) > HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES - val closingEventDispatched = lastFoldUpdate == FOLD_UPDATE_START_CLOSING + val eventNotAlreadyDispatched = lastFoldUpdate != transitionUpdate val screenAvailableEventSent = isUnfoldHandled - if (isClosing // hinge angle should be decreasing since last update - && !closingEventDispatched // we haven't sent closing event already - && !isFullyOpened // do not send closing event if we are in fully opened hinge + if ( + angleChangeSurpassedThreshold && // Do not react immediately to small changes in angle + eventNotAlreadyDispatched && // we haven't sent transition event already + !isFullyOpened && // do not send transition event if we are in fully opened hinge // angle range as closing threshold could overlap this range - && screenAvailableEventSent // do not send closing event if we are still in - // the process of turning on the inner display - && isClosingThresholdMet(angle) // hinge angle is below certain threshold. + screenAvailableEventSent && // do not send transition event if we are still in the + // process of turning on the inner display + isClosingThresholdMet(angle) // hinge angle is below certain threshold. ) { - notifyFoldUpdate(FOLD_UPDATE_START_CLOSING) + notifyFoldUpdate(transitionUpdate, lastHingeAngle) } if (isTransitionInProgress) { if (isFullyOpened) { - notifyFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) + notifyFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN, angle) cancelTimeout() } else { // The timeout will trigger some constant time after the last angle update. @@ -146,7 +163,7 @@ constructor( outputListeners.forEach { it.onHingeAngleUpdate(angle) } } - private fun isClosingThresholdMet(currentAngle: Float) : Boolean { + private fun isClosingThresholdMet(currentAngle: Float): Boolean { val closingThreshold = getClosingThreshold() return closingThreshold == null || currentAngle < closingThreshold } @@ -179,23 +196,29 @@ constructor( if (isFolded) { hingeAngleProvider.stop() - notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) + notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED, lastHingeAngle) cancelTimeout() isUnfoldHandled = false } else { - notifyFoldUpdate(FOLD_UPDATE_START_OPENING) + notifyFoldUpdate(FOLD_UPDATE_START_OPENING, lastHingeAngle) rescheduleAbortAnimationTimeout() hingeAngleProvider.start() } } } - private fun notifyFoldUpdate(@FoldUpdate update: Int) { + private fun notifyFoldUpdate(@FoldUpdate update: Int, angle: Float) { if (DEBUG) { Log.d(TAG, update.name()) } + val previouslyTransitioning = isTransitionInProgress + outputListeners.forEach { it.onFoldUpdate(update) } lastFoldUpdate = update + + if (previouslyTransitioning != isTransitionInProgress) { + lastHingeAngleBeforeTransition = angle + } } private fun rescheduleAbortAnimationTimeout() { @@ -209,7 +232,8 @@ constructor( handler.removeCallbacks(timeoutRunnable) } - private fun cancelAnimation(): Unit = notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN) + private fun cancelAnimation(): Unit = + notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN, lastHingeAngle) private inner class ScreenStatusListener : ScreenStatusProvider.ScreenListener { @@ -221,7 +245,7 @@ constructor( // receive 'folded' event. If SystemUI started when device is already folded it will // still receive 'folded' event on startup. if (!isFolded && !isUnfoldHandled) { - outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) } + outputListeners.forEach { it.onUnfoldedScreenAvailable() } isUnfoldHandled = true } } @@ -257,7 +281,6 @@ fun @receiver:FoldUpdate Int.name() = when (this) { FOLD_UPDATE_START_OPENING -> "START_OPENING" FOLD_UPDATE_START_CLOSING -> "START_CLOSING" - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> "UNFOLDED_SCREEN_AVAILABLE" FOLD_UPDATE_FINISH_HALF_OPEN -> "FINISH_HALF_OPEN" FOLD_UPDATE_FINISH_FULL_OPEN -> "FINISH_FULL_OPEN" FOLD_UPDATE_FINISH_CLOSED -> "FINISH_CLOSED" @@ -270,5 +293,8 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) /** Threshold after which we consider the device fully unfolded. */ @VisibleForTesting const val FULLY_OPEN_THRESHOLD_DEGREES = 15f +/** Threshold after which hinge angle updates are considered. This is to eliminate noise. */ +@VisibleForTesting const val HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES = 7.5f + /** Fold animation on top of apps only when the angle exceeds this threshold. */ @VisibleForTesting const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60 diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt index c7a8bf336777..0af372f9da24 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt @@ -31,8 +31,9 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> { val isFinishedOpening: Boolean interface FoldUpdatesListener { - fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) - fun onFoldUpdate(@FoldUpdate update: Int) + @JvmDefault fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {} + @JvmDefault fun onFoldUpdate(@FoldUpdate update: Int) {} + @JvmDefault fun onUnfoldedScreenAvailable() {} } @IntDef( @@ -40,7 +41,6 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> { [ FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING, - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE, FOLD_UPDATE_FINISH_HALF_OPEN, FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_FINISH_CLOSED]) @@ -50,7 +50,6 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> { const val FOLD_UPDATE_START_OPENING = 0 const val FOLD_UPDATE_START_CLOSING = 1 -const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 2 -const val FOLD_UPDATE_FINISH_HALF_OPEN = 3 -const val FOLD_UPDATE_FINISH_FULL_OPEN = 4 -const val FOLD_UPDATE_FINISH_CLOSED = 5 +const val FOLD_UPDATE_FINISH_HALF_OPEN = 2 +const val FOLD_UPDATE_FINISH_FULL_OPEN = 3 +const val FOLD_UPDATE_FINISH_CLOSED = 4 diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt index b7bab3e5ed5a..f9751d9c279c 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt @@ -47,6 +47,7 @@ constructor(source: UnfoldTransitionProgressProvider? = null) : /** * Sets the source for the unfold transition progress updates. Replaces current provider if it * is already set + * * @param provider transition provider that emits transition progress updates */ fun setSourceProvider(provider: UnfoldTransitionProgressProvider?) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 25381be88add..7fba72b74e8d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3801,6 +3801,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public boolean registerProxyForDisplay(IAccessibilityServiceClient client, int displayId) throws RemoteException { mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); + mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); if (client == null) { return false; } @@ -3837,6 +3838,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public boolean unregisterProxyForDisplay(int displayId) { mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); + mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); final long identity = Binder.clearCallingIdentity(); try { return mProxyManager.unregisterProxy(displayId); diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java index 21b51b1acef0..607439b79d91 100644 --- a/services/companion/java/com/android/server/companion/virtual/InputController.java +++ b/services/companion/java/com/android/server/companion/virtual/InputController.java @@ -88,7 +88,7 @@ class InputController { */ private static final int DEVICE_NAME_MAX_LENGTH = 80; - final Object mLock; + final Object mLock = new Object(); /* Token -> file descriptor associations. */ @GuardedBy("mLock") @@ -101,18 +101,17 @@ class InputController { private final WindowManager mWindowManager; private final DeviceCreationThreadVerifier mThreadVerifier; - InputController(@NonNull Object lock, @NonNull Handler handler, + InputController(@NonNull Handler handler, @NonNull WindowManager windowManager) { - this(lock, new NativeWrapper(), handler, windowManager, + this(new NativeWrapper(), handler, windowManager, // Verify that virtual devices are not created on the handler thread. () -> !handler.getLooper().isCurrentThread()); } @VisibleForTesting - InputController(@NonNull Object lock, @NonNull NativeWrapper nativeWrapper, + InputController(@NonNull NativeWrapper nativeWrapper, @NonNull Handler handler, @NonNull WindowManager windowManager, @NonNull DeviceCreationThreadVerifier threadVerifier) { - mLock = lock; mHandler = handler; mNativeWrapper = nativeWrapper; mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java index 7804ebf1583d..864fe0f5edc1 100644 --- a/services/companion/java/com/android/server/companion/virtual/SensorController.java +++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java @@ -22,8 +22,11 @@ import android.companion.virtual.sensor.IVirtualSensorCallback; import android.companion.virtual.sensor.VirtualSensor; import android.companion.virtual.sensor.VirtualSensorConfig; import android.companion.virtual.sensor.VirtualSensorEvent; +import android.hardware.SensorDirectChannel; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import android.os.SharedMemory; import android.util.ArrayMap; import android.util.Slog; @@ -36,6 +39,7 @@ import java.io.PrintWriter; import java.util.Iterator; import java.util.Map; import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; /** Controls virtual sensors, including their lifecycle and sensor event dispatch. */ public class SensorController { @@ -47,6 +51,8 @@ public class SensorController { private static final int UNKNOWN_ERROR = (-2147483647 - 1); // INT32_MIN value private static final int BAD_VALUE = -22; + private static AtomicInteger sNextDirectChannelHandle = new AtomicInteger(1); + private final Object mLock; private final int mVirtualDeviceId; @GuardedBy("mLock") @@ -57,8 +63,6 @@ public class SensorController { private final SensorManagerInternal mSensorManagerInternal; private final VirtualDeviceManagerInternal mVdmInternal; - - public SensorController(@NonNull Object lock, int virtualDeviceId, @Nullable IVirtualSensorCallback virtualSensorCallback) { mLock = lock; @@ -97,7 +101,7 @@ public class SensorController { throws SensorCreationException { final int handle = mSensorManagerInternal.createRuntimeSensor(mVirtualDeviceId, config.getType(), config.getName(), - config.getVendor() == null ? "" : config.getVendor(), + config.getVendor() == null ? "" : config.getVendor(), config.getFlags(), mRuntimeSensorCallback); if (handle <= 0) { throw new SensorCreationException("Received an invalid virtual sensor handle."); @@ -212,6 +216,66 @@ public class SensorController { } return OK; } + + @Override + public int onDirectChannelCreated(ParcelFileDescriptor fd) { + if (mCallback == null) { + Slog.e(TAG, "No sensor callback for virtual deviceId " + mVirtualDeviceId); + return BAD_VALUE; + } else if (fd == null) { + Slog.e(TAG, "Received invalid ParcelFileDescriptor"); + return BAD_VALUE; + } + final int channelHandle = sNextDirectChannelHandle.getAndIncrement(); + SharedMemory sharedMemory = SharedMemory.fromFileDescriptor(fd); + try { + mCallback.onDirectChannelCreated(channelHandle, sharedMemory); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to call sensor callback: " + e); + return UNKNOWN_ERROR; + } + return channelHandle; + } + + @Override + public void onDirectChannelDestroyed(int channelHandle) { + if (mCallback == null) { + Slog.e(TAG, "No sensor callback for virtual deviceId " + mVirtualDeviceId); + return; + } + try { + mCallback.onDirectChannelDestroyed(channelHandle); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to call sensor callback: " + e); + } + } + + @Override + public int onDirectChannelConfigured(int channelHandle, int sensorHandle, + @SensorDirectChannel.RateLevel int rateLevel) { + if (mCallback == null) { + Slog.e(TAG, "No runtime sensor callback configured."); + return BAD_VALUE; + } + VirtualSensor sensor = mVdmInternal.getVirtualSensor(mVirtualDeviceId, sensorHandle); + if (sensor == null) { + Slog.e(TAG, "No sensor found for deviceId=" + mVirtualDeviceId + + " and sensor handle=" + sensorHandle); + return BAD_VALUE; + } + try { + mCallback.onDirectChannelConfigured(channelHandle, sensor, rateLevel, sensorHandle); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to call sensor callback: " + e); + return UNKNOWN_ERROR; + } + if (rateLevel == SensorDirectChannel.RATE_STOP) { + return OK; + } else { + // Use the sensor handle as a report token, i.e. a unique identifier of the sensor. + return sensorHandle; + } + } } @VisibleForTesting diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index f650560e6b22..ee1b1fd4a500 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -105,6 +105,14 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private static final String TAG = "VirtualDeviceImpl"; + private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS = + DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC + | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT + | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY + | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL + | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH + | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS; + /** * Timeout until {@link #launchPendingIntent} stops waiting for an activity to be launched. */ @@ -251,7 +259,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mDisplayManager = displayManager; if (inputController == null) { mInputController = new InputController( - mVirtualDeviceLock, context.getMainThreadHandler(), context.getSystemService(WindowManager.class)); } else { @@ -281,7 +288,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub * device. */ int getBaseVirtualDisplayFlags() { - int flags = 0; + int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS; if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) { flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED; } diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 10cd6ac5b0c2..e9a7f205c519 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -192,20 +192,21 @@ public final class BatteryService extends SystemService { private ArrayDeque<Bundle> mBatteryLevelsEventQueue; private long mLastBatteryLevelChangedSentMs; - private Bundle mBatteryChangedOptions = BroadcastOptions.makeRemovingMatchingFilter( - new IntentFilter(Intent.ACTION_BATTERY_CHANGED)).setDeferUntilActive(true) + private Bundle mBatteryChangedOptions = BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .setDeferUntilActive(true) .toBundle(); - private Bundle mPowerConnectedOptions = BroadcastOptions.makeRemovingMatchingFilter( - new IntentFilter(Intent.ACTION_POWER_DISCONNECTED)).setDeferUntilActive(true) + /** Used for both connected/disconnected, so match using key */ + private Bundle mPowerOptions = BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .setDeliveryGroupMatchingKey("android", Intent.ACTION_POWER_CONNECTED) + .setDeferUntilActive(true) .toBundle(); - private Bundle mPowerDisconnectedOptions = BroadcastOptions.makeRemovingMatchingFilter( - new IntentFilter(Intent.ACTION_POWER_CONNECTED)).setDeferUntilActive(true) - .toBundle(); - private Bundle mBatteryLowOptions = BroadcastOptions.makeRemovingMatchingFilter( - new IntentFilter(Intent.ACTION_BATTERY_OKAY)).setDeferUntilActive(true) - .toBundle(); - private Bundle mBatteryOkayOptions = BroadcastOptions.makeRemovingMatchingFilter( - new IntentFilter(Intent.ACTION_BATTERY_LOW)).setDeferUntilActive(true) + /** Used for both low/okay, so match using key */ + private Bundle mBatteryOptions = BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .setDeliveryGroupMatchingKey("android", Intent.ACTION_BATTERY_OKAY) + .setDeferUntilActive(true) .toBundle(); private MetricsLogger mMetricsLogger; @@ -636,7 +637,7 @@ public final class BatteryService extends SystemService { @Override public void run() { mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, - mPowerConnectedOptions); + mPowerOptions); } }); } @@ -648,7 +649,7 @@ public final class BatteryService extends SystemService { @Override public void run() { mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, - mPowerDisconnectedOptions); + mPowerOptions); } }); } @@ -662,7 +663,7 @@ public final class BatteryService extends SystemService { @Override public void run() { mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, - mBatteryLowOptions); + mBatteryOptions); } }); } else if (mSentLowBatteryBroadcast && @@ -675,7 +676,7 @@ public final class BatteryService extends SystemService { @Override public void run() { mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, - mBatteryOkayOptions); + mBatteryOptions); } }); } diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index 2992bf92e0a6..40557f4c12ca 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -350,7 +350,7 @@ public class BinaryTransparencyService extends SystemService { // lastly measure all newly installed MBAs List<IBinaryTransparencyService.AppInfo> allMbaInfo = collectAllSilentInstalledMbaInfo(packagesMeasured); - for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) { + for (IBinaryTransparencyService.AppInfo appInfo : allMbaInfo) { packagesMeasured.putBoolean(appInfo.packageName, true); writeAppInfoToLog(appInfo); } diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index 725ea5c1b3dd..19e5cb142cfd 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -79,6 +79,7 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import java.util.zip.GZIPOutputStream; @@ -105,6 +106,10 @@ public final class DropBoxManagerService extends SystemService { // Size beyond which to force-compress newly added entries. private static final long COMPRESS_THRESHOLD_BYTES = 16_384; + // Tags that we should drop by default. + private static final List<String> DISABLED_BY_DEFAULT_TAGS = + List.of("data_app_wtf", "system_app_wtf", "system_server_wtf"); + // TODO: This implementation currently uses one file per entry, which is // inefficient for smallish entries -- consider using a single queue file // per tag (or even globally) instead. @@ -549,8 +554,13 @@ public final class DropBoxManagerService extends SystemService { public boolean isTagEnabled(String tag) { final long token = Binder.clearCallingIdentity(); try { - return !"disabled".equals(Settings.Global.getString( + if (DISABLED_BY_DEFAULT_TAGS.contains(tag)) { + return "enabled".equals(Settings.Global.getString( + mContentResolver, Settings.Global.DROPBOX_TAG_PREFIX + tag)); + } else { + return !"disabled".equals(Settings.Global.getString( mContentResolver, Settings.Global.DROPBOX_TAG_PREFIX + tag)); + } } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java index 3f1ad3ae0587..2a46d862b991 100644 --- a/services/core/java/com/android/server/IntentResolver.java +++ b/services/core/java/com/android/server/IntentResolver.java @@ -16,18 +16,12 @@ package com.android.server; -import static android.content.IntentFilter.BLOCK_NULL_ACTION_INTENTS; - -import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH; - import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; -import android.os.Binder; -import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.FastImmutableArraySet; @@ -40,7 +34,6 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.util.FastPrintWriter; -import com.android.server.am.ActivityManagerUtils; import com.android.server.pm.Computer; import com.android.server.pm.snapshot.PackageDataSnapshot; @@ -88,7 +81,7 @@ public abstract class IntentResolver<F, R extends Object> { * Returns whether an intent matches the IntentFilter with a pre-resolved type. */ public static boolean intentMatchesFilter( - IntentFilter filter, Intent intent, String resolvedType, boolean blockNullAction) { + IntentFilter filter, Intent intent, String resolvedType) { final boolean debug = localLOGV || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); @@ -102,8 +95,7 @@ public abstract class IntentResolver<F, R extends Object> { } final int match = filter.match(intent.getAction(), resolvedType, intent.getScheme(), - intent.getData(), intent.getCategories(), TAG, /* supportWildcards */ false, - blockNullAction, null, null); + intent.getData(), intent.getCategories(), TAG); if (match >= 0) { if (debug) { @@ -358,32 +350,14 @@ public abstract class IntentResolver<F, R extends Object> { return Collections.unmodifiableSet(mFilters); } - private boolean blockNullAction(Computer computer, Intent intent, - String resolvedType, int callingUid, boolean debug) { - if (intent.getAction() == null) { - final boolean blockNullAction = UserHandle.isCore(callingUid) - || computer.isChangeEnabled(BLOCK_NULL_ACTION_INTENTS, callingUid); - ActivityManagerUtils.logUnsafeIntentEvent( - UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH, - callingUid, intent, resolvedType, blockNullAction); - if (blockNullAction) { - if (debug) Slog.v(TAG, "Skip matching filters: action is null"); - return true; - } - } - return false; - } - public List<R> queryIntentFromList(@NonNull Computer computer, Intent intent, - String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, - int callingUid, @UserIdInt int userId, long customFlags) { + String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, int userId, + long customFlags) { ArrayList<R> resultList = new ArrayList<R>(); final boolean debug = localLOGV || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); - if (blockNullAction(computer, intent, resolvedType, callingUid, debug)) return resultList; - FastImmutableArraySet<String> categories = getFastIntentCategories(intent); final String scheme = intent.getScheme(); int N = listCut.size(); @@ -391,26 +365,18 @@ public abstract class IntentResolver<F, R extends Object> { buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType, scheme, listCut.get(i), resultList, userId, customFlags); } - filterResults(computer, intent, resultList); + filterResults(resultList); sortResults(resultList); return resultList; } - public final List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, - String resolvedType, boolean defaultOnly, @UserIdInt int userId) { - return queryIntent(snapshot, intent, resolvedType, defaultOnly, - Binder.getCallingUid(), userId, 0); - } - public List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, - String resolvedType, boolean defaultOnly, int callingUid, @UserIdInt int userId) { - return queryIntent(snapshot, intent, resolvedType, defaultOnly, callingUid, userId, 0); + String resolvedType, boolean defaultOnly, @UserIdInt int userId) { + return queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, 0); } protected final List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, - String resolvedType, boolean defaultOnly, int callingUid, @UserIdInt int userId, - long customFlags) { - final Computer computer = (Computer) snapshot; + String resolvedType, boolean defaultOnly, @UserIdInt int userId, long customFlags) { String scheme = intent.getScheme(); ArrayList<R> finalList = new ArrayList<R>(); @@ -422,8 +388,6 @@ public abstract class IntentResolver<F, R extends Object> { TAG, "Resolving type=" + resolvedType + " scheme=" + scheme + " defaultOnly=" + defaultOnly + " userId=" + userId + " of " + intent); - if (blockNullAction(computer, intent, resolvedType, callingUid, debug)) return finalList; - F[] firstTypeCut = null; F[] secondTypeCut = null; F[] thirdTypeCut = null; @@ -484,6 +448,7 @@ public abstract class IntentResolver<F, R extends Object> { } FastImmutableArraySet<String> categories = getFastIntentCategories(intent); + Computer computer = (Computer) snapshot; if (firstTypeCut != null) { buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType, scheme, firstTypeCut, finalList, userId, customFlags); @@ -500,7 +465,7 @@ public abstract class IntentResolver<F, R extends Object> { buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType, scheme, schemeCut, finalList, userId, customFlags); } - filterResults(computer, intent, finalList); + filterResults(finalList); sortResults(finalList); if (debug) { @@ -569,8 +534,7 @@ public abstract class IntentResolver<F, R extends Object> { /** * Apply filtering to the results. This happens before the results are sorted. */ - protected void filterResults(@NonNull Computer computer, @NonNull Intent intent, - List<R> results) { + protected void filterResults(List<R> results) { } protected void dumpFilter(PrintWriter out, String prefix, F filter) { @@ -802,11 +766,7 @@ public abstract class IntentResolver<F, R extends Object> { continue; } - match = intentFilter.match(action, resolvedType, scheme, data, categories, TAG, - false /*supportWildcards*/, - false /*blockNullAction: already handled*/, - null /*ignoreActions*/, - null /*extras*/); + match = intentFilter.match(action, resolvedType, scheme, data, categories, TAG); if (match >= 0) { if (debug) Slog.v(TAG, " Filter matched! match=0x" + Integer.toHexString(match) + " hasDefault=" diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index c16314b6a117..225afea3d1b7 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -38,6 +38,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationManager; @@ -195,6 +196,9 @@ public class AccountManagerService private final IAccountAuthenticatorCache mAuthenticatorCache; private static final String PRE_N_DATABASE_NAME = "accounts.db"; private static final Intent ACCOUNTS_CHANGED_INTENT; + private static final Bundle ACCOUNTS_CHANGED_OPTIONS = new BroadcastOptions() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .toBundle(); private static final int SIGNATURE_CHECK_MISMATCH = 0; private static final int SIGNATURE_CHECK_MATCH = 1; @@ -1075,7 +1079,8 @@ public class AccountManagerService Log.i(TAG, "the accountType= " + (accountType == null ? "" : accountType) + " changed with useCase=" + useCase + " for userId=" + userId + ", sending broadcast of " + ACCOUNTS_CHANGED_INTENT.getAction()); - mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId)); + mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId), + null /* receiverPermission */, ACCOUNTS_CHANGED_OPTIONS); } private void sendAccountRemovedBroadcast( diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 70304c55067e..f90a3ce76e71 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -137,6 +137,7 @@ import android.app.PendingIntent; import android.app.RemoteServiceException.ForegroundServiceDidNotStartInTimeException; import android.app.Service; import android.app.ServiceStartArgs; +import android.app.StartForegroundCalledOnStoppedServiceException; import android.app.admin.DevicePolicyEventLogger; import android.app.compat.CompatChanges; import android.app.usage.UsageEvents; @@ -800,13 +801,17 @@ public final class ActiveServices { ? res.permission : "private to package"); } - - // TODO(short-service): This is inside startService() / startForegroundService(). - // Consider if there's anything special we have to do if these are called on an already- - // running short-FGS... But given these APIs shouldn't change the FGS type, we likely - // don't need to do anything. (If they would change the FGS type, we'd have to stop - // the timeout) ServiceRecord r = res.record; + // Note, when startService() or startForegroundService() is called on an already + // running SHORT_SERVICE FGS, the call will succeed (i.e. we won't throw + // ForegroundServiceStartNotAllowedException), even when the service is alerady timed + // out. This is because these APIs will essnetially only change the "started" state + // of the service, and it won't afect "the foreground-ness" of the service, or the type + // of the FGS. + // However, this call will still _not_ extend the SHORT_SERVICE timeout either. + // Also, if the app tries to change the type of the FGS later (using + // Service.startForeground()), at that point we will consult the BFSL check and the timeout + // and make the necessary decisions. setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r, userId, backgroundStartPrivileges, false /* isBindService */); @@ -2035,8 +2040,7 @@ public final class ActiveServices { // suddenly disallow it. // However, this would be very problematic if used with a short-FGS, so we // explicitly disallow this combination. - // TODO(short-service): Change to another exception type? - throw new IllegalStateException( + throw new StartForegroundCalledOnStoppedServiceException( "startForeground(SHORT_SERVICE) called on a service that's not" + " started."); } @@ -2241,10 +2245,6 @@ public final class ActiveServices { cancelForegroundNotificationLocked(r); r.foregroundId = id; } - - // TODO(short-service): Stop the short service timeout, if the type is changing - // from short to non-short. (should we do it earlier?) - notification.flags |= Notification.FLAG_FOREGROUND_SERVICE; r.foregroundNoti = notification; r.foregroundServiceType = foregroundServiceType; @@ -3190,7 +3190,7 @@ public final class ActiveServices { try { sr.app.getThread().scheduleTimeoutService(sr, sr.getShortFgsInfo().getStartId()); } catch (RemoteException e) { - // TODO(short-service): Anything to do here? + Slog.w(TAG_SERVICE, "Exception from scheduleTimeoutService: " + e.toString()); } // Schedule the procstate demotion timeout and ANR timeout. { @@ -3262,8 +3262,9 @@ public final class ActiveServices { } mAm.appNotResponding(sr.app, tr); - // TODO(short-service): Make sure, if the FGS stops after this, the ANR dialog - // disappears. + // TODO: Can we close the ANR dialog here, if it's still shown? Currently, the ANR + // dialog really doesn't remember the "cause" (especially if there have been multiple + // ANRs), so it's not doable. } } @@ -3278,8 +3279,8 @@ public final class ActiveServices { } } - // TODO(short-service): Hmm what is it? Should we stop the timeout here? private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) { + maybeStopShortFgsTimeoutLocked(service); final ProcessServiceRecord psr = service.app.mServices; psr.stopService(service); psr.updateBoundClientUids(); @@ -5406,8 +5407,6 @@ public final class ActiveServices { // Check to see if the service had been started as foreground, but being // brought down before actually showing a notification. That is not allowed. - // TODO(short-service): This is unlikely related to short-FGS, but I'm curious why it's - // not allowed. Look into it. if (r.fgRequired) { Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: " + r); @@ -5478,6 +5477,7 @@ public final class ActiveServices { cancelForegroundNotificationLocked(r); final boolean exitingFg = r.isForeground; if (exitingFg) { + maybeStopShortFgsTimeoutLocked(r); decActiveForegroundAppLocked(smap, r); synchronized (mAm.mProcessStats.mLock) { ServiceState stracker = r.getTracker(); @@ -5501,8 +5501,6 @@ public final class ActiveServices { mFGSLogger.logForegroundServiceStop(r.appInfo.uid, r); } mAm.updateForegroundServiceUsageStats(r.name, r.userId, false); - - // TODO(short-service): Make sure we stop the timeout by here. } r.isForeground = false; @@ -7822,7 +7820,7 @@ public final class ActiveServices { final int callerTargetSdkVersion = r.mRecentCallerApplicationInfo != null ? r.mRecentCallerApplicationInfo.targetSdkVersion : 0; - // TODO(short-service): Log BFSL too. + // TODO(short-service): Log the UID capabilities (for BFSL) too, and also the procstate? FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortInstanceName, @@ -7872,7 +7870,8 @@ public final class ActiveServices { r.mFgsNotificationShown ? 1 : 0, durationMs, r.mStartForegroundCount, - fgsStopReasonToString(fgsStopReason)); + fgsStopReasonToString(fgsStopReason), + r.foregroundServiceType); } private void updateNumForegroundServicesLocked() { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 4a134eef8532..a1f7445d4142 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -32,7 +32,6 @@ import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS; import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART; import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY; import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; -import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.ActivityManager.PROCESS_STATE_TOP; @@ -1128,19 +1127,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - protected void filterResults(@NonNull Computer computer, - @NonNull Intent intent, List<BroadcastFilter> results) { - if (intent.getAction() != null) return; - // When the resolved component is targeting U+, block null action intents - for (int i = results.size() - 1; i >= 0; --i) { - if (computer.isChangeEnabled( - IntentFilter.BLOCK_NULL_ACTION_INTENTS, results.get(i).owningUid)) { - results.remove(i); - } - } - } - - @Override protected IntentFilter getIntentFilter(@NonNull BroadcastFilter input) { return input; } @@ -3341,7 +3327,6 @@ public class ActivityManagerService extends IActivityManager.Stub } mBatteryStatsService.noteProcessDied(app.info.uid, pid); - mOomAdjuster.updateShortFgsOwner(app.info.uid, pid, false); if (!app.isKilled()) { if (!fromBinderDied) { @@ -3537,7 +3522,7 @@ public class ActivityManagerService extends IActivityManager.Stub // We'll take the stack crawls of just the top apps using CPU. final int workingStatsNumber = processCpuTracker.countWorkingStats(); - for (int i = 0; i < workingStatsNumber && extraPids.size() < 5; i++) { + for (int i = 0; i < workingStatsNumber && extraPids.size() < 2; i++) { ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i); if (lastPids.indexOfKey(stats.pid) >= 0) { if (DEBUG_ANR) { @@ -7015,36 +7000,6 @@ public class ActivityManagerService extends IActivityManager.Stub } /** - * Allows apps to retrieve the MIME type of a URI. - * If an app is in the same user as the ContentProvider, or if it is allowed to interact across - * users, then it does not need permission to access the ContentProvider. - * Either, it needs cross-user uri grants. - * - * CTS tests for this functionality can be run with "runtest cts-appsecurity". - * - * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/ - * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java - * - * @deprecated -- use getProviderMimeTypeAsync. - */ - @Deprecated - @Override - public String getProviderMimeType(Uri uri, int userId) { - return mCpHelper.getProviderMimeType(uri, userId); - } - - /** - * Allows apps to retrieve the MIME type of a URI. - * If an app is in the same user as the ContentProvider, or if it is allowed to interact across - * users, then it does not need permission to access the ContentProvider. - * Either way, it needs cross-user uri grants. - */ - @Override - public void getProviderMimeTypeAsync(Uri uri, int userId, RemoteCallback resultCallback) { - mCpHelper.getProviderMimeTypeAsync(uri, userId, resultCallback); - } - - /** * Filters calls to getType based on permission. If the caller has required permission, * then it returns the contentProvider#getType. * Else, it returns the contentProvider#getTypeAnonymous, which does not @@ -13978,19 +13933,11 @@ public class ActivityManagerService extends IActivityManager.Stub (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) { continue; } - - final boolean blockNullAction = mPlatformCompat.isChangeEnabledInternal( - IntentFilter.BLOCK_NULL_ACTION_INTENTS, callerApp.info); // If intent has scheme "content", it will need to access // provider that needs to lock mProviderMap in ActivityThread // and also it may need to wait application response, so we // cannot lock ActivityManagerService here. - if (filter.match(intent.getAction(), intent.resolveType(resolver), - intent.getScheme(), intent.getData(), intent.getCategories(), TAG, - false /* supportWildcards */, - blockNullAction, - null /* ignoreActions */, - intent.getExtras()) >= 0) { + if (filter.match(resolver, intent, true, TAG) >= 0) { if (allSticky == null) { allSticky = new ArrayList<Intent>(); } @@ -15009,7 +14956,7 @@ public class ActivityManagerService extends IActivityManager.Stub } List<BroadcastFilter> registeredReceiversForUser = mReceiverResolver.queryIntent(snapshot, intent, - resolvedType, false /*defaultOnly*/, callingUid, users[i]); + resolvedType, false /*defaultOnly*/, users[i]); if (registeredReceivers == null) { registeredReceivers = registeredReceiversForUser; } else if (registeredReceiversForUser != null) { @@ -15018,7 +14965,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } else { registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent, - resolvedType, false /*defaultOnly*/, callingUid, userId); + resolvedType, false /*defaultOnly*/, userId); } } BroadcastQueue.traceEnd(cookie); @@ -18209,8 +18156,9 @@ public class ActivityManagerService extends IActivityManager.Stub bOptions.setTemporaryAppAllowlist(mInternal.getBootTimeTempAllowListDuration(), TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, PowerExemptionManager.REASON_LOCALE_CHANGED, ""); - bOptions.setRemoveMatchingFilter( - new IntentFilter(Intent.ACTION_LOCALE_CHANGED)); + bOptions.setDeliveryGroupPolicy( + BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT); + bOptions.setDeferUntilActive(true); broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, null, null, OP_NONE, bOptions.toBundle(), false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), @@ -18769,23 +18717,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public boolean canHoldWakeLocksInDeepDoze(int uid, int procstate) { - // This method is called with the PowerManager lock held. Do not hold AM here. - - // If the procstate is high enough, it's always allowed. - if (procstate <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { - return true; - } - // IF it's too low, it's not allowed. - if (procstate > PROCESS_STATE_IMPORTANT_FOREGROUND) { - return false; - } - // If it's PROCESS_STATE_IMPORTANT_FOREGROUND, then we allow it only wheen the UID - // has a SHORT_FGS. - return mOomAdjuster.hasUidShortForegroundService(uid); - } - - @Override public boolean startProfileEvenWhenDisabled(@UserIdInt int userId) { return mUserController.startProfile(userId, /* evenWhenDisabled= */ true, /* unlockListener= */ null); diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 72d6ca9fd761..4c1835eb80f8 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -146,6 +146,7 @@ import java.util.Locale; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; @@ -595,10 +596,14 @@ final class ActivityManagerShellCommand extends ShellCommand { return 1; } - String mimeType = intent.getType(); - if (mimeType == null && intent.getData() != null + AtomicReference<String> mimeType = new AtomicReference<>(intent.getType()); + + if (mimeType.get() == null && intent.getData() != null && "content".equals(intent.getData().getScheme())) { - mimeType = mInterface.getProviderMimeType(intent.getData(), mUserId); + mInterface.getMimeTypeFilterAsync(intent.getData(), mUserId, + new RemoteCallback(result -> { + mimeType.set(result.getPairValue()); + })); } do { @@ -611,8 +616,8 @@ final class ActivityManagerShellCommand extends ShellCommand { int userIdForQuery = mInternal.mUserController.handleIncomingUser( Binder.getCallingPid(), Binder.getCallingUid(), mUserId, false, ALLOW_NON_FULL, "ActivityManagerShellCommand", null); - List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType, 0, - userIdForQuery).getList(); + List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType.get(), + 0, userIdForQuery).getList(); if (activities == null || activities.size() <= 0) { getErrPrintWriter().println("Error: Intent does not match any activities: " + intent); @@ -708,12 +713,12 @@ final class ActivityManagerShellCommand extends ShellCommand { } if (mWaitOption) { result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, null, intent, - mimeType, null, null, 0, mStartFlags, profilerInfo, + mimeType.get(), null, null, 0, mStartFlags, profilerInfo, options != null ? options.toBundle() : null, mUserId); res = result.result; } else { res = mInternal.startActivityAsUserWithFeature(null, SHELL_PACKAGE_NAME, null, - intent, mimeType, null, null, 0, mStartFlags, profilerInfo, + intent, mimeType.get(), null, null, 0, mStartFlags, profilerInfo, options != null ? options.toBundle() : null, mUserId); } final long endTime = SystemClock.uptimeMillis(); diff --git a/services/core/java/com/android/server/am/ActivityManagerUtils.java b/services/core/java/com/android/server/am/ActivityManagerUtils.java index 01466b845a61..9be553c49a35 100644 --- a/services/core/java/com/android/server/am/ActivityManagerUtils.java +++ b/services/core/java/com/android/server/am/ActivityManagerUtils.java @@ -17,13 +17,11 @@ package com.android.server.am; import android.app.ActivityThread; import android.content.ContentResolver; -import android.content.Intent; import android.provider.Settings; import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FrameworkStatsLog; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -135,25 +133,4 @@ public class ActivityManagerUtils { public static int hashComponentNameForAtom(String shortInstanceName) { return getUnsignedHashUnCached(shortInstanceName) ^ getAndroidIdHash(); } - - /** - * Helper method to log an unsafe intent event. - */ - public static void logUnsafeIntentEvent(int event, int callingUid, - Intent intent, String resolvedType, boolean blocked) { - String[] categories = intent.getCategories() == null ? new String[0] - : intent.getCategories().toArray(String[]::new); - String component = intent.getComponent() == null ? null - : intent.getComponent().flattenToString(); - FrameworkStatsLog.write(FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED, - event, - callingUid, - component, - intent.getPackage(), - intent.getAction(), - categories, - resolvedType, - intent.getScheme(), - blocked); - } } diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index b942f4b96b21..841b61e8e81f 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -597,18 +597,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue { final int cookie = traceBegin("enqueueBroadcast"); r.applySingletonPolicy(mService); - final IntentFilter removeMatchingFilter = (r.options != null) - ? r.options.getRemoveMatchingFilter() : null; - if (removeMatchingFilter != null) { - final Predicate<Intent> removeMatching = removeMatchingFilter.asPredicate(); - forEachMatchingBroadcast(QUEUE_PREDICATE_ANY, (testRecord, testIndex) -> { - // We only allow caller to remove broadcasts they enqueued - return (r.callingUid == testRecord.callingUid) - && (r.userId == testRecord.userId) - && removeMatching.test(testRecord.intent); - }, mBroadcastConsumerSkipAndCanceled, true); - } - applyDeliveryGroupPolicy(r); r.enqueueTime = SystemClock.uptimeMillis(); @@ -909,6 +897,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue { final IApplicationThread thread = app.getOnewayThread(); if (thread != null) { try { + if (r.shareIdentity) { + mService.mPackageManagerInt.grantImplicitAccess(r.userId, r.intent, + UserHandle.getAppId(app.uid), r.callingUid, true); + } if (receiver instanceof BroadcastFilter) { notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver); thread.scheduleRegisteredReceiver( diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index f721d69958f5..48df1494fbe8 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -969,122 +969,6 @@ public class ContentProviderHelper { } /** - * Allows apps to retrieve the MIME type of a URI. - * If an app is in the same user as the ContentProvider, or if it is allowed to interact across - * users, then it does not need permission to access the ContentProvider. - * Either, it needs cross-user uri grants. - * - * CTS tests for this functionality can be run with "runtest cts-appsecurity". - * - * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/ - * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java - * - * @deprecated -- use getProviderMimeTypeAsync. - */ - @Deprecated - String getProviderMimeType(Uri uri, int userId) { - mService.enforceNotIsolatedCaller("getProviderMimeType"); - final String name = uri.getAuthority(); - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); - final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId); - final long ident = canClearIdentity(callingPid, callingUid, safeUserId) - ? Binder.clearCallingIdentity() : 0; - final ContentProviderHolder holder; - try { - holder = getContentProviderExternalUnchecked(name, null /* token */, callingUid, - "*getmimetype*", safeUserId); - } finally { - if (ident != 0) { - Binder.restoreCallingIdentity(ident); - } - } - try { - if (isHolderVisibleToCaller(holder, callingUid, safeUserId)) { - final IBinder providerConnection = holder.connection; - final ComponentName providerName = holder.info.getComponentName(); - // Note: creating a new Runnable instead of using a lambda here since lambdas in - // java provide no guarantee that there will be a new instance returned every call. - // Hence, it's possible that a cached copy is returned and the ANR is executed on - // the incorrect provider. - final Runnable providerNotResponding = new Runnable() { - @Override - public void run() { - Log.w(TAG, "Provider " + providerName + " didn't return from getType()."); - appNotRespondingViaProvider(providerConnection); - } - }; - mService.mHandler.postDelayed(providerNotResponding, 1000); - try { - final String type = holder.provider.getType(uri); - return type; - } finally { - mService.mHandler.removeCallbacks(providerNotResponding); - // We need to clear the identity to call removeContentProviderExternalUnchecked - final long token = Binder.clearCallingIdentity(); - try { - removeContentProviderExternalUnchecked(name, null /* token */, safeUserId); - } finally { - Binder.restoreCallingIdentity(token); - } - } - } - } catch (RemoteException e) { - Log.w(TAG, "Content provider dead retrieving " + uri, e); - return null; - } catch (Exception e) { - Log.w(TAG, "Exception while determining type of " + uri, e); - return null; - } - - return null; - } - - /** - * Allows apps to retrieve the MIME type of a URI. - * If an app is in the same user as the ContentProvider, or if it is allowed to interact across - * users, then it does not need permission to access the ContentProvider. - * Either way, it needs cross-user uri grants. - */ - void getProviderMimeTypeAsync(Uri uri, int userId, RemoteCallback resultCallback) { - mService.enforceNotIsolatedCaller("getProviderMimeTypeAsync"); - final String name = uri.getAuthority(); - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); - final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId); - final long ident = canClearIdentity(callingPid, callingUid, safeUserId) - ? Binder.clearCallingIdentity() : 0; - final ContentProviderHolder holder; - try { - holder = getContentProviderExternalUnchecked(name, null /* token */, callingUid, - "*getmimetype*", safeUserId); - } finally { - if (ident != 0) { - Binder.restoreCallingIdentity(ident); - } - } - - try { - if (isHolderVisibleToCaller(holder, callingUid, safeUserId)) { - holder.provider.getTypeAsync(uri, new RemoteCallback(result -> { - final long identity = Binder.clearCallingIdentity(); - try { - removeContentProviderExternalUnchecked(name, null, safeUserId); - } finally { - Binder.restoreCallingIdentity(identity); - } - resultCallback.sendResult(result); - })); - } else { - resultCallback.sendResult(Bundle.EMPTY); - } - } catch (RemoteException e) { - Log.w(TAG, "Content provider dead retrieving " + uri, e); - resultCallback.sendResult(Bundle.EMPTY); - } - } - - /** * Filters calls to getType based on permission. If the caller has required permission, * then it returns the contentProvider#getType. * Else, it returns the contentProvider#getTypeAnonymous, which does not diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index 1534ff5651de..50841ae4488c 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -122,9 +122,9 @@ option java_package com.android.server.am 30091 um_user_visibility_changed (userId|1|5),(visible|1) # Foreground service start/stop events. -30100 am_foreground_service_start (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3) -30101 am_foreground_service_denied (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3) -30102 am_foreground_service_stop (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3) +30100 am_foreground_service_start (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1) +30101 am_foreground_service_denied (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1) +30102 am_foreground_service_stop (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1) # Intent Sender redirect for UserHandle.USER_CURRENT 30110 am_intent_sender_redirect_user (userId|1|5) diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 0c366268604d..679cf0a5694b 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -127,7 +127,6 @@ import android.os.Trace; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; -import android.util.SparseSetArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.CompositeRWLock; @@ -207,7 +206,8 @@ public class OomAdjuster { return AppProtoEnums.OOM_ADJ_REASON_PROCESS_BEGIN; case OOM_ADJ_REASON_PROCESS_END: return AppProtoEnums.OOM_ADJ_REASON_PROCESS_END; - case OOM_ADJ_REASON_SHORT_FGS_TIMEOUT: // TODO(short-service) add value to AppProtoEnums + case OOM_ADJ_REASON_SHORT_FGS_TIMEOUT: + return AppProtoEnums.OOM_ADJ_REASON_SHORT_FGS_TIMEOUT; default: return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO; } @@ -366,19 +366,6 @@ public class OomAdjuster { @GuardedBy("mService") private boolean mPendingFullOomAdjUpdate = false; - /** - * PIDs that has a SHORT_SERVICE. We need to access it with the PowerManager lock held, - * so we use a fine-grained lock here. - */ - @GuardedBy("mPidsWithShortFgs") - private final ArraySet<Integer> mPidsWithShortFgs = new ArraySet<>(); - - /** - * UIDs -> PIDs map, used with mPidsWithShortFgs. - */ - @GuardedBy("mPidsWithShortFgs") - private final SparseSetArray<Integer> mUidsToPidsWithShortFgs = new SparseSetArray<>(); - /** Overrideable by a test */ @VisibleForTesting protected boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId, @@ -1955,7 +1942,6 @@ public class OomAdjuster { } } } - updateShortFgsOwner(psr.mApp.uid, psr.mApp.mPid, hasShortForegroundServices); // If the app was recently in the foreground and moved to a foreground service status, // allow it to get a higher rank in memory for some time, compared to other foreground @@ -2184,8 +2170,6 @@ public class OomAdjuster { } } - // TODO(short-service): While-in-user permissions. Do we need any change here for - // short-FGS? (Likely not) if (s.isForeground) { final int fgsType = s.foregroundServiceType; if (s.mAllowWhileInUsePermissionInFgs) { @@ -3467,40 +3451,4 @@ public class OomAdjuster { mCachedAppOptimizer.unfreezeAppLSP(app, oomAdjReason); } } - - /** - * Update {@link #mPidsWithShortFgs} and {@link #mUidsToPidsWithShortFgs} to keep track - * of which UID/PID has a short FGS. - * - * TODO(short-FGS): Remove it and all the relevant code once SHORT_FGS use the FGS procstate. - */ - void updateShortFgsOwner(int uid, int pid, boolean add) { - synchronized (mPidsWithShortFgs) { - if (add) { - mUidsToPidsWithShortFgs.add(uid, pid); - mPidsWithShortFgs.add(pid); - } else { - mUidsToPidsWithShortFgs.remove(uid, pid); - mPidsWithShortFgs.remove(pid); - } - } - } - - /** - * Whether a UID has a (non-timed-out) short FGS or not. - * It's indirectly called by PowerManager, so we can't hold the AM lock in it. - */ - boolean hasUidShortForegroundService(int uid) { - synchronized (mPidsWithShortFgs) { - final ArraySet<Integer> pids = mUidsToPidsWithShortFgs.get(uid); - if (pids == null || pids.size() == 0) { - return false; - } - for (int i = pids.size() - 1; i >= 0; i--) { - final int pid = pids.valueAt(i); - return mPidsWithShortFgs.contains(pid); - } - } - return false; - } } diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING index c6a8bcd2fb03..d4bcd9e9c66e 100644 --- a/services/core/java/com/android/server/am/TEST_MAPPING +++ b/services/core/java/com/android/server/am/TEST_MAPPING @@ -106,9 +106,7 @@ { "exclude-annotation": "androidx.test.filters.FlakyTest" }, { "exclude-annotation": "org.junit.Ignore" } ] - } - ], - "presubmit-large": [ + }, { "name": "CtsUsageStatsTestCases", "file_patterns": [ diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 45181e83e752..0d0e5764b522 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1315,7 +1315,7 @@ public class AudioService extends IAudioService.Stub // persistent data initVolumeGroupStates(); - mSoundDoseHelper.initSafeUsbMediaVolumeIndex(); + mSoundDoseHelper.initSafeMediaVolumeIndex(); // Link VGS on VSS initVolumeStreamStates(); @@ -8837,7 +8837,7 @@ public class AudioService extends IAudioService.Stub final VolumeStreamState streamState = mStreamStates[update.mStreamType]; if (update.hasVolumeIndex()) { int index = update.getVolumeIndex(); - if (!mSoundDoseHelper.checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) { + if (mSoundDoseHelper.checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) { index = mSoundDoseHelper.safeMediaVolumeIndex(update.mDevice); } streamState.setIndex(index, update.mDevice, update.mCaller, diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index 4b302345eb08..cf81dbe08182 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -52,10 +52,9 @@ import com.android.server.utils.EventLogger; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; +import java.util.HashMap; import java.util.List; import java.util.Objects; -import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -80,7 +79,6 @@ public class SoundDoseHelper { // SAFE_MEDIA_VOLUME_DISABLED according to country option. If not SAFE_MEDIA_VOLUME_DISABLED, it // can be set to SAFE_MEDIA_VOLUME_INACTIVE by calling AudioService.disableSafeMediaVolume() // (when user opts out). - // Note: when CSD calculation is enabled the state is set to SAFE_MEDIA_VOLUME_DISABLED private static final int SAFE_MEDIA_VOLUME_NOT_CONFIGURED = 0; private static final int SAFE_MEDIA_VOLUME_DISABLED = 1; private static final int SAFE_MEDIA_VOLUME_INACTIVE = 2; // confirmed @@ -94,6 +92,8 @@ public class SoundDoseHelper { private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours + private static final int MOMENTARY_EXPOSURE_TIMEOUT_MS = (20 * 3600 * 1000); // 20 hours + // 30s after boot completed private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; @@ -127,30 +127,50 @@ public class SoundDoseHelper { // mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property private int mSafeMediaVolumeIndex; - // mSafeUsbMediaVolumeDbfs is the cached value of the config_safe_media_volume_usb_mB + // mSafeMediaVolumeDbfs is the cached value of the config_safe_media_volume_usb_mB // property, divided by 100.0. - private float mSafeUsbMediaVolumeDbfs; - - // mSafeUsbMediaVolumeIndex is used for USB Headsets and is the music volume UI index - // corresponding to a gain of mSafeUsbMediaVolumeDbfs (defaulting to -37dB) in audio - // flinger mixer. - // We remove -22 dBs from the theoretical -15dB to account for the EQ + bass boost - // amplification when both effects are on with all band gains at maximum. - // This level corresponds to a loudness of 85 dB SPL for the warning to be displayed when - // the headset is compliant to EN 60950 with a max loudness of 100dB SPL. - private int mSafeUsbMediaVolumeIndex; - // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced, - private final Set<Integer> mSafeMediaVolumeDevices = new HashSet<>( - Arrays.asList(AudioSystem.DEVICE_OUT_WIRED_HEADSET, - AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, AudioSystem.DEVICE_OUT_USB_HEADSET)); - - private final Set<Integer> mSafeMediaCsdDevices = new HashSet<>( - Arrays.asList(AudioSystem.DEVICE_OUT_WIRED_HEADSET, - AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, AudioSystem.DEVICE_OUT_USB_HEADSET, - AudioSystem.DEVICE_OUT_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_BROADCAST, - AudioSystem.DEVICE_OUT_HEARING_AID, - AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, - AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); + // For now using the same value for CSD supported devices + private float mSafeMediaVolumeDbfs; + + private static class SafeDeviceVolumeInfo { + int mDeviceType; + int mSafeVolumeIndex = -1; + + SafeDeviceVolumeInfo(int deviceType) { + mDeviceType = deviceType; + } + } + + /** + * mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced. + * Contains a safe volume index for a given device type. + * Indexes are used for headsets and is the music volume UI index + * corresponding to a gain of mSafeMediaVolumeDbfs (defaulting to -37dB) in audio + * flinger mixer. + * We remove -22 dBs from the theoretical -15dB to account for the EQ + bass boost + * amplification when both effects are on with all band gains at maximum. + * This level corresponds to a loudness of 85 dB SPL for the warning to be displayed when + * the headset is compliant to EN 60950 with a max loudness of 100dB SPL. + */ + private final HashMap<Integer, SafeDeviceVolumeInfo> mSafeMediaVolumeDevices = + new HashMap<>() {{ + put(AudioSystem.DEVICE_OUT_WIRED_HEADSET, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_WIRED_HEADSET)); + put(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE)); + put(AudioSystem.DEVICE_OUT_USB_HEADSET, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_USB_HEADSET)); + put(AudioSystem.DEVICE_OUT_BLE_HEADSET, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLE_HEADSET)); + put(AudioSystem.DEVICE_OUT_BLE_BROADCAST, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLE_BROADCAST)); + put(AudioSystem.DEVICE_OUT_HEARING_AID, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_HEARING_AID)); + put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES)); + put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); + }}; // mMusicActiveMs is the cumulative time of music activity since safe volume was disabled. // When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled @@ -173,6 +193,10 @@ public class SoundDoseHelper { @GuardedBy("mCsdStateLock") private float mCurrentCsd = 0.f; + + @GuardedBy("mCsdStateLock") + private long mLastMomentaryExposureTimeMs = -1; + // dose at which the next dose reached warning occurs @GuardedBy("mCsdStateLock") private float mNextCsdWarning = 1.0f; @@ -188,10 +212,26 @@ public class SoundDoseHelper { private final ISoundDoseCallback.Stub mSoundDoseCallback = new ISoundDoseCallback.Stub() { public void onMomentaryExposure(float currentMel, int deviceId) { + if (!mEnableCsd) { + Log.w(TAG, "onMomentaryExposure: csd not supported, ignoring callback"); + return; + } + Log.w(TAG, "DeviceId " + deviceId + " triggered momentary exposure with value: " + currentMel); mLogger.enqueue(SoundDoseEvent.getMomentaryExposureEvent(currentMel)); - if (mEnableCsd) { + + boolean postWarning = false; + synchronized (mCsdStateLock) { + if (mLastMomentaryExposureTimeMs < 0 + || (System.currentTimeMillis() - mLastMomentaryExposureTimeMs) + >= MOMENTARY_EXPOSURE_TIMEOUT_MS) { + mLastMomentaryExposureTimeMs = System.currentTimeMillis(); + postWarning = true; + } + } + + if (postWarning) { mVolumeController.postDisplayCsdWarning( AudioManager.CSD_WARNING_MOMENTARY_EXPOSURE, getTimeoutMsForWarning(AudioManager.CSD_WARNING_MOMENTARY_EXPOSURE)); @@ -250,15 +290,11 @@ public class SoundDoseHelper { mContext = context; mEnableCsd = mContext.getResources().getBoolean(R.bool.config_audio_csd_enabled_default); - if (mEnableCsd) { - mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED; - } else { - mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(), - Settings.Global.AUDIO_SAFE_VOLUME_STATE, SAFE_MEDIA_VOLUME_NOT_CONFIGURED); - } - initCsd(); + mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(), + Settings.Global.AUDIO_SAFE_VOLUME_STATE, SAFE_MEDIA_VOLUME_NOT_CONFIGURED); + // The default safe volume index read here will be replaced by the actual value when // the mcc is read by onConfigureSafeMedia() // For now we use the same index for RS2 initial warning with CSD @@ -388,14 +424,12 @@ public class SoundDoseHelper { } /*package*/ int safeMediaVolumeIndex(int device) { - if (!mSafeMediaVolumeDevices.contains(device)) { + final SafeDeviceVolumeInfo vi = mSafeMediaVolumeDevices.get(device); + if (vi == null) { return MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]; } - if (device == AudioSystem.DEVICE_OUT_USB_HEADSET) { - return mSafeUsbMediaVolumeIndex; - } else { - return mSafeMediaVolumeIndex; - } + + return vi.mSafeVolumeIndex; } /*package*/ void restoreMusicActiveMs() { @@ -419,20 +453,24 @@ public class SoundDoseHelper { /*package*/ void enforceSafeMediaVolume(String caller) { AudioService.VolumeStreamState streamState = mAudioService.getVssVolumeForStream( AudioSystem.STREAM_MUSIC); - Set<Integer> devices = mSafeMediaVolumeDevices; - for (int device : devices) { - int index = streamState.getIndex(device); - int safeIndex = safeMediaVolumeIndex(device); + for (SafeDeviceVolumeInfo vi : mSafeMediaVolumeDevices.values()) { + int index = streamState.getIndex(vi.mDeviceType); + int safeIndex = safeMediaVolumeIndex(vi.mDeviceType); if (index > safeIndex) { - streamState.setIndex(safeIndex, device, caller, true /*hasModifyAudioSettings*/); + streamState.setIndex(safeIndex, vi.mDeviceType, caller, + true /*hasModifyAudioSettings*/); mAudioHandler.sendMessageAtTime( - mAudioHandler.obtainMessage(MSG_SET_DEVICE_VOLUME, device, /*arg2=*/0, - streamState), /*delay=*/0); + mAudioHandler.obtainMessage(MSG_SET_DEVICE_VOLUME, vi.mDeviceType, + /*arg2=*/0, streamState), /*delay=*/0); } } } + /** + * Returns {@code true} if the safe media actions can be applied for the given stream type, + * volume index and device. + **/ /*package*/ boolean checkSafeMediaVolume(int streamType, int index, int device) { boolean result; synchronized (mSafeMediaVolumeStateLock) { @@ -443,17 +481,16 @@ public class SoundDoseHelper { @GuardedBy("mSafeMediaVolumeStateLock") private boolean checkSafeMediaVolume_l(int streamType, int index, int device) { - return (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_ACTIVE) - || (AudioService.mStreamVolumeAlias[streamType] != AudioSystem.STREAM_MUSIC) - || (!mSafeMediaVolumeDevices.contains(device)) - || (index <= safeMediaVolumeIndex(device)) - || mEnableCsd; + return (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) + && (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) + && (mSafeMediaVolumeDevices.containsKey(device)) + && (index > safeMediaVolumeIndex(device)); } /*package*/ boolean willDisplayWarningAfterCheckVolume(int streamType, int index, int device, int flags) { synchronized (mSafeMediaVolumeStateLock) { - if (!checkSafeMediaVolume_l(streamType, index, device)) { + if (checkSafeMediaVolume_l(streamType, index, device)) { mVolumeController.postDisplaySafeVolumeWarning(flags); mPendingVolumeCommand = new StreamVolumeCommand( streamType, index, flags, device); @@ -484,15 +521,13 @@ public class SoundDoseHelper { /*package*/ void scheduleMusicActiveCheck() { synchronized (mSafeMediaVolumeStateLock) { cancelMusicActiveCheck(); - if (!mEnableCsd) { - mMusicActiveIntent = PendingIntent.getBroadcast(mContext, - REQUEST_CODE_CHECK_MUSIC_ACTIVE, - new Intent(ACTION_CHECK_MUSIC_ACTIVE), - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() - + MUSIC_ACTIVE_POLL_PERIOD_MS, mMusicActiveIntent); - } + mMusicActiveIntent = PendingIntent.getBroadcast(mContext, + REQUEST_CODE_CHECK_MUSIC_ACTIVE, + new Intent(ACTION_CHECK_MUSIC_ACTIVE), + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + + MUSIC_ACTIVE_POLL_PERIOD_MS, mMusicActiveIntent); } } @@ -500,7 +535,7 @@ public class SoundDoseHelper { synchronized (mSafeMediaVolumeStateLock) { if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) { int device = mAudioService.getDeviceForStream(AudioSystem.STREAM_MUSIC); - if (mSafeMediaVolumeDevices.contains(device) && isStreamActive) { + if (mSafeMediaVolumeDevices.containsKey(device) && isStreamActive) { scheduleMusicActiveCheck(); int index = mAudioService.getVssVolumeForDevice(AudioSystem.STREAM_MUSIC, device); @@ -528,27 +563,31 @@ public class SoundDoseHelper { /*package*/ void configureSafeMedia(boolean forced, String caller) { int msg = MSG_CONFIGURE_SAFE_MEDIA; - mAudioHandler.removeMessages(msg); + if (forced) { + // unforced should not cancel forced configure messages + mAudioHandler.removeMessages(msg); + } long time = 0; if (forced) { time = (SystemClock.uptimeMillis() + (SystemProperties.getBoolean( "audio.safemedia.bypass", false) ? 0 : SAFE_VOLUME_CONFIGURE_TIMEOUT_MS)); } + mAudioHandler.sendMessageAtTime( mAudioHandler.obtainMessage(msg, /*arg1=*/forced ? 1 : 0, /*arg2=*/0, caller), time); } - /*package*/ void initSafeUsbMediaVolumeIndex() { - // mSafeUsbMediaVolumeIndex must be initialized after createStreamStates() because it - // relies on audio policy having correct ranges for volume indexes. - mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex(); + /*package*/ void initSafeMediaVolumeIndex() { + for (SafeDeviceVolumeInfo vi : mSafeMediaVolumeDevices.values()) { + vi.mSafeVolumeIndex = getSafeDeviceMediaVolumeIndex(vi.mDeviceType); + } } /*package*/ int getSafeMediaVolumeIndex(int device) { - if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE && mSafeMediaVolumeDevices.contains( - device)) { + if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE + && mSafeMediaVolumeDevices.containsKey(device)) { return safeMediaVolumeIndex(device); } else { return -1; @@ -557,7 +596,7 @@ public class SoundDoseHelper { /*package*/ boolean raiseVolumeDisplaySafeMediaVolume(int streamType, int index, int device, int flags) { - if (checkSafeMediaVolume(streamType, index, device)) { + if (!checkSafeMediaVolume(streamType, index, device)) { return false; } @@ -566,7 +605,7 @@ public class SoundDoseHelper { } /*package*/ boolean safeDevicesContains(int device) { - return mSafeMediaVolumeDevices.contains(device); + return mSafeMediaVolumeDevices.containsKey(device); } /*package*/ void invalidatPendingVolumeCommand() { @@ -612,8 +651,11 @@ public class SoundDoseHelper { pw.print(" mSafeMediaVolumeState="); pw.println(safeMediaVolumeStateToString(mSafeMediaVolumeState)); pw.print(" mSafeMediaVolumeIndex="); pw.println(mSafeMediaVolumeIndex); - pw.print(" mSafeUsbMediaVolumeIndex="); pw.println(mSafeUsbMediaVolumeIndex); - pw.print(" mSafeUsbMediaVolumeDbfs="); pw.println(mSafeUsbMediaVolumeDbfs); + for (SafeDeviceVolumeInfo vi : mSafeMediaVolumeDevices.values()) { + pw.print(" mSafeMediaVolumeIndex["); pw.print(vi.mDeviceType); + pw.print("]="); pw.println(vi.mSafeVolumeIndex); + } + pw.print(" mSafeMediaVolumeDbfs="); pw.println(mSafeMediaVolumeDbfs); pw.print(" mMusicActiveMs="); pw.println(mMusicActiveMs); pw.print(" mMcc="); pw.println(mMcc); pw.print(" mPendingVolumeCommand="); pw.println(mPendingVolumeCommand); @@ -660,11 +702,12 @@ public class SoundDoseHelper { if (!isAbsoluteVolume) { // remove any possible previous attenuation soundDose.updateAttenuation(/* attenuationDB= */0.f, device); + return; } if (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC - && mSafeMediaCsdDevices.contains(device)) { + && mSafeMediaVolumeDevices.containsKey(device)) { soundDose.updateAttenuation( AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC, (newIndex + 5) / 10, @@ -715,7 +758,7 @@ public class SoundDoseHelper { mSafeMediaVolumeIndex = mContext.getResources().getInteger( com.android.internal.R.integer.config_safe_media_volume_index) * 10; - mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex(); + initSafeMediaVolumeIndex(); boolean safeMediaVolumeEnabled = SystemProperties.getBoolean("audio.safemedia.force", false) @@ -728,7 +771,7 @@ public class SoundDoseHelper { // The persisted state is either "disabled" or "active": this is the state applied // next time we boot and cannot be "inactive" int persistedState; - if (safeMediaVolumeEnabled && !safeMediaVolumeBypass && !mEnableCsd) { + if (safeMediaVolumeEnabled && !safeMediaVolumeBypass) { persistedState = SAFE_MEDIA_VOLUME_ACTIVE; // The state can already be "inactive" here if the user has forced it before // the 30 seconds timeout for forced configuration. In this case we don't reset @@ -801,25 +844,32 @@ public class SoundDoseHelper { mAudioHandler.obtainMessage(MSG_PERSIST_MUSIC_ACTIVE_MS, mMusicActiveMs, 0).sendToTarget(); } - private int getSafeUsbMediaVolumeIndex() { + private int getSafeDeviceMediaVolumeIndex(int deviceType) { + // legacy implementation uses mSafeMediaVolumeIndex for wired HS/HP + // instead of computing it from the volume curves + if ((deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE + || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd) { + return mSafeMediaVolumeIndex; + } + // determine UI volume index corresponding to the wanted safe gain in dBFS int min = MIN_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]; int max = MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]; - mSafeUsbMediaVolumeDbfs = mContext.getResources().getInteger( + mSafeMediaVolumeDbfs = mContext.getResources().getInteger( com.android.internal.R.integer.config_safe_media_volume_usb_mB) / 100.0f; while (Math.abs(max - min) > 1) { int index = (max + min) / 2; - float gainDB = AudioSystem.getStreamVolumeDB( - AudioSystem.STREAM_MUSIC, index, AudioSystem.DEVICE_OUT_USB_HEADSET); + float gainDB = AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC, index, + deviceType); if (Float.isNaN(gainDB)) { //keep last min in case of read error break; - } else if (gainDB == mSafeUsbMediaVolumeDbfs) { + } else if (gainDB == mSafeMediaVolumeDbfs) { min = index; break; - } else if (gainDB < mSafeUsbMediaVolumeDbfs) { + } else if (gainDB < mSafeMediaVolumeDbfs) { min = index; } else { max = index; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 8a33f22d6967..f6c1375730bb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -427,13 +427,6 @@ public class FingerprintService extends SystemService { return -1; } - if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, options.getUserId())) { - // If this happens, something in KeyguardUpdateMonitor is wrong. This should only - // ever be invoked when the user is encrypted or lockdown. - Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown"); - return -1; - } - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for detectFingerprint"); diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java index 3a4aaa76d633..1f82961efd22 100644 --- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java @@ -31,7 +31,9 @@ import android.net.metrics.DnsEvent; import android.net.metrics.NetworkMetrics; import android.net.metrics.WakeupEvent; import android.net.metrics.WakeupStats; +import android.os.BatteryStatsInternal; import android.os.RemoteException; +import android.os.SystemClock; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.Log; @@ -44,6 +46,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.RingBuffer; import com.android.internal.util.TokenBucket; import com.android.net.module.util.BaseNetdEventListener; +import com.android.server.LocalServices; import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; import java.io.PrintWriter; @@ -74,7 +77,7 @@ public class NetdEventListenerService extends BaseNetdEventListener { // TODO: dedup this String constant with the one used in // ConnectivityService#wakeupModifyInterface(). @VisibleForTesting - static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:"; + static final String WAKEUP_EVENT_PREFIX_DELIM = ":"; // Array of aggregated DNS and connect events sent by netd, grouped by net id. @GuardedBy("this") @@ -278,17 +281,14 @@ public class NetdEventListenerService extends BaseNetdEventListener { @Override public synchronized void onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader, byte[] dstHw, String srcIp, String dstIp, int srcPort, int dstPort, long timestampNs) { - String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, ""); - final long timestampMs; - if (timestampNs > 0) { - timestampMs = timestampNs / NANOS_PER_MS; - } else { - timestampMs = System.currentTimeMillis(); + final String[] prefixParts = prefix.split(WAKEUP_EVENT_PREFIX_DELIM); + if (prefixParts.length != 2) { + throw new IllegalArgumentException("Prefix " + prefix + + " required in format <nethandle>:<interface>"); } - WakeupEvent event = new WakeupEvent(); - event.iface = iface; - event.timestampMs = timestampMs; + final WakeupEvent event = new WakeupEvent(); + event.iface = prefixParts[1]; event.uid = uid; event.ethertype = ethertype; event.dstHwAddr = MacAddress.fromBytes(dstHw); @@ -297,11 +297,25 @@ public class NetdEventListenerService extends BaseNetdEventListener { event.ipNextHeader = ipNextHeader; event.srcPort = srcPort; event.dstPort = dstPort; + if (timestampNs > 0) { + event.timestampMs = timestampNs / NANOS_PER_MS; + } else { + event.timestampMs = System.currentTimeMillis(); + } addWakeupEvent(event); - String dstMac = event.dstHwAddr.toString(); + final BatteryStatsInternal bsi = LocalServices.getService(BatteryStatsInternal.class); + if (bsi != null) { + final long netHandle = Long.parseLong(prefixParts[0]); + final long elapsedMs = SystemClock.elapsedRealtime() + event.timestampMs + - System.currentTimeMillis(); + bsi.noteCpuWakingNetworkPacket(Network.fromNetworkHandle(netHandle), elapsedMs, + event.uid); + } + + final String dstMac = event.dstHwAddr.toString(); FrameworkStatsLog.write(FrameworkStatsLog.PACKET_WAKEUP_OCCURRED, - uid, iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort); + uid, event.iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort); } @Override diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index 6e1640d545fe..22b6a53ab907 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -35,7 +35,6 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; -import android.hardware.display.BrightnessConfiguration; import android.hardware.display.ColorDisplayManager; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; @@ -126,7 +125,7 @@ public class BrightnessTracker { private static final int MSG_BRIGHTNESS_CHANGED = 1; private static final int MSG_STOP_SENSOR_LISTENER = 2; private static final int MSG_START_SENSOR_LISTENER = 3; - private static final int MSG_BRIGHTNESS_CONFIG_CHANGED = 4; + private static final int MSG_SHOULD_COLLECT_COLOR_SAMPLE_CHANGED = 4; private static final int MSG_SENSOR_CHANGED = 5; private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); @@ -162,7 +161,7 @@ public class BrightnessTracker { private boolean mColorSamplingEnabled; private int mNoFramesToSample; private float mFrameRate; - private BrightnessConfiguration mBrightnessConfiguration; + private boolean mShouldCollectColorSample = false; // End of block of members that should only be accessed on the mBgHandler thread. private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL; @@ -208,9 +207,9 @@ public class BrightnessTracker { /** * Update tracker with new brightness configuration. */ - public void setBrightnessConfiguration(BrightnessConfiguration brightnessConfiguration) { - mBgHandler.obtainMessage(MSG_BRIGHTNESS_CONFIG_CHANGED, - brightnessConfiguration).sendToTarget(); + public void setShouldCollectColorSample(boolean shouldCollectColorSample) { + mBgHandler.obtainMessage(MSG_SHOULD_COLLECT_COLOR_SAMPLE_CHANGED, + shouldCollectColorSample).sendToTarget(); } private void backgroundStart(float initialBrightness) { @@ -320,7 +319,7 @@ public class BrightnessTracker { * Notify the BrightnessTracker that the user has changed the brightness of the display. */ public void notifyBrightnessChanged(float brightness, boolean userInitiated, - float powerBrightnessFactor, boolean isUserSetBrightness, + float powerBrightnessFactor, boolean wasShortTermModelActive, boolean isDefaultBrightnessConfig, String uniqueDisplayId, float[] luxValues, long[] luxTimestamps) { if (DEBUG) { @@ -329,7 +328,7 @@ public class BrightnessTracker { } Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED, userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness, - powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig, + powerBrightnessFactor, wasShortTermModelActive, isDefaultBrightnessConfig, mInjector.currentTimeMillis(), uniqueDisplayId, luxValues, luxTimestamps)); m.sendToTarget(); } @@ -343,7 +342,7 @@ public class BrightnessTracker { } private void handleBrightnessChanged(float brightness, boolean userInitiated, - float powerBrightnessFactor, boolean isUserSetBrightness, + float powerBrightnessFactor, boolean wasShortTermModelActive, boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId, float[] luxValues, long[] luxTimestamps) { BrightnessChangeEvent.Builder builder; @@ -368,7 +367,7 @@ public class BrightnessTracker { builder.setBrightness(brightness); builder.setTimeStamp(timestamp); builder.setPowerBrightnessFactor(powerBrightnessFactor); - builder.setUserBrightnessPoint(isUserSetBrightness); + builder.setUserBrightnessPoint(wasShortTermModelActive); builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig); builder.setUniqueDisplayId(uniqueDisplayId); @@ -827,8 +826,7 @@ public class BrightnessTracker { if (!mInjector.isBrightnessModeAutomatic(mContentResolver) || !mInjector.isInteractive(mContext) || mColorSamplingEnabled - || mBrightnessConfiguration == null - || !mBrightnessConfiguration.shouldCollectColorSamples()) { + || !mShouldCollectColorSample) { return; } @@ -997,7 +995,7 @@ public class BrightnessTracker { BrightnessChangeValues values = (BrightnessChangeValues) msg.obj; boolean userInitiatedChange = (msg.arg1 == 1); handleBrightnessChanged(values.brightness, userInitiatedChange, - values.powerBrightnessFactor, values.isUserSetBrightness, + values.powerBrightnessFactor, values.wasShortTermModelActive, values.isDefaultBrightnessConfig, values.timestamp, values.uniqueDisplayId, values.luxValues, values.luxTimestamps); break; @@ -1009,14 +1007,11 @@ public class BrightnessTracker { stopSensorListener(); disableColorSampling(); break; - case MSG_BRIGHTNESS_CONFIG_CHANGED: - mBrightnessConfiguration = (BrightnessConfiguration) msg.obj; - boolean shouldCollectColorSamples = - mBrightnessConfiguration != null - && mBrightnessConfiguration.shouldCollectColorSamples(); - if (shouldCollectColorSamples && !mColorSamplingEnabled) { + case MSG_SHOULD_COLLECT_COLOR_SAMPLE_CHANGED: + mShouldCollectColorSample = (boolean) msg.obj; + if (mShouldCollectColorSample && !mColorSamplingEnabled) { enableColorSampling(); - } else if (!shouldCollectColorSamples && mColorSamplingEnabled) { + } else if (!mShouldCollectColorSample && mColorSamplingEnabled) { disableColorSampling(); } break; @@ -1031,7 +1026,7 @@ public class BrightnessTracker { private static class BrightnessChangeValues { public final float brightness; public final float powerBrightnessFactor; - public final boolean isUserSetBrightness; + public final boolean wasShortTermModelActive; public final boolean isDefaultBrightnessConfig; public final long timestamp; public final String uniqueDisplayId; @@ -1039,11 +1034,11 @@ public class BrightnessTracker { public final long[] luxTimestamps; BrightnessChangeValues(float brightness, float powerBrightnessFactor, - boolean isUserSetBrightness, boolean isDefaultBrightnessConfig, + boolean wasShortTermModelActive, boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId, float[] luxValues, long[] luxTimestamps) { this.brightness = brightness; this.powerBrightnessFactor = powerBrightnessFactor; - this.isUserSetBrightness = isUserSetBrightness; + this.wasShortTermModelActive = wasShortTermModelActive; this.isDefaultBrightnessConfig = isDefaultBrightnessConfig; this.timestamp = timestamp; this.uniqueDisplayId = uniqueDisplayId; diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 99e709ea3fd8..7b560cecbf21 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -27,6 +27,8 @@ import android.view.DisplayAddress; import android.view.Surface; import android.view.SurfaceControl; +import com.android.server.display.mode.DisplayModeDirector; + import java.io.PrintWriter; /** diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index d9b350189fc4..75f8accde3a5 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -1260,7 +1260,7 @@ public class DisplayDeviceConfig { return mAmbientDarkeningPercentagesIdle; } - SensorData getAmbientLightSensor() { + public SensorData getAmbientLightSensor() { return mAmbientLightSensor; } @@ -2821,7 +2821,7 @@ public class DisplayDeviceConfig { /** * Uniquely identifies a Sensor, with the combination of Type and Name. */ - static class SensorData { + public static class SensorData { public String type; public String name; public float minRefreshRate = 0.0f; diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 6eb465e1049e..21cc172f5908 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -152,6 +152,7 @@ import com.android.server.UiThread; import com.android.server.companion.virtual.VirtualDeviceManagerInternal; import com.android.server.display.DisplayDeviceConfig.SensorData; import com.android.server.display.layout.Layout; +import com.android.server.display.mode.DisplayModeDirector; import com.android.server.display.utils.SensorUtils; import com.android.server.input.InputManagerInternal; import com.android.server.wm.SurfaceAnimationThread; @@ -2104,8 +2105,7 @@ public final class DisplayManagerService extends SystemService { } } - // TODO (b/264979880) - Add unit test for HDR output control methods. - private void setHdrConversionModeInternal(HdrConversionMode hdrConversionMode) { + void setHdrConversionModeInternal(HdrConversionMode hdrConversionMode) { if (!mInjector.getHdrOutputConversionSupport()) { return; } @@ -2139,7 +2139,7 @@ public final class DisplayManagerService extends SystemService { } } - private HdrConversionMode getHdrConversionModeSettingInternal() { + HdrConversionMode getHdrConversionModeSettingInternal() { if (!mInjector.getHdrOutputConversionSupport()) { return HDR_CONVERSION_MODE_UNSUPPORTED; } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 9917bfc3aca6..84fe8f21b764 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -885,7 +885,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final boolean isInTransition = mLogicalDisplay.isInTransitionLocked(); final String brightnessThrottlingDataId = mLogicalDisplay.getBrightnessThrottlingDataIdLocked(); - mHandler.post(() -> { + mHandler.postAtTime(() -> { boolean changed = false; if (mDisplayDevice != device) { changed = true; @@ -916,7 +916,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (changed) { updatePowerState(); } - }); + }, mClock.uptimeMillis()); } /** @@ -940,10 +940,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.stop(); } - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.stop(); - } - if (mBrightnessSetting != null) { mBrightnessSetting.unregisterListener(mBrightnessSettingListener); } @@ -1190,6 +1186,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mScreenOffBrightnessSensorController != null) { mScreenOffBrightnessSensorController.stop(); + mScreenOffBrightnessSensorController = null; } loadScreenOffBrightnessSensor(); int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux(); @@ -1311,6 +1308,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mPowerState.stop(); mPowerState = null; } + + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController.stop(); + } } private void updatePowerState() { @@ -1538,10 +1539,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // user, or is a temporary adjustment. boolean userInitiatedChange = (Float.isNaN(brightnessState)) && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged); - boolean hadUserBrightnessPoint = false; + boolean wasShortTermModelActive = false; // Configure auto-brightness. if (mAutomaticBrightnessController != null) { - hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints(); + wasShortTermModelActive = mAutomaticBrightnessController.hasUserDataPoints(); mAutomaticBrightnessController.configure(autoBrightnessState, mBrightnessConfiguration, mLastUserSetScreenBrightness, @@ -1555,7 +1556,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); if (mBrightnessTracker != null) { - mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration); + mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null + && mBrightnessConfiguration.shouldCollectColorSamples()); } boolean updateScreenBrightnessSetting = false; @@ -1823,7 +1825,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call userInitiatedChange = false; } notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange, - hadUserBrightnessPoint); + wasShortTermModelActive); } // We save the brightness info *after* the brightness setting has been changed and @@ -1865,7 +1867,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mTempBrightnessEvent.setRbcStrength(mCdsi != null ? mCdsi.getReduceBrightColorsStrength() : -1); mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor); - mTempBrightnessEvent.setWasShortTermModelActive(hadUserBrightnessPoint); + mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive); // Temporary is what we use during slider interactions. We avoid logging those so that // we don't spam logcat when the slider is being used. boolean tempToTempTransition = @@ -2634,7 +2636,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated, - boolean hadUserDataPoint) { + boolean wasShortTermModelActive) { final float brightnessInNits = convertToNits(brightness); if (mUseAutoBrightness && brightnessInNits >= 0.0f && mAutomaticBrightnessController != null && mBrightnessTracker != null) { @@ -2645,7 +2647,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call ? mPowerRequest.screenLowPowerBrightnessFactor : 1.0f; mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated, - powerFactor, hadUserDataPoint, + powerFactor, wasShortTermModelActive, mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId, mAutomaticBrightnessController.getLastSensorValues(), mAutomaticBrightnessController.getLastSensorTimestamps()); diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index f49419cf789e..297a6f8a52e5 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -729,7 +729,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal final String brightnessThrottlingDataId = mLogicalDisplay.getBrightnessThrottlingDataIdLocked(); - mHandler.post(() -> { + mHandler.postAtTime(() -> { boolean changed = false; if (mDisplayDevice != device) { changed = true; @@ -761,7 +761,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal if (changed) { updatePowerState(); } - }); + }, mClock.uptimeMillis()); } /** @@ -1028,6 +1028,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mBrightnessEventRingBuffer = new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX); + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController.stop(); + mScreenOffBrightnessSensorController = null; + } loadScreenOffBrightnessSensor(); int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux(); if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) { @@ -1133,6 +1137,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mPowerState.stop(); mPowerState = null; } + + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController.stop(); + } } private void updatePowerState() { @@ -1250,10 +1258,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // user, or is a temporary adjustment. boolean userInitiatedChange = (Float.isNaN(brightnessState)) && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged); - boolean hadUserBrightnessPoint = false; + boolean wasShortTermModelActive = false; // Configure auto-brightness. if (mAutomaticBrightnessController != null) { - hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints(); + wasShortTermModelActive = mAutomaticBrightnessController.hasUserDataPoints(); mAutomaticBrightnessController.configure(autoBrightnessState, mBrightnessConfiguration, mDisplayBrightnessController.getLastUserSetScreenBrightness(), @@ -1267,7 +1275,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); if (mBrightnessTracker != null) { - mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration); + mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null + && mBrightnessConfiguration.shouldCollectColorSamples()); } boolean updateScreenBrightnessSetting = false; @@ -1537,7 +1546,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal userInitiatedChange = false; } notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange, - hadUserBrightnessPoint); + wasShortTermModelActive); } // We save the brightness info *after* the brightness setting has been changed and @@ -1579,7 +1588,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mTempBrightnessEvent.setRbcStrength(mCdsi != null ? mCdsi.getReduceBrightColorsStrength() : -1); mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor); - mTempBrightnessEvent.setWasShortTermModelActive(hadUserBrightnessPoint); + mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive); mTempBrightnessEvent.setDisplayBrightnessStrategyName(displayBrightnessState .getDisplayBrightnessStrategyName()); // Temporary is what we use during slider interactions. We avoid logging those so that @@ -2220,7 +2229,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal } private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated, - boolean hadUserDataPoint) { + boolean wasShortTermModelActive) { final float brightnessInNits = convertToNits(brightness); if (mUseAutoBrightness && brightnessInNits >= 0.0f && mAutomaticBrightnessController != null && mBrightnessTracker != null) { @@ -2231,7 +2240,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal ? mPowerRequest.screenLowPowerBrightnessFactor : 1.0f; mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated, - powerFactor, hadUserDataPoint, + powerFactor, wasShortTermModelActive, mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId, mAutomaticBrightnessController.getLastSensorValues(), mAutomaticBrightnessController.getLastSensorTimestamps()); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 8f52c97c1f87..58c50346a38f 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -46,6 +46,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; +import com.android.server.display.mode.DisplayModeDirector; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index fc90db66c1cb..78c597ea6e53 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -33,6 +33,7 @@ import android.view.Surface; import android.view.SurfaceControl; import com.android.server.display.layout.Layout; +import com.android.server.display.mode.DisplayModeDirector; import com.android.server.wm.utils.InsetUtils; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index 3e67f0a466d7..2ce7690ecc3f 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -36,6 +36,7 @@ import android.view.SurfaceControl; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.display.mode.DisplayModeDirector; import java.io.PrintWriter; import java.util.ArrayList; diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index 31f5ab7dfe98..24d5ca402dd0 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.display; +package com.android.server.display.mode; import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED; import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE; @@ -67,6 +67,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; +import com.android.server.display.DisplayDeviceConfig; import com.android.server.display.utils.AmbientFilter; import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.display.utils.SensorUtils; @@ -214,6 +215,9 @@ public class DisplayModeDirector { mUdfpsObserver.observe(); } + /** + * Enables or disables component logging + */ public void setLoggingEnabled(boolean loggingEnabled) { if (mLoggingEnabled == loggingEnabled) { return; @@ -1574,7 +1578,10 @@ public class DisplayModeDirector { } } - final class AppRequestObserver { + /** + * Responsible for keeping track of app requested refresh rates per display + */ + public final class AppRequestObserver { private final SparseArray<Display.Mode> mAppRequestedModeByDisplay; private final SparseArray<RefreshRateRange> mAppPreferredRefreshRateRangeByDisplay; @@ -1583,6 +1590,9 @@ public class DisplayModeDirector { mAppPreferredRefreshRateRangeByDisplay = new SparseArray<>(); } + /** + * Sets refresh rates from app request + */ public void setAppRequest(int displayId, int modeId, float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) { synchronized (mLock) { @@ -1665,7 +1675,7 @@ public class DisplayModeDirector { return null; } - public void dumpLocked(PrintWriter pw) { + private void dumpLocked(PrintWriter pw) { pw.println(" AppRequestObserver"); pw.println(" mAppRequestedModeByDisplay:"); for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) { @@ -1794,7 +1804,7 @@ public class DisplayModeDirector { */ @VisibleForTesting public class BrightnessObserver implements DisplayManager.DisplayListener { - private final static int LIGHT_SENSOR_RATE_MS = 250; + private static final int LIGHT_SENSOR_RATE_MS = 250; private int[] mLowDisplayBrightnessThresholds; private int[] mLowAmbientBrightnessThresholds; private int[] mHighDisplayBrightnessThresholds; @@ -2019,7 +2029,7 @@ public class DisplayModeDirector { return mLowAmbientBrightnessThresholds; } - public void observe(SensorManager sensorManager) { + private void observe(SensorManager sensorManager) { mSensorManager = sensorManager; mBrightness = getBrightness(Display.DEFAULT_DISPLAY); @@ -2064,11 +2074,11 @@ public class DisplayModeDirector { mDeviceConfigDisplaySettings.startListening(); mInjector.registerDisplayListener(this, mHandler, - DisplayManager.EVENT_FLAG_DISPLAY_CHANGED | - DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS); + DisplayManager.EVENT_FLAG_DISPLAY_CHANGED + | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS); } - public void setLoggingEnabled(boolean loggingEnabled) { + private void setLoggingEnabled(boolean loggingEnabled) { if (mLoggingEnabled == loggingEnabled) { return; } @@ -2076,7 +2086,8 @@ public class DisplayModeDirector { mLightSensorListener.setLoggingEnabled(loggingEnabled); } - public void onRefreshRateSettingChangedLocked(float min, float max) { + @VisibleForTesting + void onRefreshRateSettingChangedLocked(float min, float max) { boolean changeable = (max - min > 1f && max > 60f); if (mRefreshRateChangeable != changeable) { mRefreshRateChangeable = changeable; @@ -2089,14 +2100,14 @@ public class DisplayModeDirector { } } - public void onLowPowerModeEnabledLocked(boolean b) { + private void onLowPowerModeEnabledLocked(boolean b) { if (mLowPowerModeEnabled != b) { mLowPowerModeEnabled = b; updateSensorStatus(); } } - public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds, + private void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds) { if (displayThresholds != null && ambientThresholds != null && displayThresholds.length == ambientThresholds.length) { @@ -2123,7 +2134,7 @@ public class DisplayModeDirector { } } - public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds, + private void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds) { if (displayThresholds != null && ambientThresholds != null && displayThresholds.length == ambientThresholds.length) { @@ -2150,7 +2161,7 @@ public class DisplayModeDirector { } } - public void dumpLocked(PrintWriter pw) { + void dumpLocked(PrintWriter pw) { pw.println(" BrightnessObserver"); pw.println(" mAmbientLux: " + mAmbientLux); pw.println(" mBrightness: " + mBrightness); @@ -2400,7 +2411,7 @@ public class DisplayModeDirector { } @VisibleForTesting - public void setDefaultDisplayState(int state) { + void setDefaultDisplayState(int state) { if (mLoggingEnabled) { Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = " + mDefaultDisplayState + ", state = " + state); @@ -2475,7 +2486,7 @@ public class DisplayModeDirector { } private final class LightSensorEventListener implements SensorEventListener { - final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS; + private static final int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS; private float mLastSensorData; private long mTimestamp; private boolean mLoggingEnabled; @@ -2876,10 +2887,10 @@ public class DisplayModeDirector { } final int hbmMode = info.highBrightnessMode; - final boolean isHbmActive = hbmMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF && - info.adjustedBrightness > info.highBrightnessTransitionPoint; - if (hbmMode == mHbmMode.get(displayId) && - isHbmActive == mHbmActive.get(displayId)) { + final boolean isHbmActive = hbmMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF + && info.adjustedBrightness > info.highBrightnessTransitionPoint; + if (hbmMode == mHbmMode.get(displayId) + && isHbmActive == mHbmActive.get(displayId)) { // no change, ignore. return; } @@ -2901,7 +2912,7 @@ public class DisplayModeDirector { Vote vote = null; if (mHbmActive.get(displayId, false)) { final int hbmMode = - mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); + mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) { // Device resource properties take priority over DisplayDeviceConfig if (mRefreshRateInHbmSunlight > 0) { @@ -2909,7 +2920,7 @@ public class DisplayModeDirector { mRefreshRateInHbmSunlight); } else { final List<RefreshRateLimitation> limits = - mDisplayManagerInternal.getRefreshRateLimitations(displayId); + mDisplayManagerInternal.getRefreshRateLimitations(displayId); for (int i = 0; limits != null && i < limits.size(); i++) { final RefreshRateLimitation limitation = limits.get(i); if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) { @@ -2919,8 +2930,8 @@ public class DisplayModeDirector { } } } - } else if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR && - mRefreshRateInHbmHdr > 0) { + } else if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR + && mRefreshRateInHbmHdr > 0) { // HBM for HDR vote isn't supported through DisplayDeviceConfig yet, so look for // a vote from Device properties vote = Vote.forPhysicalRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr); @@ -2988,9 +2999,6 @@ public class DisplayModeDirector { } private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener { - public DeviceConfigDisplaySettings() { - } - public void startListening() { mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, BackgroundThread.getExecutor(), this); @@ -3001,8 +3009,8 @@ public class DisplayModeDirector { */ public int[] getLowDisplayBrightnessThresholds() { return getIntArrayProperty( - DisplayManager.DeviceConfig. - KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS); + DisplayManager.DeviceConfig + .KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS); } /* @@ -3010,8 +3018,8 @@ public class DisplayModeDirector { */ public int[] getLowAmbientBrightnessThresholds() { return getIntArrayProperty( - DisplayManager.DeviceConfig. - KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS); + DisplayManager.DeviceConfig + .KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS); } public int getRefreshRateInLowZone() { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 3563938a9c3c..c0deb3f8274b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -700,6 +700,7 @@ public class HdmiControlService extends SystemService { } if (mCecController == null) { Slog.i(TAG, "Device does not support HDMI-CEC."); + return; } if (mMhlController == null) { mMhlController = HdmiMhlControllerStub.create(this); @@ -713,9 +714,6 @@ public class HdmiControlService extends SystemService { if (mEarcController == null) { Slog.i(TAG, "Device does not support eARC."); } - if (mCecController == null && mEarcController == null) { - return; - } mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController); if (isCecControlEnabled()) { initializeCec(INITIATED_BY_BOOT_UP); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index eba261ad6508..d397a0c5e0bc 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1410,6 +1410,22 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @Override + public void onPackageDataCleared(String packageName, int uid) { + boolean changed = false; + for (InputMethodInfo imi : mMethodList) { + if (imi.getPackageName().equals(packageName)) { + mAdditionalSubtypeMap.remove(imi.getId()); + changed = true; + } + } + if (changed) { + AdditionalSubtypeUtils.save( + mAdditionalSubtypeMap, mMethodMap, mSettings.getCurrentUserId()); + mChangedPackages.add(packageName); + } + } + + @Override public void onFinishPackageChanges() { onFinishPackageChangesInternal(); clearPackageChangeState(); @@ -3263,7 +3279,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub notifyInputMethodSubtypeChangedLocked(userId, info, null); return; } - if (newSubtype != oldSubtype) { + if (!newSubtype.equals(oldSubtype)) { setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true); IInputMethodInvoker curMethod = getCurMethodLocked(); if (curMethod != null) { diff --git a/services/core/java/com/android/server/locales/AppSupportedLocalesChangedAtomRecord.java b/services/core/java/com/android/server/locales/AppSupportedLocalesChangedAtomRecord.java new file mode 100644 index 000000000000..de429f008fcf --- /dev/null +++ b/services/core/java/com/android/server/locales/AppSupportedLocalesChangedAtomRecord.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.locales; + +import static android.os.Process.INVALID_UID; + +import com.android.internal.util.FrameworkStatsLog; + +/** + * Holds data used to report the AppSupportedLocalesChanged atom. + */ +public final class AppSupportedLocalesChangedAtomRecord { + // The uid which invoked this update. + final int mCallingUid; + // The uid for which the override of app’s supported locales change is being done. + int mTargetUid = INVALID_UID; + // The total number of locales in the override LocaleConfig. + int mNumLocales = -1; + // Whether the override is removed LocaleConfig from the storage. + boolean mOverrideRemoved = false; + // Whether the new override LocaleConfig is the same as the app’s LocaleConfig. + boolean mSameAsResConfig = false; + // Whether the new override LocaleConfig is the same as the previously effective one. This means + // a comparison with the previous override LocaleConfig if there was one, and a comparison with + // the resource LocaleConfig if no override was present. + boolean mSameAsPrevConfig = false; + // Application supported locales changed status. + int mStatus = FrameworkStatsLog + .APP_SUPPORTED_LOCALES_CHANGED__STATUS__STATUS_UNSPECIFIED; + + AppSupportedLocalesChangedAtomRecord(int callingUid) { + this.mCallingUid = callingUid; + } + + void setTargetUid(int targetUid) { + this.mTargetUid = targetUid; + } + + void setNumLocales(int numLocales) { + this.mNumLocales = numLocales; + } + + void setOverrideRemoved(boolean overrideRemoved) { + this.mOverrideRemoved = overrideRemoved; + } + + void setSameAsResConfig(boolean sameAsResConfig) { + this.mSameAsResConfig = sameAsResConfig; + } + + void setSameAsPrevConfig(boolean sameAsPrevConfig) { + this.mSameAsPrevConfig = sameAsPrevConfig; + } + + void setStatus(int status) { + this.mStatus = status; + } +} diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java index 99d30f54ce21..e5f589718deb 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerService.java +++ b/services/core/java/com/android/server/locales/LocaleManagerService.java @@ -250,7 +250,7 @@ public class LocaleManagerService extends SystemService { // set locales if the package name is owned by the app. Next, check if the caller has // the necessary permission and set locales. boolean isCallerOwner = isPackageOwnedByCaller(appPackageName, userId, - atomRecordForMetrics); + atomRecordForMetrics, null); if (!isCallerOwner) { enforceChangeConfigurationPermission(atomRecordForMetrics); } @@ -264,7 +264,7 @@ public class LocaleManagerService extends SystemService { Binder.restoreCallingIdentity(token); } } finally { - logMetric(atomRecordForMetrics); + logAppLocalesMetric(atomRecordForMetrics); } } @@ -355,32 +355,30 @@ public class LocaleManagerService extends SystemService { } /** - * Same as {@link LocaleManagerService#isPackageOwnedByCaller(String, int, - * AppLocaleChangedAtomRecord)}, but for methods that do not log locale atom. - */ - private boolean isPackageOwnedByCaller(String appPackageName, int userId) { - return isPackageOwnedByCaller(appPackageName, userId, /* atomRecordForMetrics= */null); - } - - /** * Checks if the package is owned by the calling app or not for the given user id. * * @throws IllegalArgumentException if package not found for given userid */ private boolean isPackageOwnedByCaller(String appPackageName, int userId, - @Nullable AppLocaleChangedAtomRecord atomRecordForMetrics) { + @Nullable AppLocaleChangedAtomRecord atomRecordForMetrics, + @Nullable AppSupportedLocalesChangedAtomRecord appSupportedLocalesChangedAtomRecord) { final int uid = getPackageUid(appPackageName, userId); if (uid < 0) { Slog.w(TAG, "Unknown package " + appPackageName + " for user " + userId); if (atomRecordForMetrics != null) { atomRecordForMetrics.setStatus(FrameworkStatsLog .APPLICATION_LOCALES_CHANGED__STATUS__FAILURE_INVALID_TARGET_PACKAGE); + } else if (appSupportedLocalesChangedAtomRecord != null) { + appSupportedLocalesChangedAtomRecord.setStatus(FrameworkStatsLog + .APP_SUPPORTED_LOCALES_CHANGED__STATUS__FAILURE_INVALID_TARGET_PACKAGE); } throw new IllegalArgumentException("Unknown package: " + appPackageName + " for user " + userId); } if (atomRecordForMetrics != null) { atomRecordForMetrics.setTargetUid(uid); + } else if (appSupportedLocalesChangedAtomRecord != null) { + appSupportedLocalesChangedAtomRecord.setTargetUid(uid); } //Once valid package found, ignore the userId part for validating package ownership //as apps with INTERACT_ACROSS_USERS permission could be changing locale for different user. @@ -425,7 +423,7 @@ public class LocaleManagerService extends SystemService { // current input method, and that app is querying locales of the current foreground app. If // neither conditions matched, check if the caller has the necessary permission and fetch // locales. - if (!isPackageOwnedByCaller(appPackageName, userId) + if (!isPackageOwnedByCaller(appPackageName, userId, null, null) && !isCallerInstaller(appPackageName, userId) && !(isCallerFromCurrentInputMethod(userId) && mActivityManagerInternal.isAppForeground( @@ -550,7 +548,7 @@ public class LocaleManagerService extends SystemService { return systemLocales; } - private void logMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) { + private void logAppLocalesMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) { FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_LOCALES_CHANGED, atomRecordForMetrics.mCallingUid, atomRecordForMetrics.mTargetUid, @@ -569,33 +567,39 @@ public class LocaleManagerService extends SystemService { return; } - requireNonNull(appPackageName); + AppSupportedLocalesChangedAtomRecord atomRecord = new AppSupportedLocalesChangedAtomRecord( + Binder.getCallingUid()); + try { + requireNonNull(appPackageName); - //Allow apps with INTERACT_ACROSS_USERS permission to set locales for different user. - userId = mActivityManagerInternal.handleIncomingUser( - Binder.getCallingPid(), Binder.getCallingUid(), userId, - false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL, - "setOverrideLocaleConfig", /* callerPackage= */ null); + //Allow apps with INTERACT_ACROSS_USERS permission to set locales for different user. + userId = mActivityManagerInternal.handleIncomingUser( + Binder.getCallingPid(), Binder.getCallingUid(), userId, + false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL, + "setOverrideLocaleConfig", /* callerPackage= */ null); - // This function handles two types of set operations: - // 1.) A normal, an app overrides its own LocaleConfig. - // 2.) A privileged system application or service is granted the necessary permission to - // override a LocaleConfig of another package. - if (!isPackageOwnedByCaller(appPackageName, userId)) { - enforceSetAppSpecificLocaleConfigPermission(); - } + // This function handles two types of set operations: + // 1.) A normal, an app overrides its own LocaleConfig. + // 2.) A privileged system application or service is granted the necessary permission to + // override a LocaleConfig of another package. + if (!isPackageOwnedByCaller(appPackageName, userId, null, atomRecord)) { + enforceSetAppSpecificLocaleConfigPermission(atomRecord); + } - final long token = Binder.clearCallingIdentity(); - try { - setOverrideLocaleConfigUnchecked(appPackageName, userId, localeConfig); + final long token = Binder.clearCallingIdentity(); + try { + setOverrideLocaleConfigUnchecked(appPackageName, userId, localeConfig, atomRecord); + } finally { + Binder.restoreCallingIdentity(token); + } } finally { - Binder.restoreCallingIdentity(token); + logAppSupportedLocalesChangedMetric(atomRecord); } - - //TODO: Add metrics to monitor the usage by applications } + private void setOverrideLocaleConfigUnchecked(@NonNull String appPackageName, - @UserIdInt int userId, @Nullable LocaleConfig overridelocaleConfig) { + @UserIdInt int userId, @Nullable LocaleConfig overridelocaleConfig, + @NonNull AppSupportedLocalesChangedAtomRecord atomRecord) { synchronized (mWriteLock) { if (DEBUG) { Slog.d(TAG, @@ -609,8 +613,18 @@ public class LocaleManagerService extends SystemService { Slog.d(TAG, "remove the override LocaleConfig"); file.delete(); } + atomRecord.setOverrideRemoved(true); + atomRecord.setStatus(FrameworkStatsLog + .APP_SUPPORTED_LOCALES_CHANGED__STATUS__SUCCESS); return; } else { + if (overridelocaleConfig.isSameLocaleConfig( + getOverrideLocaleConfig(appPackageName, userId))) { + Slog.d(TAG, "the same override, ignore it"); + atomRecord.setSameAsPrevConfig(true); + return; + } + LocaleList localeList = overridelocaleConfig.getSupportedLocales(); // Normally the LocaleList object should not be null. However we reassign it as the // empty list in case it happens. @@ -621,6 +635,7 @@ public class LocaleManagerService extends SystemService { Slog.d(TAG, "setOverrideLocaleConfig, localeList: " + localeList.toLanguageTags()); } + atomRecord.setNumLocales(localeList.size()); // Store the override LocaleConfig to the file storage. final AtomicFile atomicFile = new AtomicFile(file); @@ -633,11 +648,25 @@ public class LocaleManagerService extends SystemService { if (stream != null) { atomicFile.failWrite(stream); } + atomRecord.setStatus(FrameworkStatsLog + .APP_SUPPORTED_LOCALES_CHANGED__STATUS__FAILURE_WRITE_TO_STORAGE); return; } atomicFile.finishWrite(stream); // Clear per-app locales if they are not in the override LocaleConfig. removeUnsupportedAppLocales(appPackageName, userId, overridelocaleConfig); + try { + Context appContext = mContext.createPackageContext(appPackageName, 0); + if (overridelocaleConfig.isSameLocaleConfig( + LocaleConfig.fromContextIgnoringOverride(appContext))) { + Slog.d(TAG, "setOverrideLocaleConfig, same as the app's LocaleConfig"); + atomRecord.setSameAsResConfig(true); + } + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Unknown package name " + appPackageName); + } + atomRecord.setStatus(FrameworkStatsLog + .APP_SUPPORTED_LOCALES_CHANGED__STATUS__SUCCESS); if (DEBUG) { Slog.i(TAG, "Successfully written to " + atomicFile); } @@ -677,10 +706,17 @@ public class LocaleManagerService extends SystemService { } } - private void enforceSetAppSpecificLocaleConfigPermission() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.SET_APP_SPECIFIC_LOCALECONFIG, - "setOverrideLocaleConfig"); + private void enforceSetAppSpecificLocaleConfigPermission( + AppSupportedLocalesChangedAtomRecord atomRecord) { + try { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_APP_SPECIFIC_LOCALECONFIG, + "setOverrideLocaleConfig"); + } catch (SecurityException e) { + atomRecord.setStatus(FrameworkStatsLog + .APP_SUPPORTED_LOCALES_CHANGED__STATUS__FAILURE_PERMISSION_ABSENT); + throw e; + } } /** @@ -796,4 +832,16 @@ public class LocaleManagerService extends SystemService { final File dir = new File(Environment.getDataSystemDeDirectory(userId), LOCALE_CONFIGS); return new File(dir, appPackageName + SUFFIX_FILE_NAME); } + + private void logAppSupportedLocalesChangedMetric( + @NonNull AppSupportedLocalesChangedAtomRecord atomRecord) { + FrameworkStatsLog.write(FrameworkStatsLog.APP_SUPPORTED_LOCALES_CHANGED, + atomRecord.mCallingUid, + atomRecord.mTargetUid, + atomRecord.mNumLocales, + atomRecord.mOverrideRemoved, + atomRecord.mSameAsResConfig, + atomRecord.mSameAsPrevConfig, + atomRecord.mStatus); + } } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 0c6985574693..5a832b78487c 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -1065,18 +1065,7 @@ public class LockSettingsService extends ILockSettings.Stub { mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsHave"); } - private static final String[] UNPROTECTED_SETTINGS = { - // These three LOCK_PATTERN_* settings have traditionally been readable via the public API - // android.provider.Settings.{System,Secure}.getString() without any permission. - Settings.Secure.LOCK_PATTERN_ENABLED, - Settings.Secure.LOCK_PATTERN_VISIBLE, - Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, - }; - private final void checkDatabaseReadPermission(String requestedKey, int userId) { - if (ArrayUtils.contains(UNPROTECTED_SETTINGS, requestedKey)) { - return; - } if (!hasPermission(PERMISSION)) { throw new SecurityException("uid=" + getCallingUid() + " needs permission " + PERMISSION + " to read " + requestedKey + " for user " + userId); @@ -1190,9 +1179,6 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public boolean getBoolean(String key, boolean defaultValue, int userId) { checkDatabaseReadPermission(key, userId); - if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) { - return getCredentialTypeInternal(userId) == CREDENTIAL_TYPE_PATTERN; - } return mStorage.getBoolean(key, defaultValue, userId); } diff --git a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java new file mode 100644 index 000000000000..eb997badca52 --- /dev/null +++ b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO; +import static android.bluetooth.BluetoothAdapter.STATE_CONNECTED; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothLeAudio; +import android.bluetooth.BluetoothProfile; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.media.AudioSystem; +import android.media.MediaRoute2Info; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Slog; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Controls bluetooth routes and provides selected route override. + * + * <p>The controller offers similar functionality to {@link LegacyBluetoothRouteController} but does + * not support routes selection logic. Instead, relies on external clients to make a decision + * about currently selected route. + * + * <p>Selected route override should be used by {@link AudioManager} which is aware of Audio + * Policies. + */ +/* package */ class AudioPoliciesBluetoothRouteController + implements BluetoothRouteController { + private static final String TAG = "APBtRouteController"; + + private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_"; + private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_"; + + @NonNull + private final AdapterStateChangedReceiver mAdapterStateChangedReceiver = + new AdapterStateChangedReceiver(); + + @NonNull + private final DeviceStateChangedReceiver mDeviceStateChangedReceiver = + new DeviceStateChangedReceiver(); + + @NonNull + private final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>(); + + @NonNull + private final SparseIntArray mVolumeMap = new SparseIntArray(); + + @NonNull + private final Context mContext; + @NonNull + private final BluetoothAdapter mBluetoothAdapter; + @NonNull + private final BluetoothRouteController.BluetoothRoutesUpdatedListener mListener; + @NonNull + private final BluetoothProfileMonitor mBluetoothProfileMonitor; + @NonNull + private final AudioManager mAudioManager; + + @Nullable + private BluetoothRouteInfo mSelectedBluetoothRoute; + + AudioPoliciesBluetoothRouteController(@NonNull Context context, + @NonNull BluetoothAdapter bluetoothAdapter, + @NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) { + this(context, bluetoothAdapter, + new BluetoothProfileMonitor(context, bluetoothAdapter), listener); + } + + @VisibleForTesting + AudioPoliciesBluetoothRouteController(@NonNull Context context, + @NonNull BluetoothAdapter bluetoothAdapter, + @NonNull BluetoothProfileMonitor bluetoothProfileMonitor, + @NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) { + Objects.requireNonNull(context); + Objects.requireNonNull(bluetoothAdapter); + Objects.requireNonNull(bluetoothProfileMonitor); + Objects.requireNonNull(listener); + + mContext = context; + mBluetoothAdapter = bluetoothAdapter; + mBluetoothProfileMonitor = bluetoothProfileMonitor; + mAudioManager = mContext.getSystemService(AudioManager.class); + mListener = listener; + + updateBluetoothRoutes(); + } + + @Override + public void start(UserHandle user) { + mBluetoothProfileMonitor.start(); + + IntentFilter adapterStateChangedIntentFilter = new IntentFilter(); + + adapterStateChangedIntentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + mContext.registerReceiverAsUser(mAdapterStateChangedReceiver, user, + adapterStateChangedIntentFilter, null, null); + + IntentFilter deviceStateChangedIntentFilter = new IntentFilter(); + + deviceStateChangedIntentFilter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); + deviceStateChangedIntentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); + deviceStateChangedIntentFilter.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED); + deviceStateChangedIntentFilter.addAction( + BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); + deviceStateChangedIntentFilter.addAction( + BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); + deviceStateChangedIntentFilter.addAction( + BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); + + mContext.registerReceiverAsUser(mDeviceStateChangedReceiver, user, + deviceStateChangedIntentFilter, null, null); + } + + @Override + public void stop() { + mContext.unregisterReceiver(mAdapterStateChangedReceiver); + mContext.unregisterReceiver(mDeviceStateChangedReceiver); + } + + @Override + public boolean selectRoute(@Nullable String deviceAddress) { + synchronized (this) { + // Fetch all available devices in order to avoid race conditions with Bluetooth stack. + updateBluetoothRoutes(); + + if (deviceAddress == null) { + mSelectedBluetoothRoute = null; + return true; + } + + BluetoothRouteInfo bluetoothRouteInfo = mBluetoothRoutes.get(deviceAddress); + + if (bluetoothRouteInfo == null) { + Slog.w(TAG, "Cannot find bluetooth route for " + deviceAddress); + return false; + } + + mSelectedBluetoothRoute = bluetoothRouteInfo; + setRouteConnectionState(mSelectedBluetoothRoute, STATE_CONNECTED); + + updateConnectivityStateForDevicesInTheSameGroup(); + + return true; + } + } + + /** + * Updates connectivity state for devices in the same devices group. + * + * <p>{@link BluetoothProfile#LE_AUDIO} and {@link BluetoothProfile#HEARING_AID} support + * grouping devices. Devices that belong to the same group should have the same routeId but + * different physical address. + * + * <p>In case one of the devices from the group is selected then other devices should also + * reflect this by changing their connectivity status to + * {@link MediaRoute2Info#CONNECTION_STATE_CONNECTED}. + */ + private void updateConnectivityStateForDevicesInTheSameGroup() { + synchronized (this) { + for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) { + if (TextUtils.equals(btRoute.mRoute.getId(), mSelectedBluetoothRoute.mRoute.getId()) + && !TextUtils.equals(btRoute.mBtDevice.getAddress(), + mSelectedBluetoothRoute.mBtDevice.getAddress())) { + setRouteConnectionState(btRoute, STATE_CONNECTED); + } + } + } + } + + @Override + public void transferTo(@Nullable String routeId) { + if (routeId == null) { + mBluetoothAdapter.removeActiveDevice(ACTIVE_DEVICE_AUDIO); + return; + } + + BluetoothRouteInfo btRouteInfo = findBluetoothRouteWithRouteId(routeId); + + if (btRouteInfo == null) { + Slog.w(TAG, "transferTo: Unknown route. ID=" + routeId); + return; + } + + mBluetoothAdapter.setActiveDevice(btRouteInfo.mBtDevice, ACTIVE_DEVICE_AUDIO); + } + + @Nullable + private BluetoothRouteInfo findBluetoothRouteWithRouteId(@Nullable String routeId) { + if (routeId == null) { + return null; + } + synchronized (this) { + for (BluetoothRouteInfo btRouteInfo : mBluetoothRoutes.values()) { + if (TextUtils.equals(btRouteInfo.mRoute.getId(), routeId)) { + return btRouteInfo; + } + } + } + return null; + } + + private void updateBluetoothRoutes() { + Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices(); + + if (bondedDevices == null) { + return; + } + + synchronized (this) { + mBluetoothRoutes.clear(); + + // We need to query all available to BT stack devices in order to avoid inconsistency + // between external services, like, AndroidManager, and BT stack. + for (BluetoothDevice device : bondedDevices) { + if (isDeviceConnected(device)) { + BluetoothRouteInfo newBtRoute = createBluetoothRoute(device); + if (newBtRoute.mConnectedProfiles.size() > 0) { + mBluetoothRoutes.put(device.getAddress(), newBtRoute); + } + } + } + } + } + + @VisibleForTesting + /* package */ boolean isDeviceConnected(@NonNull BluetoothDevice device) { + return device.isConnected(); + } + + @Nullable + @Override + public MediaRoute2Info getSelectedRoute() { + synchronized (this) { + if (mSelectedBluetoothRoute == null) { + return null; + } + + return mSelectedBluetoothRoute.mRoute; + } + } + + @NonNull + @Override + public List<MediaRoute2Info> getTransferableRoutes() { + List<MediaRoute2Info> routes = getAllBluetoothRoutes(); + synchronized (this) { + if (mSelectedBluetoothRoute != null) { + routes.remove(mSelectedBluetoothRoute.mRoute); + } + } + return routes; + } + + @NonNull + @Override + public List<MediaRoute2Info> getAllBluetoothRoutes() { + List<MediaRoute2Info> routes = new ArrayList<>(); + List<String> routeIds = new ArrayList<>(); + + MediaRoute2Info selectedRoute = getSelectedRoute(); + if (selectedRoute != null) { + routes.add(selectedRoute); + routeIds.add(selectedRoute.getId()); + } + + synchronized (this) { + for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) { + // A pair of hearing aid devices or having the same hardware address + if (routeIds.contains(btRoute.mRoute.getId())) { + continue; + } + routes.add(btRoute.mRoute); + routeIds.add(btRoute.mRoute.getId()); + } + } + return routes; + } + + @Override + public boolean updateVolumeForDevices(int devices, int volume) { + int routeType; + if ((devices & (AudioSystem.DEVICE_OUT_HEARING_AID)) != 0) { + routeType = MediaRoute2Info.TYPE_HEARING_AID; + } else if ((devices & (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP + | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES + | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) { + routeType = MediaRoute2Info.TYPE_BLUETOOTH_A2DP; + } else if ((devices & (AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0) { + routeType = MediaRoute2Info.TYPE_BLE_HEADSET; + } else { + return false; + } + + synchronized (this) { + mVolumeMap.put(routeType, volume); + if (mSelectedBluetoothRoute == null + || mSelectedBluetoothRoute.mRoute.getType() != routeType) { + return false; + } + + mSelectedBluetoothRoute.mRoute = + new MediaRoute2Info.Builder(mSelectedBluetoothRoute.mRoute) + .setVolume(volume) + .build(); + } + + notifyBluetoothRoutesUpdated(); + return true; + } + + private void notifyBluetoothRoutesUpdated() { + mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes()); + } + + private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) { + BluetoothRouteInfo + newBtRoute = new BluetoothRouteInfo(); + newBtRoute.mBtDevice = device; + + String routeId = device.getAddress(); + String deviceName = device.getName(); + if (TextUtils.isEmpty(deviceName)) { + deviceName = mContext.getResources().getText(R.string.unknownName).toString(); + } + int type = MediaRoute2Info.TYPE_BLUETOOTH_A2DP; + newBtRoute.mConnectedProfiles = new SparseBooleanArray(); + if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.A2DP, device)) { + newBtRoute.mConnectedProfiles.put(BluetoothProfile.A2DP, true); + } + if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.HEARING_AID, device)) { + newBtRoute.mConnectedProfiles.put(BluetoothProfile.HEARING_AID, true); + // Intentionally assign the same ID for a pair of devices to publish only one of them. + routeId = HEARING_AID_ROUTE_ID_PREFIX + + mBluetoothProfileMonitor.getGroupId(BluetoothProfile.HEARING_AID, device); + type = MediaRoute2Info.TYPE_HEARING_AID; + } + if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.LE_AUDIO, device)) { + newBtRoute.mConnectedProfiles.put(BluetoothProfile.LE_AUDIO, true); + routeId = LE_AUDIO_ROUTE_ID_PREFIX + + mBluetoothProfileMonitor.getGroupId(BluetoothProfile.LE_AUDIO, device); + type = MediaRoute2Info.TYPE_BLE_HEADSET; + } + + // Current volume will be set when connected. + newBtRoute.mRoute = new MediaRoute2Info.Builder(routeId, deviceName) + .addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO) + .addFeature(MediaRoute2Info.FEATURE_LOCAL_PLAYBACK) + .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED) + .setDescription(mContext.getResources().getText( + R.string.bluetooth_a2dp_audio_route_name).toString()) + .setType(type) + .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) + .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) + .setAddress(device.getAddress()) + .build(); + return newBtRoute; + } + + private void setRouteConnectionState(@NonNull BluetoothRouteInfo btRoute, + @MediaRoute2Info.ConnectionState int state) { + if (btRoute == null) { + Slog.w(TAG, "setRouteConnectionState: route shouldn't be null"); + return; + } + if (btRoute.mRoute.getConnectionState() == state) { + return; + } + + MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(btRoute.mRoute) + .setConnectionState(state); + builder.setType(btRoute.getRouteType()); + + + + if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) { + int currentVolume; + synchronized (this) { + currentVolume = mVolumeMap.get(btRoute.getRouteType(), 0); + } + builder.setVolume(currentVolume); + } + + btRoute.mRoute = builder.build(); + } + + private static class BluetoothRouteInfo { + private BluetoothDevice mBtDevice; + private MediaRoute2Info mRoute; + private SparseBooleanArray mConnectedProfiles; + + @MediaRoute2Info.Type + int getRouteType() { + // Let hearing aid profile have a priority. + if (mConnectedProfiles.get(BluetoothProfile.HEARING_AID, false)) { + return MediaRoute2Info.TYPE_HEARING_AID; + } + + if (mConnectedProfiles.get(BluetoothProfile.LE_AUDIO, false)) { + return MediaRoute2Info.TYPE_BLE_HEADSET; + } + + return MediaRoute2Info.TYPE_BLUETOOTH_A2DP; + } + } + + private class AdapterStateChangedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); + if (state == BluetoothAdapter.STATE_OFF + || state == BluetoothAdapter.STATE_TURNING_OFF) { + synchronized (AudioPoliciesBluetoothRouteController.this) { + mBluetoothRoutes.clear(); + } + notifyBluetoothRoutesUpdated(); + } else if (state == BluetoothAdapter.STATE_ON) { + updateBluetoothRoutes(); + + boolean shouldCallListener; + synchronized (AudioPoliciesBluetoothRouteController.this) { + shouldCallListener = !mBluetoothRoutes.isEmpty(); + } + + if (shouldCallListener) { + notifyBluetoothRoutesUpdated(); + } + } + } + } + + private class DeviceStateChangedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED: + case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED: + case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED: + case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED: + case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED: + case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED: + updateBluetoothRoutes(); + notifyBluetoothRoutesUpdated(); + } + } + } +} diff --git a/services/core/java/com/android/server/media/BluetoothProfileMonitor.java b/services/core/java/com/android/server/media/BluetoothProfileMonitor.java new file mode 100644 index 000000000000..b129dd0f74c6 --- /dev/null +++ b/services/core/java/com/android/server/media/BluetoothProfileMonitor.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothLeAudio; +import android.bluetooth.BluetoothProfile; +import android.content.Context; + +import java.util.Objects; + +/* package */ class BluetoothProfileMonitor { + + /* package */ static final long GROUP_ID_NO_GROUP = -1L; + + @NonNull + private final ProfileListener mProfileListener = new ProfileListener(); + + @NonNull + private final Context mContext; + @NonNull + private final BluetoothAdapter mBluetoothAdapter; + + @Nullable + private BluetoothA2dp mA2dpProfile; + @Nullable + private BluetoothHearingAid mHearingAidProfile; + @Nullable + private BluetoothLeAudio mLeAudioProfile; + + @Nullable + private OnProfileChangedListener mOnProfileChangedListener; + + BluetoothProfileMonitor(@NonNull Context context, + @NonNull BluetoothAdapter bluetoothAdapter) { + Objects.requireNonNull(context); + Objects.requireNonNull(bluetoothAdapter); + + mContext = context; + mBluetoothAdapter = bluetoothAdapter; + } + + /* package */ synchronized void setOnProfileChangedListener( + @NonNull OnProfileChangedListener listener) { + mOnProfileChangedListener = listener; + } + + /* package */ void start() { + mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP); + mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID); + mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.LE_AUDIO); + } + + /* package */ boolean isProfileSupported(int profile, @NonNull BluetoothDevice device) { + BluetoothProfile bluetoothProfile; + + synchronized (this) { + switch (profile) { + case BluetoothProfile.A2DP: + bluetoothProfile = mA2dpProfile; + break; + case BluetoothProfile.LE_AUDIO: + bluetoothProfile = mLeAudioProfile; + break; + case BluetoothProfile.HEARING_AID: + bluetoothProfile = mHearingAidProfile; + break; + default: + throw new IllegalArgumentException(profile + + " is not supported as Bluetooth profile"); + } + } + + if (bluetoothProfile == null) { + return false; + } + + return bluetoothProfile.getConnectedDevices().contains(device); + } + + /* package */ long getGroupId(int profile, @NonNull BluetoothDevice device) { + synchronized (this) { + switch (profile) { + case BluetoothProfile.A2DP: + return GROUP_ID_NO_GROUP; + case BluetoothProfile.LE_AUDIO: + return mLeAudioProfile == null ? GROUP_ID_NO_GROUP : mLeAudioProfile.getGroupId( + device); + case BluetoothProfile.HEARING_AID: + return mHearingAidProfile == null + ? GROUP_ID_NO_GROUP : mHearingAidProfile.getHiSyncId(device); + default: + throw new IllegalArgumentException(profile + + " is not supported as Bluetooth profile"); + } + } + } + + /* package */ interface OnProfileChangedListener { + void onProfileChange(int profile); + } + + private final class ProfileListener implements BluetoothProfile.ServiceListener { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + OnProfileChangedListener listener; + + synchronized (BluetoothProfileMonitor.this) { + switch (profile) { + case BluetoothProfile.A2DP: + mA2dpProfile = (BluetoothA2dp) proxy; + break; + case BluetoothProfile.HEARING_AID: + mHearingAidProfile = (BluetoothHearingAid) proxy; + break; + case BluetoothProfile.LE_AUDIO: + mLeAudioProfile = (BluetoothLeAudio) proxy; + break; + default: + return; + } + + listener = mOnProfileChangedListener; + } + + if (listener != null) { + listener.onProfileChange(profile); + } + } + + @Override + public void onServiceDisconnected(int profile) { + OnProfileChangedListener listener; + + synchronized (BluetoothProfileMonitor.this) { + switch (profile) { + case BluetoothProfile.A2DP: + mA2dpProfile = null; + break; + case BluetoothProfile.HEARING_AID: + mHearingAidProfile = null; + break; + case BluetoothProfile.LE_AUDIO: + mLeAudioProfile = null; + break; + default: + return; + } + + listener = mOnProfileChangedListener; + } + + if (listener != null) { + listener.onProfileChange(profile); + } + } + } + +} diff --git a/services/core/java/com/android/server/media/BluetoothRouteController.java b/services/core/java/com/android/server/media/BluetoothRouteController.java index 08691d035f29..d4a118458952 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteController.java +++ b/services/core/java/com/android/server/media/BluetoothRouteController.java @@ -68,6 +68,17 @@ import java.util.Objects; */ void stop(); + + /** + * Selects the route with the given {@code deviceAddress}. + * + * @param deviceAddress The physical address of the device to select. May be null to unselect + * the currently selected device. + * @return Whether the selection succeeds. If the selection fails, the state of the instance + * remains unaltered. + */ + boolean selectRoute(@Nullable String deviceAddress); + /** * Transfers Bluetooth output to the given route. * @@ -145,6 +156,12 @@ import java.util.Objects; } @Override + public boolean selectRoute(String deviceAddress) { + // no op + return false; + } + + @Override public void transferTo(String routeId) { // no op } diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java new file mode 100644 index 000000000000..8bd6416a6ddb --- /dev/null +++ b/services/core/java/com/android/server/media/DeviceRouteController.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; +import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO; +import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK; +import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER; +import static android.media.MediaRoute2Info.TYPE_DOCK; +import static android.media.MediaRoute2Info.TYPE_HDMI; +import static android.media.MediaRoute2Info.TYPE_USB_DEVICE; +import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; +import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.media.AudioManager; +import android.media.AudioRoutesInfo; +import android.media.IAudioRoutesObserver; +import android.media.IAudioService; +import android.media.MediaRoute2Info; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Slog; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Objects; + +/** + * Controls device routes. + * + * <p>A device route is a system wired route, for example, built-in speaker, wired + * headsets and headphones, dock, hdmi, or usb devices. + * + * <p>Thread safe. + * + * @see SystemMediaRoute2Provider + */ +/* package */ final class DeviceRouteController { + + private static final String TAG = "WiredRoutesController"; + + private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE"; + + @NonNull + private final Context mContext; + @NonNull + private final AudioManager mAudioManager; + @NonNull + private final IAudioService mAudioService; + + @NonNull + private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener; + @NonNull + private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver(); + + private int mDeviceVolume; + private MediaRoute2Info mDeviceRoute; + + /* package */ static DeviceRouteController createInstance(@NonNull Context context, + @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) { + AudioManager audioManager = context.getSystemService(AudioManager.class); + IAudioService audioService = IAudioService.Stub.asInterface( + ServiceManager.getService(Context.AUDIO_SERVICE)); + + return new DeviceRouteController(context, + audioManager, + audioService, + onDeviceRouteChangedListener); + } + + @VisibleForTesting + /* package */ DeviceRouteController(@NonNull Context context, + @NonNull AudioManager audioManager, + @NonNull IAudioService audioService, + @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) { + Objects.requireNonNull(context); + Objects.requireNonNull(audioManager); + Objects.requireNonNull(audioService); + Objects.requireNonNull(onDeviceRouteChangedListener); + + mContext = context; + mOnDeviceRouteChangedListener = onDeviceRouteChangedListener; + + mAudioManager = audioManager; + mAudioService = audioService; + + AudioRoutesInfo newAudioRoutes = null; + try { + newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); + } catch (RemoteException e) { + Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e); + } + + mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes); + } + + @NonNull + /* package */ synchronized MediaRoute2Info getDeviceRoute() { + return mDeviceRoute; + } + + /* package */ synchronized boolean updateVolume(int volume) { + if (mDeviceVolume == volume) { + return false; + } + + mDeviceVolume = volume; + mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) + .setVolume(volume) + .build(); + + return true; + } + + private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) { + int name = R.string.default_audio_route_name; + int type = TYPE_BUILTIN_SPEAKER; + + if (newRoutes != null) { + if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) { + type = TYPE_WIRED_HEADPHONES; + name = com.android.internal.R.string.default_audio_route_name_headphones; + } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) { + type = TYPE_WIRED_HEADSET; + name = com.android.internal.R.string.default_audio_route_name_headphones; + } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { + type = TYPE_DOCK; + name = com.android.internal.R.string.default_audio_route_name_dock_speakers; + } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) { + type = TYPE_HDMI; + name = com.android.internal.R.string.default_audio_route_name_external_device; + } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) { + type = TYPE_USB_DEVICE; + name = com.android.internal.R.string.default_audio_route_name_usb; + } + } + + synchronized (this) { + return new MediaRoute2Info.Builder( + DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) + .setVolumeHandling(mAudioManager.isVolumeFixed() + ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED + : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) + .setVolume(mDeviceVolume) + .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) + .setType(type) + .addFeature(FEATURE_LIVE_AUDIO) + .addFeature(FEATURE_LIVE_VIDEO) + .addFeature(FEATURE_LOCAL_PLAYBACK) + .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED) + .build(); + } + } + + private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) { + mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute); + } + + /* package */ interface OnDeviceRouteChangedListener { + void onDeviceRouteChanged(@NonNull MediaRoute2Info deviceRoute); + } + + private class AudioRoutesObserver extends IAudioRoutesObserver.Stub { + + @Override + public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) { + MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes); + synchronized (DeviceRouteController.this) { + mDeviceRoute = deviceRoute; + } + notifyDeviceRouteUpdate(deviceRoute); + } + } + +} diff --git a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java index 7979d2a3fb3b..e31a7fc5d5ff 100644 --- a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java +++ b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java @@ -52,7 +52,7 @@ import java.util.Map; import java.util.Set; class LegacyBluetoothRouteController implements BluetoothRouteController { - private static final String TAG = "BTRouteProvider"; + private static final String TAG = "LBtRouteProvider"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_"; @@ -132,6 +132,12 @@ class LegacyBluetoothRouteController implements BluetoothRouteController { mContext.unregisterReceiver(mDeviceStateChangedReceiver); } + @Override + public boolean selectRoute(String deviceAddress) { + // No-op as the class decides if a route is selected based on Bluetooth events. + return false; + } + /** * Transfers to a given bluetooth route. * The dedicated BT device with the route would be activated. diff --git a/services/core/java/com/android/server/media/MediaFeatureFlagManager.java b/services/core/java/com/android/server/media/MediaFeatureFlagManager.java new file mode 100644 index 000000000000..723cda056694 --- /dev/null +++ b/services/core/java/com/android/server/media/MediaFeatureFlagManager.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import android.annotation.StringDef; +import android.provider.DeviceConfig; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/* package */ class MediaFeatureFlagManager { + + /** + * Namespace for media better together features. + */ + private static final String NAMESPACE_MEDIA_BETTER_TOGETHER = "media_better_together"; + + @StringDef(prefix = "FEATURE_", value = { + FEATURE_IS_USING_LEGACY_BLUETOOTH_CONTROLLER + }) + @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) + @Retention(RetentionPolicy.SOURCE) + /* package */ @interface MediaFeatureFlag {} + + /** + * Whether to use old legacy implementation of BluetoothRouteController or new + * 'Audio Strategies'-aware controller. + */ + /* package */ static final @MediaFeatureFlag String + FEATURE_IS_USING_LEGACY_BLUETOOTH_CONTROLLER = + "BluetoothRouteController__enable_legacy_bluetooth_routes_controller"; + + private static final MediaFeatureFlagManager sInstance = new MediaFeatureFlagManager(); + + private MediaFeatureFlagManager() { + // Empty to prevent instantiation. + } + + /* package */ MediaFeatureFlagManager getInstance() { + return sInstance; + } + + /** + * Returns a boolean value from {@link DeviceConfig} from the system_time namespace, or + * {@code defaultValue} if there is no explicit value set. + */ + public boolean getBoolean(@MediaFeatureFlag String key, boolean defaultValue) { + return DeviceConfig.getBoolean(NAMESPACE_MEDIA_BETTER_TOGETHER, key, defaultValue); + } +} diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 76ff19fcb322..638e81a5df36 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -16,25 +16,12 @@ package com.android.server.media; -import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; -import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO; -import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK; -import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER; -import static android.media.MediaRoute2Info.TYPE_DOCK; -import static android.media.MediaRoute2Info.TYPE_HDMI; -import static android.media.MediaRoute2Info.TYPE_USB_DEVICE; -import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; -import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; - import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; -import android.media.AudioRoutesInfo; -import android.media.IAudioRoutesObserver; -import android.media.IAudioService; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; @@ -43,14 +30,11 @@ import android.media.RoutingSessionInfo; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.Slog; -import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import java.util.Objects; @@ -68,23 +52,21 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SystemMediaRoute2Provider.class.getName()); static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE"; - static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE"; static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION"; private final AudioManager mAudioManager; - private final IAudioService mAudioService; private final Handler mHandler; private final Context mContext; private final UserHandle mUser; + + private final DeviceRouteController mDeviceRouteController; private final BluetoothRouteController mBtRouteProvider; private String mSelectedRouteId; // For apps without MODIFYING_AUDIO_ROUTING permission. // This should be the currently selected route. MediaRoute2Info mDefaultRoute; - MediaRoute2Info mDeviceRoute; RoutingSessionInfo mDefaultSessionInfo; - int mDeviceVolume; private final AudioManagerBroadcastReceiver mAudioReceiver = new AudioManagerBroadcastReceiver(); @@ -93,19 +75,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { @GuardedBy("mRequestLock") private volatile SessionCreationRequest mPendingSessionCreationRequest; - final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() { - @Override - public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) { - mHandler.post(() -> { - updateDeviceRoute(newRoutes); - notifyProviderState(); - if (updateSessionInfosIfNeeded()) { - notifySessionInfoUpdated(); - } - }); - } - }; - SystemMediaRoute2Provider(Context context, UserHandle user) { super(COMPONENT_NAME); mIsSystemRouteProvider = true; @@ -114,8 +83,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { mHandler = new Handler(Looper.getMainLooper()); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - mAudioService = IAudioService.Stub.asInterface( - ServiceManager.getService(Context.AUDIO_SERVICE)); mBtRouteProvider = BluetoothRouteController.createInstance(context, (routes) -> { publishProviderState(); @@ -124,15 +91,18 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } }); - AudioRoutesInfo newAudioRoutes = null; - try { - newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); - } catch (RemoteException e) { - } + mDeviceRouteController = DeviceRouteController.createInstance(context, (deviceRoute) -> { + mHandler.post(() -> { + publishProviderState(); + if (updateSessionInfosIfNeeded()) { + notifySessionInfoUpdated(); + } + }); + }); - // The methods below should be called after all fields are initialized, as they + // These methods below should be called after all fields are initialized, as they // access the fields inside. - updateDeviceRoute(newAudioRoutes); + updateProviderState(); updateSessionInfosIfNeeded(); } @@ -216,7 +186,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { // The currently selected route is the default route. return; } - if (TextUtils.equals(routeId, mDeviceRoute.getId())) { + + MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); + if (TextUtils.equals(routeId, deviceRoute.getId())) { mBtRouteProvider.transferTo(null); } else { mBtRouteProvider.transferTo(routeId); @@ -254,9 +226,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (mSessionInfos.isEmpty()) { return null; } + + MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); + RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( SYSTEM_SESSION_ID, packageName).setSystemSession(true); - builder.addSelectedRoute(mDeviceRoute.getId()); + builder.addSelectedRoute(deviceRoute.getId()); for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) { builder.addTransferableRoute(route.getId()); } @@ -264,47 +239,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } - private void updateDeviceRoute(AudioRoutesInfo newRoutes) { - int name = R.string.default_audio_route_name; - int type = TYPE_BUILTIN_SPEAKER; - if (newRoutes != null) { - if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) { - type = TYPE_WIRED_HEADPHONES; - name = com.android.internal.R.string.default_audio_route_name_headphones; - } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) { - type = TYPE_WIRED_HEADSET; - name = com.android.internal.R.string.default_audio_route_name_headphones; - } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { - type = TYPE_DOCK; - name = com.android.internal.R.string.default_audio_route_name_dock_speakers; - } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) { - type = TYPE_HDMI; - name = com.android.internal.R.string.default_audio_route_name_external_device; - } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) { - type = TYPE_USB_DEVICE; - name = com.android.internal.R.string.default_audio_route_name_usb; - } - } - - mDeviceRoute = new MediaRoute2Info.Builder( - DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) - .setVolumeHandling(mAudioManager.isVolumeFixed() - ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED - : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) - .setVolume(mDeviceVolume) - .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) - .setType(type) - .addFeature(FEATURE_LIVE_AUDIO) - .addFeature(FEATURE_LIVE_VIDEO) - .addFeature(FEATURE_LOCAL_PLAYBACK) - .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED) - .build(); - updateProviderState(); - } - private void updateProviderState() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); - builder.addRoute(mDeviceRoute); + + // We must have a device route in the provider info. + builder.addRoute(mDeviceRouteController.getDeviceRoute()); + for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) { builder.addRoute(route); } @@ -327,11 +267,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SYSTEM_SESSION_ID, "" /* clientPackageName */) .setSystemSession(true); - MediaRoute2Info selectedRoute = mDeviceRoute; + MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); + MediaRoute2Info selectedRoute = deviceRoute; MediaRoute2Info selectedBtRoute = mBtRouteProvider.getSelectedRoute(); if (selectedBtRoute != null) { selectedRoute = selectedBtRoute; - builder.addTransferableRoute(mDeviceRoute.getId()); + builder.addTransferableRoute(deviceRoute.getId()); } mSelectedRouteId = selectedRoute.getId(); mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute) @@ -423,12 +364,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (mBtRouteProvider.updateVolumeForDevices(devices, volume)) { return; } - if (mDeviceVolume != volume) { - mDeviceVolume = volume; - mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) - .setVolume(volume) - .build(); - } + + mDeviceRouteController.updateVolume(volume); + publishProviderState(); } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 4b2c88c2bf06..27744626385e 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -20,7 +20,7 @@ import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY; -import static android.service.notification.DNDModeProto.ROOT_CONFIG; +import static android.service.notification.NotificationServiceProto.ROOT_CONFIG; import static android.util.StatsLog.ANNOTATION_ID_IS_UID; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index fac77486cd19..c232b3698bc0 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -678,8 +678,4 @@ public interface Computer extends PackageDataSnapshot { @NonNull Collection<SharedUserSetting> getAllSharedUsers(); - - boolean isChangeEnabled(long changeId, int uid); - - boolean isChangeEnabled(long changeId, ApplicationInfo info); } diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 21f661b4f21f..5984360a534c 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -125,7 +125,6 @@ import com.android.internal.util.CollectionUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.modules.utils.TypedXmlSerializer; -import com.android.server.compat.PlatformCompat; import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; @@ -575,7 +574,8 @@ public class ComputerEngine implements Computer { list = new ArrayList<>(1); list.add(ri); PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - this, list, false, intent, resolvedType, filterCallingUid); + mInjector.getCompatibility(), mComponentResolver, + list, false, intent, resolvedType, filterCallingUid); } } } else { @@ -591,7 +591,7 @@ public class ComputerEngine implements Computer { String callingPkgName = getInstantAppPackageName(filterCallingUid); boolean isRequesterInstantApp = isInstantApp(callingPkgName, userId); lockedResult.result = maybeAddInstantAppInstaller( - lockedResult.result, intent, resolvedType, flags, filterCallingUid, + lockedResult.result, intent, resolvedType, flags, userId, resolveForStart, isRequesterInstantApp); } if (lockedResult.sortResult) { @@ -604,7 +604,8 @@ public class ComputerEngine implements Computer { if (originalIntent != null) { // We also have to ensure all components match the original intent PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - this, list, false, originalIntent, resolvedType, filterCallingUid); + mInjector.getCompatibility(), mComponentResolver, + list, false, originalIntent, resolvedType, filterCallingUid); } return skipPostResolution ? list : applyPostResolutionFilter( @@ -686,7 +687,8 @@ public class ComputerEngine implements Computer { list = new ArrayList<>(1); list.add(ri); PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - this, list, false, intent, resolvedType, callingUid); + mInjector.getCompatibility(), mComponentResolver, + list, false, intent, resolvedType, callingUid); } } } else { @@ -697,7 +699,8 @@ public class ComputerEngine implements Computer { if (originalIntent != null) { // We also have to ensure all components match the original intent PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - this, list, false, originalIntent, resolvedType, callingUid); + mInjector.getCompatibility(), mComponentResolver, + list, false, originalIntent, resolvedType, callingUid); } return list; @@ -710,7 +713,7 @@ public class ComputerEngine implements Computer { String pkgName = intent.getPackage(); if (pkgName == null) { final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent, - resolvedType, flags, callingUid, userId); + resolvedType, flags, userId); if (resolveInfos == null) { return Collections.emptyList(); } @@ -720,7 +723,7 @@ public class ComputerEngine implements Computer { final AndroidPackage pkg = mPackages.get(pkgName); if (pkg != null) { final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent, - resolvedType, flags, pkg.getServices(), callingUid, + resolvedType, flags, pkg.getServices(), userId); if (resolveInfos == null) { return Collections.emptyList(); @@ -750,7 +753,7 @@ public class ComputerEngine implements Computer { {@link PackageManager.SKIP_CURRENT_PROFILE} set. */ result.addAll(filterIfNotSystemUser(mComponentResolver.queryActivities(this, - intent, resolvedType, flags, filterCallingUid, userId), userId)); + intent, resolvedType, flags, userId), userId)); } addInstant = isInstantAppResolutionAllowed(intent, result, userId, false /*skipPackageCheck*/, flags); @@ -774,7 +777,7 @@ public class ComputerEngine implements Computer { || !shouldFilterApplication(setting, filterCallingUid, userId))) { result.addAll(filterIfNotSystemUser(mComponentResolver.queryActivities(this, intent, resolvedType, flags, setting.getAndroidPackage().getActivities(), - filterCallingUid, userId), userId)); + userId), userId)); } if (result == null || result.size() == 0) { // the caller wants to resolve for a particular package; however, there @@ -1105,7 +1108,7 @@ public class ComputerEngine implements Computer { return null; } List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(this, intent, - resolvedType, flags, Binder.getCallingUid(), parentUserId); + resolvedType, flags, parentUserId); if (resultTargetUser == null || resultTargetUser.isEmpty()) { return null; @@ -1343,7 +1346,7 @@ public class ComputerEngine implements Computer { private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result, Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, - int callingUid, int userId, boolean resolveForStart, boolean isRequesterInstantApp) { + int userId, boolean resolveForStart, boolean isRequesterInstantApp) { // first, check to see if we've got an instant app already installed final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0; ResolveInfo localInstantApp = null; @@ -1356,7 +1359,6 @@ public class ComputerEngine implements Computer { | PackageManager.GET_RESOLVED_FILTER | PackageManager.MATCH_INSTANT | PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY, - callingUid, userId); for (int i = instantApps.size() - 1; i >= 0; --i) { final ResolveInfo info = instantApps.get(i); @@ -3691,13 +3693,10 @@ public class ComputerEngine implements Computer { ps, callingUid, component, TYPE_ACTIVITY, userId, true /* filterUninstall */)) { return false; } - final boolean callerBlocksNullAction = isChangeEnabled( - IntentFilter.BLOCK_NULL_ACTION_INTENTS, callingUid); for (int i=0; i< a.getIntents().size(); i++) { if (a.getIntents().get(i).getIntentFilter() .match(intent.getAction(), resolvedType, intent.getScheme(), - intent.getData(), intent.getCategories(), TAG, - /*supportWildcards*/ false, callerBlocksNullAction, null, null) >= 0) { + intent.getData(), intent.getCategories(), TAG) >= 0) { return true; } } @@ -5795,37 +5794,4 @@ public class ComputerEngine implements Computer { public UserInfo[] getUserInfos() { return mInjector.getUserManagerInternal().getUserInfos(); } - - @Override - public boolean isChangeEnabled(long changeId, int uid) { - final PlatformCompat compat = mInjector.getCompatibility(); - int appId = UserHandle.getAppId(uid); - SettingBase s = mSettings.getSettingBase(appId); - if (s instanceof PackageSetting) { - var ps = (PackageSetting) s; - if (ps.getAndroidPackage() == null) return false; - var info = new ApplicationInfo(); - info.packageName = ps.getPackageName(); - info.targetSdkVersion = ps.getAndroidPackage().getTargetSdkVersion(); - return compat.isChangeEnabledInternal(changeId, info); - } else if (s instanceof SharedUserSetting) { - var ss = (SharedUserSetting) s; - List<AndroidPackage> packages = ss.getPackages(); - for (int i = 0; i < packages.size(); ++i) { - var pkg = packages.get(i); - var info = new ApplicationInfo(); - info.packageName = pkg.getPackageName(); - info.targetSdkVersion = pkg.getTargetSdkVersion(); - if (compat.isChangeEnabledInternal(changeId, info)) { - return true; - } - } - } - return false; - } - - @Override - public boolean isChangeEnabled(long changeId, ApplicationInfo info) { - return mInjector.getCompatibility().isChangeEnabledInternal(changeId, info); - } } diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java b/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java index ee8f5602fad2..90d89c606686 100644 --- a/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java +++ b/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java @@ -23,7 +23,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; -import android.os.Binder; import android.util.SparseBooleanArray; import com.android.internal.app.IntentForwarderActivity; @@ -279,7 +278,7 @@ public final class DefaultCrossProfileResolver extends CrossProfileResolver { } List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(computer, intent, - resolvedType, flags, Binder.getCallingUid(), targetUserId); + resolvedType, flags, targetUserId); if (CollectionUtils.isEmpty(resultTargetUser)) { return null; } diff --git a/services/core/java/com/android/server/pm/NoFilteringResolver.java b/services/core/java/com/android/server/pm/NoFilteringResolver.java index e53e756b29bd..392389009398 100644 --- a/services/core/java/com/android/server/pm/NoFilteringResolver.java +++ b/services/core/java/com/android/server/pm/NoFilteringResolver.java @@ -102,7 +102,7 @@ public class NoFilteringResolver extends CrossProfileResolver { boolean hasNonNegativePriorityResult, Function<String, PackageStateInternal> pkgSettingFunction) { List<ResolveInfo> resolveInfos = mComponentResolver.queryActivities(computer, - intent, resolvedType, flags, Binder.getCallingUid(), targetUserId); + intent, resolvedType, flags, targetUserId); List<CrossProfileDomainInfo> crossProfileDomainInfos = new ArrayList<>(); if (resolveInfos != null) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6557ec565343..de5f0c4d523f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -6675,15 +6675,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Deprecated public void legacyDumpProfiles(String packageName, boolean dumpClassesAndMethods) throws LegacyDexoptDisabledException { - /* Only the shell, root, or the app user should be able to dump profiles. */ - final int callingUid = Binder.getCallingUid(); final Computer snapshot = snapshotComputer(); - final String[] callerPackageNames = snapshot.getPackagesForUid(callingUid); - if (!PackageManagerServiceUtils.isRootOrShell(callingUid) - && !ArrayUtils.contains(callerPackageNames, packageName)) { - throw new SecurityException("dumpProfiles"); - } - AndroidPackage pkg = snapshot.getPackage(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 9036d4c0615b..928ffa718c6f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -16,7 +16,6 @@ package com.android.server.pm; -import static android.content.IntentFilter.BLOCK_NULL_ACTION_INTENTS; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; @@ -95,6 +94,7 @@ import com.android.server.EventLogTags; import com.android.server.IntentResolver; import com.android.server.LocalManagerRegistry; import com.android.server.Watchdog; +import com.android.server.compat.PlatformCompat; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; @@ -1166,8 +1166,10 @@ public class PackageManagerServiceUtils { return (ps.getFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; } + // Static to give access to ComputeEngine public static void applyEnforceIntentFilterMatching( - Computer computer, List<ResolveInfo> resolveInfos, boolean isReceiver, + PlatformCompat compat, ComponentResolverApi resolver, + List<ResolveInfo> resolveInfos, boolean isReceiver, Intent intent, String resolvedType, int filterCallingUid) { if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return; @@ -1175,11 +1177,6 @@ public class PackageManagerServiceUtils { ? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM) : null; - final boolean callerBlocksNullAction = computer.isChangeEnabled( - BLOCK_NULL_ACTION_INTENTS, filterCallingUid); - - final ComponentResolverApi resolver = computer.getComponentResolver(); - for (int i = resolveInfos.size() - 1; i >= 0; --i) { final ComponentInfo info = resolveInfos.get(i).getComponentInfo(); @@ -1190,15 +1187,11 @@ public class PackageManagerServiceUtils { } // Only enforce filter matching if target app's target SDK >= T - if (!computer.isChangeEnabled( + if (!compat.isChangeEnabledInternal( ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, info.applicationInfo)) { continue; } - // Block null action intent if either source or target app's target SDK >= U - final boolean blockNullAction = callerBlocksNullAction - || computer.isChangeEnabled(BLOCK_NULL_ACTION_INTENTS, info.applicationInfo); - final ParsedMainComponent comp; if (info instanceof ActivityInfo) { if (isReceiver) { @@ -1220,8 +1213,7 @@ public class PackageManagerServiceUtils { boolean match = false; for (int j = 0, size = comp.getIntents().size(); j < size; ++j) { IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter(); - if (IntentResolver.intentMatchesFilter( - intentFilter, intent, resolvedType, blockNullAction)) { + if (IntentResolver.intentMatchesFilter(intentFilter, intent, resolvedType)) { match = true; break; } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 41592bd3b7be..586e1123fdc4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -391,6 +391,11 @@ class PackageManagerShellCommand extends ShellCommand { private int runLegacyDexoptCommand(@NonNull String cmd) throws RemoteException, LegacyDexoptDisabledException { Installer.checkLegacyDexoptDisabled(); + + if (!PackageManagerServiceUtils.isRootOrShell(Binder.getCallingUid())) { + throw new SecurityException("Dexopt shell commands need root or shell access"); + } + switch (cmd) { case "compile": return runCompile(); diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java index 8bca4a9fc779..a13c568f87a6 100644 --- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java +++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java @@ -466,14 +466,15 @@ final class ResolveIntentHelper { list = new ArrayList<>(1); list.add(ri); PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - computer, list, true, intent, resolvedType, filterCallingUid); + mPlatformCompat, componentResolver, list, true, intent, + resolvedType, filterCallingUid); } } } else { String pkgName = intent.getPackage(); if (pkgName == null) { - final List<ResolveInfo> result = componentResolver.queryReceivers( - computer, intent, resolvedType, flags, filterCallingUid, userId); + final List<ResolveInfo> result = componentResolver + .queryReceivers(computer, intent, resolvedType, flags, userId); if (result != null) { list = result; } @@ -481,7 +482,7 @@ final class ResolveIntentHelper { final AndroidPackage pkg = computer.getPackage(pkgName); if (pkg != null) { final List<ResolveInfo> result = componentResolver.queryReceivers(computer, - intent, resolvedType, flags, pkg.getReceivers(), filterCallingUid, userId); + intent, resolvedType, flags, pkg.getReceivers(), userId); if (result != null) { list = result; } @@ -491,7 +492,8 @@ final class ResolveIntentHelper { if (originalIntent != null) { // We also have to ensure all components match the original intent PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - computer, list, true, originalIntent, resolvedType, filterCallingUid); + mPlatformCompat, componentResolver, + list, true, originalIntent, resolvedType, filterCallingUid); } return computer.applyPostResolutionFilter(list, instantAppPkgName, false, queryingUid, @@ -575,7 +577,7 @@ final class ResolveIntentHelper { String pkgName = intent.getPackage(); if (pkgName == null) { final List<ResolveInfo> resolveInfos = componentResolver.queryProviders(computer, - intent, resolvedType, flags, callingUid, userId); + intent, resolvedType, flags, userId); if (resolveInfos == null) { return Collections.emptyList(); } @@ -585,7 +587,7 @@ final class ResolveIntentHelper { final AndroidPackage pkg = computer.getPackage(pkgName); if (pkg != null) { final List<ResolveInfo> resolveInfos = componentResolver.queryProviders(computer, - intent, resolvedType, flags, pkg.getProviders(), callingUid, userId); + intent, resolvedType, flags, pkg.getProviders(), userId); if (resolveInfos == null) { return Collections.emptyList(); } diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 6d5e2b00aa78..67639fbef124 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -430,6 +430,7 @@ class ShortcutPackage extends ShortcutPackageItem { @NonNull List<ShortcutInfo> changedShortcuts) { Preconditions.checkArgument(newShortcut.isEnabled(), "pushDynamicShortcuts() cannot publish disabled shortcuts"); + ensureShortcutCountBeforePush(); newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); @@ -437,7 +438,7 @@ class ShortcutPackage extends ShortcutPackageItem { final ShortcutInfo oldShortcut = findShortcutById(newShortcut.getId()); boolean deleted = false; - if (oldShortcut == null) { + if (oldShortcut == null || !oldShortcut.isDynamic()) { final ShortcutService service = mShortcutUser.mService; final int maxShortcuts = service.getMaxActivityShortcuts(); @@ -446,18 +447,12 @@ class ShortcutPackage extends ShortcutPackageItem { final ArrayList<ShortcutInfo> activityShortcuts = all.get(newShortcut.getActivity()); if (activityShortcuts != null && activityShortcuts.size() > maxShortcuts) { - Slog.e(TAG, "Error pushing shortcut. There are already " - + activityShortcuts.size() + " shortcuts, exceeding the " + maxShortcuts - + " shortcuts limit when pushing the new shortcut " + newShortcut - + ". Id of shortcuts currently available in system memory are " - + activityShortcuts.stream().map(ShortcutInfo::getId) - .collect(Collectors.joining(",", "[", "]"))); - // TODO: This should not have happened. If it does, identify the root cause where - // possible, otherwise bail-out early to prevent memory issue. + // Root cause was discovered in b/233155034, so this should not be happening. + service.wtf("Error pushing shortcut. There are already " + + activityShortcuts.size() + " shortcuts."); } if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) { // Max has reached. Delete the shortcut with lowest rank. - // Sort by isManifestShortcut() and getRank(). Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator); @@ -473,7 +468,8 @@ class ShortcutPackage extends ShortcutPackageItem { deleted = deleteDynamicWithId(shortcut.getId(), /* ignoreInvisible =*/ true, /*ignorePersistedShortcuts=*/ true) != null; } - } else { + } + if (oldShortcut != null) { // It's an update case. // Make sure the target is updatable. (i.e. should be mutable.) oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false); @@ -505,6 +501,32 @@ class ShortcutPackage extends ShortcutPackageItem { return deleted; } + private void ensureShortcutCountBeforePush() { + final ShortcutService service = mShortcutUser.mService; + // Ensure the total number of shortcuts doesn't exceed the hard limit per app. + final int maxShortcutPerApp = service.getMaxAppShortcuts(); + synchronized (mLock) { + final List<ShortcutInfo> appShortcuts = mShortcuts.values().stream().filter(si -> + !si.isPinned()).collect(Collectors.toList()); + if (appShortcuts.size() >= maxShortcutPerApp) { + // Max has reached. Removes shortcuts until they fall within the hard cap. + // Sort by isManifestShortcut(), isDynamic() and getLastChangedTimestamp(). + Collections.sort(appShortcuts, mShortcutTypeRankAndTimeComparator); + + while (appShortcuts.size() >= maxShortcutPerApp) { + final ShortcutInfo shortcut = appShortcuts.remove(appShortcuts.size() - 1); + if (shortcut.isDeclaredInManifest()) { + // All shortcuts are manifest shortcuts and cannot be removed. + throw new IllegalArgumentException(getPackageName() + " has published " + + appShortcuts.size() + " manifest shortcuts across different" + + " activities."); + } + forceDeleteShortcutInner(shortcut.getId()); + } + } + } + } + /** * Remove all shortcuts that aren't pinned, cached nor dynamic. * @@ -1371,6 +1393,61 @@ class ShortcutPackage extends ShortcutPackageItem { }; /** + * To sort by isManifestShortcut(), isDynamic(), getRank() and + * getLastChangedTimestamp(). i.e. manifest shortcuts come before non-manifest shortcuts, + * dynamic shortcuts come before floating shortcuts, then sort by last changed timestamp. + * + * This is used to decide which shortcuts to remove when the total number of shortcuts retained + * for the app exceeds the limit defined in {@link ShortcutService#getMaxAppShortcuts()}. + * + * (Note the number of manifest shortcuts is always <= the max number, because if there are + * more, ShortcutParser would ignore the rest.) + */ + final Comparator<ShortcutInfo> mShortcutTypeRankAndTimeComparator = (ShortcutInfo a, + ShortcutInfo b) -> { + if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) { + return -1; + } + if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) { + return 1; + } + if (a.isDynamic() && b.isDynamic()) { + return Integer.compare(a.getRank(), b.getRank()); + } + if (a.isDynamic()) { + return -1; + } + if (b.isDynamic()) { + return 1; + } + if (a.isCached() && b.isCached()) { + // if both shortcuts are cached, prioritize shortcuts cached by people tile, + if (a.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE) + && !b.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE)) { + return -1; + } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE) + && b.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE)) { + return 1; + } + // followed by bubbles. + if (a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) + && !b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { + return -1; + } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) + && b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { + return 1; + } + } + if (a.isCached()) { + return -1; + } + if (b.isCached()) { + return 1; + } + return Long.compare(b.getLastChangedTimestamp(), a.getLastChangedTimestamp()); + }; + + /** * Build a list of shortcuts for each target activity and return as a map. The result won't * contain "floating" shortcuts because they don't belong on any activities. */ diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index b7c3b97bf019..372b3bb8681b 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -180,6 +180,9 @@ public class ShortcutService extends IShortcutService.Stub { static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15; @VisibleForTesting + static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 100; + + @VisibleForTesting static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; @VisibleForTesting @@ -260,6 +263,11 @@ public class ShortcutService extends IShortcutService.Stub { String KEY_MAX_SHORTCUTS = "max_shortcuts"; /** + * Key name for the max shortcuts can be retained in system ram per app. (int) + */ + String KEY_MAX_SHORTCUTS_PER_APP = "max_shortcuts_per_app"; + + /** * Key name for icon compression quality, 0-100. */ String KEY_ICON_QUALITY = "icon_quality"; @@ -332,11 +340,16 @@ public class ShortcutService extends IShortcutService.Stub { new SparseArray<>(); /** - * Max number of dynamic + manifest shortcuts that each application can have at a time. + * Max number of dynamic + manifest shortcuts that each activity can have at a time. */ private int mMaxShortcuts; /** + * Max number of shortcuts that can exists in system ram for each application. + */ + private int mMaxShortcutsPerApp; + + /** * Max number of updating API calls that each application can make during the interval. */ int mMaxUpdatesPerInterval; @@ -810,6 +823,9 @@ public class ShortcutService extends IShortcutService.Stub { mMaxShortcuts = Math.max(0, (int) parser.getLong( ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY)); + mMaxShortcutsPerApp = Math.max(0, (int) parser.getLong( + ConfigConstants.KEY_MAX_SHORTCUTS_PER_APP, DEFAULT_MAX_SHORTCUTS_PER_APP)); + final int iconDimensionDp = Math.max(1, injectIsLowRamDevice() ? (int) parser.getLong( ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, @@ -1786,6 +1802,13 @@ public class ShortcutService extends IShortcutService.Stub { } /** + * Return the max number of shortcuts can be retaiend in system ram for each application. + */ + int getMaxAppShortcuts() { + return mMaxShortcutsPerApp; + } + + /** * - Sends a notification to LauncherApps * - Write to file */ @@ -4350,14 +4373,8 @@ public class ShortcutService extends IShortcutService.Stub { @NonNull ComponentName activity, @UserIdInt int userId) { final long start = getStatStartTime(); try { - final ActivityInfo ai; - try { - ai = mContext.getPackageManager().getActivityInfoAsUser(activity, - PackageManager.ComponentInfoFlags.of(PACKAGE_MATCH_FLAGS), userId); - } catch (NameNotFoundException e) { - return false; - } - return ai.enabled && ai.exported; + return queryActivities(new Intent(), activity.getPackageName(), activity, userId) + .size() > 0; } finally { logDurationStat(Stats.IS_ACTIVITY_ENABLED, start); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index dd014ee637c7..2b08a2c0a31b 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -114,6 +114,7 @@ import android.util.TimeUtils; import android.util.TypedValue; import android.util.Xml; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; @@ -7369,9 +7370,19 @@ public class UserManagerService extends IUserManager.Stub { * If the main user is a permanent admin user it can't be deleted * or downgraded to non-admin status. */ - private static boolean isMainUserPermanentAdmin() { + public boolean isMainUserPermanentAdmin() { return Resources.getSystem() - .getBoolean(com.android.internal.R.bool.config_isMainUserPermanentAdmin); + .getBoolean(R.bool.config_isMainUserPermanentAdmin); + } + + /** + * Returns true if {@link com.android.internal.R.bool#config_canSwitchToHeadlessSystemUser} + * is true. If allowed, headless system user can run in the foreground even though + * it is not a full user. + */ + public boolean canSwitchToHeadlessSystemUser() { + return Resources.getSystem() + .getBoolean(R.bool.config_canSwitchToHeadlessSystemUser); } } diff --git a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java index eed2a7896fcd..98b24ea34853 100644 --- a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java +++ b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java @@ -150,6 +150,10 @@ public class UserManagerServiceShellCommand extends ShellCommand { return runIsUserVisible(); case "get-main-user": return runGetMainUserId(); + case "can-switch-to-headless-system-user": + return canSwitchToHeadlessSystemUser(); + case "is-main-user-permanent-admin": + return isMainUserPermanentAdmin(); default: return handleDefaultCommands(cmd); } @@ -532,6 +536,20 @@ public class UserManagerServiceShellCommand extends ShellCommand { return 0; } + private int canSwitchToHeadlessSystemUser() { + PrintWriter pw = getOutPrintWriter(); + boolean canSwitchToHeadlessSystemUser = mService.canSwitchToHeadlessSystemUser(); + pw.println(canSwitchToHeadlessSystemUser); + return 0; + } + + private int isMainUserPermanentAdmin() { + PrintWriter pw = getOutPrintWriter(); + boolean isMainUserPermanentAdmin = mService.isMainUserPermanentAdmin(); + pw.println(isMainUserPermanentAdmin); + return 0; + } + /** * Gets the {@link UserManager} associated with the context of the given user. */ diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java index 977fab16e29b..fac681aaf1c4 100644 --- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java +++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java @@ -807,10 +807,10 @@ public class ComponentResolver extends ComponentResolverLocked implements } } - private abstract static class MimeGroupsAwareIntentResolver<F extends ParsedComponent> - extends IntentResolver<Pair<F, ParsedIntentInfo>, ResolveInfo> { - private final ArrayMap<String, Pair<F, ParsedIntentInfo>[]> mMimeGroupToFilter = - new ArrayMap<>(); + private abstract static class MimeGroupsAwareIntentResolver<F extends Pair<? + extends ParsedComponent, ParsedIntentInfo>, R> + extends IntentResolver<F, R> { + private final ArrayMap<String, F[]> mMimeGroupToFilter = new ArrayMap<>(); private boolean mIsUpdatingMimeGroup = false; @NonNull @@ -822,7 +822,7 @@ public class ComponentResolver extends ComponentResolverLocked implements } // Copy constructor used in creating snapshots - MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F> orig, + MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F, R> orig, @NonNull UserManagerService userManager) { mUserManager = userManager; copyFrom(orig); @@ -831,7 +831,7 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override - public void addFilter(@Nullable PackageDataSnapshot snapshot, Pair<F, ParsedIntentInfo> f) { + public void addFilter(@Nullable PackageDataSnapshot snapshot, F f) { IntentFilter intentFilter = getIntentFilter(f); // We assume Computer is available for this class and all subclasses. Because this class // uses subclass method override to handle logic, the Computer parameter must be in the @@ -846,7 +846,7 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override - protected void removeFilterInternal(Pair<F, ParsedIntentInfo> f) { + protected void removeFilterInternal(F f) { IntentFilter intentFilter = getIntentFilter(f); if (!mIsUpdatingMimeGroup) { unregister_intent_filter(f, intentFilter.mimeGroupsIterator(), mMimeGroupToFilter, @@ -857,86 +857,6 @@ public class ComponentResolver extends ComponentResolverLocked implements intentFilter.clearDynamicDataTypes(); } - @Override - public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, - String resolvedType, boolean defaultOnly, int callingUid, @UserIdInt int userId) { - if (!mUserManager.exists(userId)) return null; - long flags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0); - return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, callingUid, - userId, flags); - } - - List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent, - String resolvedType, long flags, int callingUid, int userId) { - if (!mUserManager.exists(userId)) return null; - return super.queryIntent(computer, intent, resolvedType, - (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, callingUid, userId, flags); - } - - List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent, - String resolvedType, long flags, List<F> packageComponents, - int callingUid, int userId) { - if (!mUserManager.exists(userId)) { - return null; - } - if (packageComponents == null) { - return Collections.emptyList(); - } - final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; - final int componentsSize = packageComponents.size(); - ArrayList<Pair<F, ParsedIntentInfo>[]> listCut = new ArrayList<>(componentsSize); - - List<ParsedIntentInfo> intentFilters; - for (int i = 0; i < componentsSize; ++i) { - F component = packageComponents.get(i); - intentFilters = component.getIntents(); - if (!intentFilters.isEmpty()) { - Pair<F, ParsedIntentInfo>[] array = newArray(intentFilters.size()); - for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) { - array[arrayIndex] = Pair.create(component, intentFilters.get(arrayIndex)); - } - listCut.add(array); - } - } - return super.queryIntentFromList(computer, intent, resolvedType, - defaultOnly, listCut, callingUid, userId, flags); - } - - @Override - protected boolean isPackageForFilter(String packageName, Pair<F, ParsedIntentInfo> info) { - return packageName.equals(info.first.getPackageName()); - } - - @Override - protected void sortResults(List<ResolveInfo> results) { - results.sort(RESOLVE_PRIORITY_SORTER); - } - - @Override - protected void filterResults(@NonNull Computer computer, @NonNull Intent intent, - List<ResolveInfo> results) { - if (intent.getAction() != null) return; - // When the resolved component is targeting U+, block null action intents - for (int i = results.size() - 1; i >= 0; --i) { - if (computer.isChangeEnabled(IntentFilter.BLOCK_NULL_ACTION_INTENTS, - results.get(i).getComponentInfo().applicationInfo)) { - results.remove(i); - } - } - } - - @Override - protected Pair<F, ParsedIntentInfo>[] newArray(int size) { - //noinspection unchecked - return (Pair<F, ParsedIntentInfo>[]) new Pair<?, ?>[size]; - } - - @Override - protected IntentFilter getIntentFilter( - @NonNull Pair<F, ParsedIntentInfo> input) { - return input.second.getIntentFilter(); - } - /** * Updates MIME group by applying changes to all IntentFilters * that contain the group and repopulating m*ToFilter maps accordingly @@ -947,12 +867,12 @@ public class ComponentResolver extends ComponentResolverLocked implements */ public boolean updateMimeGroup(@NonNull Computer computer, String packageName, String mimeGroup) { - Pair<F, ParsedIntentInfo>[] filters = mMimeGroupToFilter.get(mimeGroup); + F[] filters = mMimeGroupToFilter.get(mimeGroup); int n = filters != null ? filters.length : 0; mIsUpdatingMimeGroup = true; boolean hasChanges = false; - Pair<F, ParsedIntentInfo> filter; + F filter; for (int i = 0; i < n && (filter = filters[i]) != null; i++) { if (isPackageForFilter(packageName, filter)) { hasChanges |= updateFilter(computer, filter); @@ -962,7 +882,7 @@ public class ComponentResolver extends ComponentResolverLocked implements return hasChanges; } - private boolean updateFilter(@NonNull Computer computer, Pair<F, ParsedIntentInfo> f) { + private boolean updateFilter(@NonNull Computer computer, F f) { IntentFilter filter = getIntentFilter(f); List<String> oldTypes = filter.dataTypes(); removeFilter(f); @@ -987,7 +907,7 @@ public class ComponentResolver extends ComponentResolverLocked implements return first.equals(second); } - private void applyMimeGroups(@NonNull Computer computer, Pair<F, ParsedIntentInfo> f) { + private void applyMimeGroups(@NonNull Computer computer, F f) { IntentFilter filter = getIntentFilter(f); for (int i = filter.countMimeGroups() - 1; i >= 0; i--) { @@ -1011,8 +931,8 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override - protected boolean isFilterStopped(@NonNull Computer computer, - Pair<F, ParsedIntentInfo> filter, @UserIdInt int userId) { + protected boolean isFilterStopped(@NonNull Computer computer, F filter, + @UserIdInt int userId) { if (!mUserManager.exists(userId)) { return true; } @@ -1028,7 +948,7 @@ public class ComponentResolver extends ComponentResolverLocked implements } public static class ActivityIntentResolver - extends MimeGroupsAwareIntentResolver<ParsedActivity> { + extends MimeGroupsAwareIntentResolver<Pair<ParsedActivity, ParsedIntentInfo>, ResolveInfo> { @NonNull private UserNeedsBadgingCache mUserNeedsBadging; @@ -1049,6 +969,53 @@ public class ComponentResolver extends ComponentResolverLocked implements mUserNeedsBadging = userNeedsBadgingCache; } + @Override + public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, + String resolvedType, boolean defaultOnly, @UserIdInt int userId) { + if (!mUserManager.exists(userId)) return null; + long flags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0); + return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags); + } + + List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent, + String resolvedType, long flags, int userId) { + if (!mUserManager.exists(userId)) { + return null; + } + return super.queryIntent(computer, intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags); + } + + List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent, + String resolvedType, long flags, List<ParsedActivity> packageActivities, + int userId) { + if (!mUserManager.exists(userId)) { + return null; + } + if (packageActivities == null) { + return Collections.emptyList(); + } + final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; + final int activitiesSize = packageActivities.size(); + ArrayList<Pair<ParsedActivity, ParsedIntentInfo>[]> listCut = + new ArrayList<>(activitiesSize); + + List<ParsedIntentInfo> intentFilters; + for (int i = 0; i < activitiesSize; ++i) { + ParsedActivity activity = packageActivities.get(i); + intentFilters = activity.getIntents(); + if (!intentFilters.isEmpty()) { + Pair<ParsedActivity, ParsedIntentInfo>[] array = newArray(intentFilters.size()); + for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) { + array[arrayIndex] = Pair.create(activity, intentFilters.get(arrayIndex)); + } + listCut.add(array); + } + } + return super.queryIntentFromList(computer, intent, resolvedType, + defaultOnly, listCut, userId, flags); + } + protected void addActivity(@NonNull Computer computer, ParsedActivity a, String type, List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents) { mActivities.put(a.getComponentName(), a); @@ -1105,6 +1072,18 @@ public class ComponentResolver extends ComponentResolverLocked implements return true; } + @Override + protected Pair<ParsedActivity, ParsedIntentInfo>[] newArray(int size) { + //noinspection unchecked + return (Pair<ParsedActivity, ParsedIntentInfo>[]) new Pair<?, ?>[size]; + } + + @Override + protected boolean isPackageForFilter(String packageName, + Pair<ParsedActivity, ParsedIntentInfo> info) { + return packageName.equals(info.first.getPackageName()); + } + private void log(String reason, ParsedIntentInfo info, int match, int userId) { Slog.w(TAG, reason @@ -1220,6 +1199,11 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override + protected void sortResults(List<ResolveInfo> results) { + results.sort(RESOLVE_PRIORITY_SORTER); + } + + @Override protected void dumpFilter(PrintWriter out, String prefix, Pair<ParsedActivity, ParsedIntentInfo> pair) { ParsedActivity activity = pair.first; @@ -1253,6 +1237,12 @@ public class ComponentResolver extends ComponentResolverLocked implements out.println(); } + @Override + protected IntentFilter getIntentFilter( + @NonNull Pair<ParsedActivity, ParsedIntentInfo> input) { + return input.second.getIntentFilter(); + } + protected List<ParsedActivity> getResolveList(AndroidPackage pkg) { return pkg.getActivities(); } @@ -1288,7 +1278,7 @@ public class ComponentResolver extends ComponentResolverLocked implements } public static final class ProviderIntentResolver - extends MimeGroupsAwareIntentResolver<ParsedProvider> { + extends MimeGroupsAwareIntentResolver<Pair<ParsedProvider, ParsedIntentInfo>, ResolveInfo> { // Default constructor ProviderIntentResolver(@NonNull UserManagerService userManager) { super(userManager); @@ -1301,6 +1291,57 @@ public class ComponentResolver extends ComponentResolverLocked implements mProviders.putAll(orig.mProviders); } + @Override + public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, + String resolvedType, boolean defaultOnly, @UserIdInt int userId) { + if (!mUserManager.exists(userId)) { + return null; + } + long flags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; + return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags); + } + + @Nullable + List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent, + String resolvedType, long flags, int userId) { + if (!mUserManager.exists(userId)) { + return null; + } + return super.queryIntent(computer, intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags); + } + + @Nullable + List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent, + String resolvedType, long flags, List<ParsedProvider> packageProviders, + int userId) { + if (!mUserManager.exists(userId)) { + return null; + } + if (packageProviders == null) { + return Collections.emptyList(); + } + final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; + final int providersSize = packageProviders.size(); + ArrayList<Pair<ParsedProvider, ParsedIntentInfo>[]> listCut = + new ArrayList<>(providersSize); + + List<ParsedIntentInfo> intentFilters; + for (int i = 0; i < providersSize; ++i) { + ParsedProvider provider = packageProviders.get(i); + intentFilters = provider.getIntents(); + if (!intentFilters.isEmpty()) { + Pair<ParsedProvider, ParsedIntentInfo>[] array = newArray(intentFilters.size()); + for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) { + array[arrayIndex] = Pair.create(provider, intentFilters.get(arrayIndex)); + } + listCut.add(array); + } + } + return super.queryIntentFromList(computer, intent, resolvedType, + defaultOnly, listCut, userId, flags); + } + void addProvider(@NonNull Computer computer, ParsedProvider p) { if (mProviders.containsKey(p.getComponentName())) { Slog.w(TAG, "Provider " + p.getComponentName() + " already defined; ignoring"); @@ -1361,6 +1402,18 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override + protected Pair<ParsedProvider, ParsedIntentInfo>[] newArray(int size) { + //noinspection unchecked + return (Pair<ParsedProvider, ParsedIntentInfo>[]) new Pair<?, ?>[size]; + } + + @Override + protected boolean isPackageForFilter(String packageName, + Pair<ParsedProvider, ParsedIntentInfo> info) { + return packageName.equals(info.first.getPackageName()); + } + + @Override protected ResolveInfo newResult(@NonNull Computer computer, Pair<ParsedProvider, ParsedIntentInfo> pair, int match, int userId, long customFlags) { @@ -1426,6 +1479,11 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override + protected void sortResults(List<ResolveInfo> results) { + results.sort(RESOLVE_PRIORITY_SORTER); + } + + @Override protected void dumpFilter(PrintWriter out, String prefix, Pair<ParsedProvider, ParsedIntentInfo> pair) { ParsedProvider provider = pair.first; @@ -1460,11 +1518,17 @@ public class ComponentResolver extends ComponentResolverLocked implements out.println(); } + @Override + protected IntentFilter getIntentFilter( + @NonNull Pair<ParsedProvider, ParsedIntentInfo> input) { + return input.second.getIntentFilter(); + } + final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>(); } public static final class ServiceIntentResolver - extends MimeGroupsAwareIntentResolver<ParsedService> { + extends MimeGroupsAwareIntentResolver<Pair<ParsedService, ParsedIntentInfo>, ResolveInfo> { // Default constructor ServiceIntentResolver(@NonNull UserManagerService userManager) { super(userManager); @@ -1477,6 +1541,50 @@ public class ComponentResolver extends ComponentResolverLocked implements mServices.putAll(orig.mServices); } + @Override + public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, + String resolvedType, boolean defaultOnly, @UserIdInt int userId) { + if (!mUserManager.exists(userId)) { + return null; + } + long flags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; + return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags); + } + + List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent, + String resolvedType, long flags, int userId) { + if (!mUserManager.exists(userId)) return null; + return super.queryIntent(computer, intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags); + } + + List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent, + String resolvedType, long flags, List<ParsedService> packageServices, int userId) { + if (!mUserManager.exists(userId)) return null; + if (packageServices == null) { + return Collections.emptyList(); + } + final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; + final int servicesSize = packageServices.size(); + ArrayList<Pair<ParsedService, ParsedIntentInfo>[]> listCut = + new ArrayList<>(servicesSize); + + List<ParsedIntentInfo> intentFilters; + for (int i = 0; i < servicesSize; ++i) { + ParsedService service = packageServices.get(i); + intentFilters = service.getIntents(); + if (intentFilters.size() > 0) { + Pair<ParsedService, ParsedIntentInfo>[] array = newArray(intentFilters.size()); + for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) { + array[arrayIndex] = Pair.create(service, intentFilters.get(arrayIndex)); + } + listCut.add(array); + } + } + return super.queryIntentFromList(computer, intent, resolvedType, + defaultOnly, listCut, userId, flags); + } + void addService(@NonNull Computer computer, ParsedService s) { mServices.put(s.getComponentName(), s); if (DEBUG_SHOW_INFO) { @@ -1532,6 +1640,18 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override + protected Pair<ParsedService, ParsedIntentInfo>[] newArray(int size) { + //noinspection unchecked + return (Pair<ParsedService, ParsedIntentInfo>[]) new Pair<?, ?>[size]; + } + + @Override + protected boolean isPackageForFilter(String packageName, + Pair<ParsedService, ParsedIntentInfo> info) { + return packageName.equals(info.first.getPackageName()); + } + + @Override protected ResolveInfo newResult(@NonNull Computer computer, Pair<ParsedService, ParsedIntentInfo> pair, int match, int userId, long customFlags) { @@ -1590,6 +1710,11 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override + protected void sortResults(List<ResolveInfo> results) { + results.sort(RESOLVE_PRIORITY_SORTER); + } + + @Override protected void dumpFilter(PrintWriter out, String prefix, Pair<ParsedService, ParsedIntentInfo> pair) { ParsedService service = pair.first; @@ -1627,6 +1752,12 @@ public class ComponentResolver extends ComponentResolverLocked implements out.println(); } + @Override + protected IntentFilter getIntentFilter( + @NonNull Pair<ParsedService, ParsedIntentInfo> input) { + return input.second.getIntentFilter(); + } + // Keys are String (activity class name), values are Activity. final ArrayMap<ComponentName, ParsedService> mServices = new ArrayMap<>(); } @@ -1690,8 +1821,7 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override - protected void filterResults(@NonNull Computer computer, - @NonNull Intent intent, List<AuxiliaryResolveInfo.AuxiliaryFilter> results) { + protected void filterResults(List<AuxiliaryResolveInfo.AuxiliaryFilter> results) { // only do work if ordering is enabled [most of the time it won't be] if (mOrderResult.size() == 0) { return; diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java index 7f886600121c..b8e4c8d2a51f 100644 --- a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java +++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java @@ -55,12 +55,12 @@ public interface ComponentResolverApi { @Nullable List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId); + @Nullable String resolvedType, long flags, @UserIdInt int userId); @Nullable List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities, - int callingUid, @UserIdInt int userId); + @UserIdInt int userId); @Nullable ProviderInfo queryProvider(@NonNull Computer computer, @NonNull String authority, long flags, @@ -68,12 +68,12 @@ public interface ComponentResolverApi { @Nullable List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId); + @Nullable String resolvedType, long flags, @UserIdInt int userId); @Nullable List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers, - int callingUid, @UserIdInt int userId); + @UserIdInt int userId); @Nullable List<ProviderInfo> queryProviders(@NonNull Computer computer, @Nullable String processName, @@ -81,21 +81,21 @@ public interface ComponentResolverApi { @Nullable List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId); + @Nullable String resolvedType, long flags, @UserIdInt int userId); @Nullable List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers, - int callingUid, @UserIdInt int userId); + @UserIdInt int userId); @Nullable List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId); + @Nullable String resolvedType, long flags, @UserIdInt int userId); @Nullable List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services, - int callingUid, @UserIdInt int userId); + @UserIdInt int userId); void querySyncProviders(@NonNull Computer computer, @NonNull List<String> outNames, @NonNull List<ProviderInfo> outInfo, boolean safeMode, @UserIdInt int userId); diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java index 689992444601..9115775acd05 100644 --- a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java +++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java @@ -126,17 +126,17 @@ public abstract class ComponentResolverBase extends WatchableImpl implements Com @Nullable @Override public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, int userId) { - return mActivities.queryIntent(computer, intent, resolvedType, flags, callingUid, userId); + @Nullable String resolvedType, long flags, int userId) { + return mActivities.queryIntent(computer, intent, resolvedType, flags, userId); } @Nullable @Override public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities, - int callingUid, int userId) { + int userId) { return mActivities.queryIntentForPackage(computer, intent, resolvedType, flags, activities, - callingUid, userId); + userId); } @Nullable @@ -168,17 +168,17 @@ public abstract class ComponentResolverBase extends WatchableImpl implements Com @Nullable @Override public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, int userId) { - return mProviders.queryIntent(computer, intent, resolvedType, flags, callingUid, userId); + @Nullable String resolvedType, long flags, int userId) { + return mProviders.queryIntent(computer, intent, resolvedType, flags, userId); } @Nullable @Override public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { return mProviders.queryIntentForPackage(computer, intent, resolvedType, flags, providers, - callingUid, userId); + userId); } @Nullable @@ -241,33 +241,33 @@ public abstract class ComponentResolverBase extends WatchableImpl implements Com @Nullable @Override public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, int userId) { - return mReceivers.queryIntent(computer, intent, resolvedType, flags, callingUid, userId); + @Nullable String resolvedType, long flags, int userId) { + return mReceivers.queryIntent(computer, intent, resolvedType, flags, userId); } @Nullable @Override public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { return mReceivers.queryIntentForPackage(computer, intent, resolvedType, flags, receivers, - callingUid, userId); + userId); } @Nullable @Override public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) { - return mServices.queryIntent(computer, intent, resolvedType, flags, callingUid, userId); + @Nullable String resolvedType, long flags, @UserIdInt int userId) { + return mServices.queryIntent(computer, intent, resolvedType, flags, userId); } @Nullable @Override public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { return mServices.queryIntentForPackage(computer, intent, resolvedType, flags, services, - callingUid, userId); + userId); } @Override diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java index 5bfb135c7d75..0c84f4c53dfe 100644 --- a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java +++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java @@ -92,9 +92,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Nullable @Override public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) { + @Nullable String resolvedType, long flags, @UserIdInt int userId) { synchronized (mLock) { - return super.queryActivities(computer, intent, resolvedType, flags, callingUid, userId); + return super.queryActivities(computer, intent, resolvedType, flags, userId); } } @@ -102,10 +102,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Override public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { synchronized (mLock) { - return super.queryActivities(computer, intent, resolvedType, flags, activities, - callingUid, userId); + return super.queryActivities(computer, intent, resolvedType, flags, activities, userId); } } @@ -121,9 +120,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Nullable @Override public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) { + @Nullable String resolvedType, long flags, @UserIdInt int userId) { synchronized (mLock) { - return super.queryProviders(computer, intent, resolvedType, flags, callingUid, userId); + return super.queryProviders(computer, intent, resolvedType, flags, userId); } } @@ -131,10 +130,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Override public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { synchronized (mLock) { - return super.queryProviders(computer, intent, resolvedType, flags, providers, - callingUid, userId); + return super.queryProviders(computer, intent, resolvedType, flags, providers, userId); } } @@ -151,9 +149,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Nullable @Override public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) { + @Nullable String resolvedType, long flags, @UserIdInt int userId) { synchronized (mLock) { - return super.queryReceivers(computer, intent, resolvedType, flags, callingUid, userId); + return super.queryReceivers(computer, intent, resolvedType, flags, userId); } } @@ -161,19 +159,18 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Override public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { synchronized (mLock) { - return super.queryReceivers(computer, intent, resolvedType, flags, receivers, - callingUid, userId); + return super.queryReceivers(computer, intent, resolvedType, flags, receivers, userId); } } @Nullable @Override public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) { + @Nullable String resolvedType, long flags, @UserIdInt int userId) { synchronized (mLock) { - return super.queryServices(computer, intent, resolvedType, flags, callingUid, userId); + return super.queryServices(computer, intent, resolvedType, flags, userId); } } @@ -181,10 +178,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Override public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { synchronized (mLock) { - return super.queryServices(computer, intent, resolvedType, flags, services, callingUid, - userId); + return super.queryServices(computer, intent, resolvedType, flags, services, userId); } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 37877cf8264e..45dc49dba2ca 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -332,6 +332,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int TRIPLE_PRESS_PRIMARY_NOTHING = 0; static final int TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY = 1; + // Must match: config_searchKeyBehavior in config.xml + static final int SEARCH_BEHAVIOR_DEFAULT_SEARCH = 0; + static final int SEARCH_BEHAVIOR_TARGET_ACTIVITY = 1; + static public final String SYSTEM_DIALOG_REASON_KEY = "reason"; static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions"; static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; @@ -540,6 +544,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mWakeOnAssistKeyPress; boolean mWakeOnBackKeyPress; long mWakeUpToLastStateTimeout; + int mSearchKeyBehavior; + ComponentName mSearchKeyTargetActivity; private boolean mHandleVolumeKeysInWM; @@ -2139,6 +2145,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWakeUpToLastStateTimeout = mContext.getResources().getInteger( com.android.internal.R.integer.config_wakeUpToLastStateTimeoutMillis); + mSearchKeyBehavior = mContext.getResources().getInteger( + com.android.internal.R.integer.config_searchKeyBehavior); + + mSearchKeyTargetActivity = ComponentName.unflattenFromString( + mContext.getResources().getString( + com.android.internal.R.string.config_searchKeyTargetActivity)); readConfigurationDependentBehaviors(); mDisplayFoldController = DisplayFoldController.create(mContext, DEFAULT_DISPLAY); @@ -3161,7 +3173,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { toggleNotificationPanel(); } return key_consumed; - + case KeyEvent.KEYCODE_SEARCH: + if (down && repeatCount == 0 && !keyguardOn()) { + switch(mSearchKeyBehavior) { + case SEARCH_BEHAVIOR_TARGET_ACTIVITY: { + launchTargetSearchActivity(); + return key_consumed; + } + case SEARCH_BEHAVIOR_DEFAULT_SEARCH: + default: + break; + } + } + break; case KeyEvent.KEYCODE_SPACE: // Handle keyboard layout switching. (META + SPACE) if ((metaState & KeyEvent.META_META_MASK) == 0) { @@ -6344,4 +6368,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private void launchTargetSearchActivity() { + Intent intent; + if (mSearchKeyTargetActivity != null) { + intent = new Intent(); + intent.setComponent(mSearchKeyTargetActivity); + } else { + intent = new Intent(Intent.ACTION_WEB_SEARCH); + } + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + try { + startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); + } catch (ActivityNotFoundException ignore) { + Slog.e(TAG, "Could not resolve activity with : " + + intent.getComponent().flattenToString() + + " name."); + } + } } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index b83d509b47b1..bf8cbeac30c8 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -40,7 +40,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.ActivityManager; -import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.SynchronousUserSwitchObserver; import android.content.BroadcastReceiver; @@ -317,7 +316,6 @@ public final class PowerManagerService extends SystemService private SettingsObserver mSettingsObserver; private DreamManagerInternal mDreamManager; private LogicalLight mAttentionLight; - private ActivityManagerInternal mAmInternal; private final InattentiveSleepWarningController mInattentiveSleepWarningOverlayController; private final AmbientDisplaySuppressionController mAmbientDisplaySuppressionController; @@ -1244,7 +1242,6 @@ public final class PowerManagerService extends SystemService mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class); mPolicy = getLocalService(WindowManagerPolicy.class); mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class); - mAmInternal = getLocalService(ActivityManagerInternal.class); mAttentionDetector.systemReady(mContext); SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper()); @@ -4088,8 +4085,9 @@ public final class PowerManagerService extends SystemService final UidState state = wakeLock.mUidState; if (Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 && Arrays.binarySearch(mDeviceIdleTempWhitelist, appid) < 0 && - (mAmInternal != null && !mAmInternal.canHoldWakeLocksInDeepDoze( - state.mUid, state.mProcState))) { + state.mProcState != ActivityManager.PROCESS_STATE_NONEXISTENT && + state.mProcState > + ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { disabled = true; } } diff --git a/services/core/java/com/android/server/sensors/SensorManagerInternal.java b/services/core/java/com/android/server/sensors/SensorManagerInternal.java index 41c2fbfd3314..6c32ec2e8df8 100644 --- a/services/core/java/com/android/server/sensors/SensorManagerInternal.java +++ b/services/core/java/com/android/server/sensors/SensorManagerInternal.java @@ -17,6 +17,8 @@ package com.android.server.sensors; import android.annotation.NonNull; +import android.hardware.SensorDirectChannel; +import android.os.ParcelFileDescriptor; import java.util.concurrent.Executor; @@ -58,7 +60,7 @@ public abstract class SensorManagerInternal { * @return The sensor handle. */ public abstract int createRuntimeSensor(int deviceId, int type, @NonNull String name, - @NonNull String vendor, @NonNull RuntimeSensorCallback callback); + @NonNull String vendor, int flags, @NonNull RuntimeSensorCallback callback); /** * Unregisters the sensor with the given handle from the framework. @@ -98,9 +100,31 @@ public abstract class SensorManagerInternal { public interface RuntimeSensorCallback { /** * Invoked when the listeners of the runtime sensor have changed. - * Returns an error code if the invocation was unsuccessful, zero otherwise. + * Returns zero on success, negative error code otherwise. */ int onConfigurationChanged(int handle, boolean enabled, int samplingPeriodMicros, int batchReportLatencyMicros); + + /** + * Invoked when a direct sensor channel has been created. + * Wraps the file descriptor in a {@link android.os.SharedMemory} object and passes it to + * the client process. + * Returns a positive identifier of the channel on success, negative error code otherwise. + */ + int onDirectChannelCreated(ParcelFileDescriptor fd); + + /** + * Invoked when a direct sensor channel has been destroyed. + */ + void onDirectChannelDestroyed(int channelHandle); + + /** + * Invoked when a direct sensor channel has been configured for a sensor. + * If the invocation is unsuccessful, a negative error code is returned. + * On success, the return value is zero if the rate level is {@code RATE_STOP}, and a + * positive report token otherwise. + */ + int onDirectChannelConfigured(int channelHandle, int sensorHandle, + @SensorDirectChannel.RateLevel int rateLevel); } } diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java index 979065950dc4..1baa0a6d79a1 100644 --- a/services/core/java/com/android/server/sensors/SensorService.java +++ b/services/core/java/com/android/server/sensors/SensorService.java @@ -56,7 +56,8 @@ public class SensorService extends SystemService { private static native void unregisterProximityActiveListenerNative(long ptr); private static native int registerRuntimeSensorNative(long ptr, int deviceId, int type, - String name, String vendor, SensorManagerInternal.RuntimeSensorCallback callback); + String name, String vendor, int flags, + SensorManagerInternal.RuntimeSensorCallback callback); private static native void unregisterRuntimeSensorNative(long ptr, int handle); private static native boolean sendRuntimeSensorEventNative(long ptr, int handle, int type, long timestampNanos, float[] values); @@ -95,9 +96,9 @@ public class SensorService extends SystemService { class LocalService extends SensorManagerInternal { @Override public int createRuntimeSensor(int deviceId, int type, @NonNull String name, - @NonNull String vendor, @NonNull RuntimeSensorCallback callback) { + @NonNull String vendor, int flags, @NonNull RuntimeSensorCallback callback) { synchronized (mLock) { - int handle = registerRuntimeSensorNative(mPtr, deviceId, type, name, vendor, + int handle = registerRuntimeSensorNative(mPtr, deviceId, type, name, vendor, flags, callback); mRuntimeSensorHandles.add(handle); return handle; diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java index 8a4fc0db0264..1f7af41df6e0 100644 --- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java +++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java @@ -43,7 +43,7 @@ class ActivitySecurityModelFeatureFlags { static final String DOC_LINK = "go/android-asm"; /** Used to determine which version of the ASM logic was used in logs while we iterate */ - static final int ASM_VERSION = 5; + static final int ASM_VERSION = 6; private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER; private static final String KEY_ASM_PREFIX = "ActivitySecurity__"; diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index e99046041056..ac07248d15f2 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1948,9 +1948,13 @@ class ActivityStarter { boolean passesAsmChecks = true; Task sourceTask = mSourceRecord.getTask(); - // Don't allow launches into a new task if the current task is not foreground. + // Allow launching into a new task (or a task matching the launched activity's + // affinity) only if the current task is foreground or mutating its own task. + // The latter can happen eg. if caller uses NEW_TASK flag and the activity being + // launched matches affinity of source task. if (taskToFront) { - passesAsmChecks = sourceTask != null && sourceTask.isVisible(); + passesAsmChecks = sourceTask != null + && (sourceTask.isVisible() || sourceTask == targetTask); } if (passesAsmChecks) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 18be2a355cab..7d478b3c7ca5 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -45,6 +45,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED; import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS; import static android.content.pm.PackageManager.FEATURE_CANT_SAVE_STATE; +import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP; import static android.content.pm.PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE; import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; import static android.content.pm.PackageManager.FEATURE_LEANBACK; @@ -401,6 +402,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { volatile WindowProcessController mHeavyWeightProcess; boolean mHasHeavyWeightFeature; boolean mHasLeanbackFeature; + boolean mHasCompanionDeviceSetupFeature; /** The process of the top most activity. */ volatile WindowProcessController mTopApp; /** @@ -859,6 +861,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final PackageManager pm = mContext.getPackageManager(); mHasHeavyWeightFeature = pm.hasSystemFeature(FEATURE_CANT_SAVE_STATE); mHasLeanbackFeature = pm.hasSystemFeature(FEATURE_LEANBACK); + mHasCompanionDeviceSetupFeature = pm.hasSystemFeature(FEATURE_COMPANION_DEVICE_SETUP); mVrController.onSystemReady(); mRecentTasks.onSystemReadyLocked(); mTaskSupervisor.onSystemReady(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index e463358a3cf5..df471c56fec9 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -1239,8 +1239,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { return Context.DEVICE_ID_DEFAULT; } if (mVirtualDeviceManager == null) { - mVirtualDeviceManager = - mService.mContext.getSystemService(VirtualDeviceManager.class); + if (mService.mHasCompanionDeviceSetupFeature) { + mVirtualDeviceManager = + mService.mContext.getSystemService(VirtualDeviceManager.class); + } + if (mVirtualDeviceManager == null) { + return Context.DEVICE_ID_DEFAULT; + } } return mVirtualDeviceManager.getDeviceIdForDisplayId(displayId); } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 25ce5699ab71..210a7d9538c7 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -344,6 +344,19 @@ class InsetsPolicy { } } + if (!attrs.isFullscreen() || attrs.getFitInsetsTypes() != 0) { + if (state == originalState) { + state = new InsetsState(originalState); + } + // Explicitly exclude floating windows from receiving caption insets. This is because we + // hard code caption insets for windows due to a synchronization issue that leads to + // flickering that bypasses insets frame calculation, which consequently needs us to + // remove caption insets from floating windows. + // TODO(b/254128050): Remove this workaround after we find a way to update window frames + // and caption insets frames simultaneously. + state.removeSource(InsetsState.ITYPE_CAPTION_BAR); + } + final SparseArray<WindowContainerInsetsSourceProvider> providers = mStateController.getSourceProviders(); final int windowType = attrs.type; diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 97e0b1e0de2c..ce9bff8521e6 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -948,7 +948,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { synchronized (mService.mGlobalLock) { WindowState windowState = mService.windowForClientLocked(this, window, false); if (windowState == null) { - Slog.e(TAG_WM, + Slog.i(TAG_WM, "setOnBackInvokedCallback(): No window state for package:" + mPackageName); } else { windowState.setOnBackInvokedCallbackInfo(callbackInfo); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index cc81d526af5e..0a833f4f2dba 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3512,10 +3512,7 @@ class Task extends TaskFragment { info.isKeyguardOccluded = mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY); - info.startingWindowTypeParameter = activity.mStartingData != null - ? activity.mStartingData.mTypeParams - : (StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED - | StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS); + info.startingWindowTypeParameter = activity.mStartingData.mTypeParams; if ((info.startingWindowTypeParameter & StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED) != 0) { final WindowState topMainWin = getWindow(w -> w.mAttrs.type == TYPE_BASE_APPLICATION); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index b13136534de3..3a30e4b0cf1f 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.TaskInfo.cameraCompatControlStateToString; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; @@ -44,7 +43,6 @@ import android.view.Display; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; -import android.window.IWindowlessStartingSurfaceCallback; import android.window.SplashScreenView; import android.window.StartingWindowInfo; import android.window.StartingWindowRemovalInfo; @@ -658,10 +656,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { info.splashScreenThemeResId = launchTheme; } info.taskSnapshot = taskSnapshot; - info.appToken = activity.token; // make this happen prior than prepare surface try { - lastOrganizer.addStartingWindow(info); + lastOrganizer.addStartingWindow(info, activity.token); } catch (RemoteException e) { Slog.e(TAG, "Exception sending onTaskStart callback", e); return false; @@ -707,55 +704,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } - /** - * Create a starting surface which attach on a given surface. - * @param activity Target activity, this isn't necessary to be the top activity. - * @param root The root surface which the created surface will attach on. - * @param taskSnapshot Whether to draw snapshot. - * @param callback Called when surface is drawn and attached to the root surface. - * @return The taskId, this is a token and should be used to remove the surface, even if - * the task was removed from hierarchy. - */ - int addWindowlessStartingSurface(Task task, ActivityRecord activity, SurfaceControl root, - TaskSnapshot taskSnapshot, IWindowlessStartingSurfaceCallback callback) { - final Task rootTask = task.getRootTask(); - if (rootTask == null) { - return INVALID_TASK_ID; - } - final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast(); - if (lastOrganizer == null) { - return INVALID_TASK_ID; - } - final StartingWindowInfo info = task.getStartingWindowInfo(activity); - info.taskInfo.taskDescription = activity.taskDescription; - info.taskSnapshot = taskSnapshot; - info.windowlessStartingSurfaceCallback = callback; - info.rootSurface = root; - try { - lastOrganizer.addStartingWindow(info); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending addWindowlessStartingSurface ", e); - return INVALID_TASK_ID; - } - return task.mTaskId; - } - - void removeWindowlessStartingSurface(int taskId, boolean immediately) { - final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast(); - if (lastOrganizer == null || taskId == 0) { - return; - } - final StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo(); - removalInfo.taskId = taskId; - removalInfo.windowlessSurface = true; - removalInfo.removeImmediately = immediately; - try { - lastOrganizer.removeStartingWindow(removalInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending removeWindowlessStartingSurface ", e); - } - } - boolean copySplashScreenView(Task task) { final Task rootTask = task.getRootTask(); if (rootTask == null) { diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 7c0318d2bee4..aa10291a0364 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -210,7 +210,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio /** Whether {@link #mLastReportedConfiguration} is deferred by the cached state. */ private volatile boolean mHasCachedConfiguration; - private int mTopActivityDeviceId = Context.DEVICE_ID_DEFAULT; + private int mLastTopActivityDeviceId = Context.DEVICE_ID_DEFAULT; /** * Registered {@link DisplayArea} as a listener to override config changes. {@code null} if not * registered. @@ -1411,8 +1411,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // If deviceId for the top-activity changed, schedule passing it to the app process. boolean topActivityDeviceChanged = false; int deviceId = getTopActivityDeviceId(); - if (deviceId != mTopActivityDeviceId) { + if (deviceId != mLastTopActivityDeviceId) { topActivityDeviceChanged = true; + mLastTopActivityDeviceId = deviceId; } final Configuration config = getConfiguration(); @@ -1431,15 +1432,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return; } - // TODO(b/263402938): Add tests that capture the deviceId dispatch to the client. - mTopActivityDeviceId = deviceId; - dispatchConfiguration(config, topActivityDeviceChanged ? mTopActivityDeviceId - : Context.DEVICE_ID_INVALID); + dispatchConfiguration(config); } private int getTopActivityDeviceId() { ActivityRecord topActivity = getTopNonFinishingActivity(); - int updatedDeviceId = mTopActivityDeviceId; + int updatedDeviceId = Context.DEVICE_ID_DEFAULT; if (topActivity != null && topActivity.mDisplayContent != null) { updatedDeviceId = mAtm.mTaskSupervisor.getDeviceIdForDisplayId( topActivity.mDisplayContent.mDisplayId); @@ -1484,10 +1482,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } void dispatchConfiguration(Configuration config) { - dispatchConfiguration(config, getTopActivityDeviceId()); - } - - void dispatchConfiguration(Configuration config, int deviceId) { mHasPendingConfigurationChange = false; if (mThread == null) { if (Build.IS_DEBUGGABLE && mHasImeService) { @@ -1514,16 +1508,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } } - scheduleConfigurationChange(mThread, config, deviceId); + scheduleConfigurationChange(mThread, config); } private void scheduleConfigurationChange(IApplicationThread thread, Configuration config) { - // By default send invalid deviceId as no-op signal so it's not updated on the client side. - scheduleConfigurationChange(thread, config, Context.DEVICE_ID_INVALID); - } - - private void scheduleConfigurationChange(IApplicationThread thread, Configuration config, - int deviceId) { ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName, config); if (Build.IS_DEBUGGABLE && mHasImeService) { @@ -1533,7 +1521,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mHasCachedConfiguration = false; try { mAtm.getLifecycleManager().scheduleTransaction(thread, - ConfigurationChangeItem.obtain(config, deviceId)); + ConfigurationChangeItem.obtain(config, mLastTopActivityDeviceId)); } catch (Exception e) { Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change: " + mOwner, e); } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index e2bdcdd518d5..2ce86add4e58 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -179,6 +179,7 @@ cc_defaults { "android.hardware.power.stats@1.0", "android.hardware.power.stats-V1-ndk", "android.hardware.thermal@1.0", + "android.hardware.thermal-V1-ndk", "android.hardware.tv.input@1.0", "android.hardware.tv.input-V1-ndk", "android.hardware.vibrator-V2-cpp", diff --git a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp index ed79352bba21..7e0bb68c6168 100644 --- a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp +++ b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp @@ -16,27 +16,28 @@ #define LOG_TAG "HardwarePropertiesManagerService-JNI" -#include <nativehelper/JNIHelp.h> -#include "jni.h" - -#include <math.h> -#include <stdlib.h> - +#include <aidl/android/hardware/thermal/IThermal.h> +#include <android/binder_manager.h> #include <android/hardware/thermal/1.0/IThermal.h> +#include <math.h> +#include <nativehelper/JNIHelp.h> #include <utils/Log.h> #include <utils/String8.h> #include "core_jni_helpers.h" +#include "jni.h" namespace android { +using ::aidl::android::hardware::thermal::CoolingDevice; +using ::aidl::android::hardware::thermal::IThermal; +using ::aidl::android::hardware::thermal::Temperature; +using ::aidl::android::hardware::thermal::TemperatureThreshold; +using ::aidl::android::hardware::thermal::TemperatureType; +using ::aidl::android::hardware::thermal::ThrottlingSeverity; using android::hidl::base::V1_0::IBase; using hardware::hidl_death_recipient; using hardware::hidl_vec; -using hardware::thermal::V1_0::CoolingDevice; -using hardware::thermal::V1_0::CpuUsage; -using hardware::thermal::V1_0::IThermal; -using hardware::thermal::V1_0::Temperature; using hardware::thermal::V1_0::ThermalStatus; using hardware::thermal::V1_0::ThermalStatusCode; template<typename T> @@ -62,20 +63,28 @@ jfloat gUndefinedTemperature; static void getThermalHalLocked(); static std::mutex gThermalHalMutex; -static sp<IThermal> gThermalHal = nullptr; - -// struct ThermalHalDeathRecipient; -struct ThermalHalDeathRecipient : virtual public hidl_death_recipient { - // hidl_death_recipient interface - virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override { - std::lock_guard<std::mutex> lock(gThermalHalMutex); - ALOGE("ThermalHAL just died"); - gThermalHal = nullptr; - getThermalHalLocked(); - } +static sp<hardware::thermal::V1_0::IThermal> gThermalHidlHal = nullptr; +static std::shared_ptr<IThermal> gThermalAidlHal = nullptr; + +struct ThermalHidlHalDeathRecipient : virtual public hidl_death_recipient { + // hidl_death_recipient interface + virtual void serviceDied(uint64_t cookie, const wp<IBase> &who) override { + std::lock_guard<std::mutex> lock(gThermalHalMutex); + ALOGE("Thermal HAL just died"); + gThermalHidlHal = nullptr; + getThermalHalLocked(); + } }; -sp<ThermalHalDeathRecipient> gThermalHalDeathRecipient = nullptr; +static void onThermalAidlBinderDied(void *cookie) { + std::lock_guard<std::mutex> lock(gThermalHalMutex); + ALOGE("Thermal AIDL HAL just died"); + gThermalAidlHal = nullptr; + getThermalHalLocked(); +} + +sp<ThermalHidlHalDeathRecipient> gThermalHidlHalDeathRecipient = nullptr; +ndk::ScopedAIBinder_DeathRecipient gThermalAidlDeathRecipient; // ---------------------------------------------------------------------------- @@ -85,27 +94,49 @@ float finalizeTemperature(float temperature) { // The caller must be holding gThermalHalMutex. static void getThermalHalLocked() { - if (gThermalHal != nullptr) { + if (gThermalAidlHal || gThermalHidlHal) { + return; + } + const std::string thermalInstanceName = std::string(IThermal::descriptor) + "/default"; + if (AServiceManager_isDeclared(thermalInstanceName.c_str())) { + auto binder = AServiceManager_waitForService(thermalInstanceName.c_str()); + auto thermalAidlService = IThermal::fromBinder(ndk::SpAIBinder(binder)); + if (thermalAidlService) { + gThermalAidlHal = thermalAidlService; + if (gThermalAidlDeathRecipient.get() == nullptr) { + gThermalAidlDeathRecipient = ndk::ScopedAIBinder_DeathRecipient( + AIBinder_DeathRecipient_new(onThermalAidlBinderDied)); + } + auto linked = AIBinder_linkToDeath(thermalAidlService->asBinder().get(), + gThermalAidlDeathRecipient.get(), nullptr); + if (linked != STATUS_OK) { + ALOGW("Failed to link to death (AIDL): %d", linked); + gThermalAidlHal = nullptr; + } + } else { + ALOGE("Unable to get Thermal AIDL service"); + } return; } - gThermalHal = IThermal::getService(); + ALOGI("Thermal AIDL service is not declared, trying HIDL"); + gThermalHidlHal = hardware::thermal::V1_0::IThermal::getService(); - if (gThermalHal == nullptr) { + if (gThermalHidlHal == nullptr) { ALOGE("Unable to get Thermal service."); } else { - if (gThermalHalDeathRecipient == nullptr) { - gThermalHalDeathRecipient = new ThermalHalDeathRecipient(); + if (gThermalHidlHalDeathRecipient == nullptr) { + gThermalHidlHalDeathRecipient = new ThermalHidlHalDeathRecipient(); } - hardware::Return<bool> linked = gThermalHal->linkToDeath( - gThermalHalDeathRecipient, 0x451F /* cookie */); + hardware::Return<bool> linked = + gThermalHidlHal->linkToDeath(gThermalHidlHalDeathRecipient, 0x451F /* cookie */); if (!linked.isOk()) { ALOGE("Transaction error in linking to ThermalHAL death: %s", - linked.description().c_str()); - gThermalHal = nullptr; + linked.description().c_str()); + gThermalHidlHal = nullptr; } else if (!linked) { ALOGW("Unable to link to ThermalHal death notifications"); - gThermalHal = nullptr; + gThermalHidlHal = nullptr; } else { ALOGD("Link to death notification successful"); } @@ -117,17 +148,27 @@ static void nativeInit(JNIEnv* env, jobject obj) { getThermalHalLocked(); } -static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) { - std::lock_guard<std::mutex> lock(gThermalHalMutex); - getThermalHalLocked(); - if (gThermalHal == nullptr) { - ALOGE("Couldn't get fan speeds because of HAL error."); +static jfloatArray getFanSpeedsAidl(JNIEnv *env) { + std::vector<CoolingDevice> list; + auto status = gThermalAidlHal->getCoolingDevices(&list); + if (!status.isOk()) { + ALOGE("getFanSpeeds failed status: %s", status.getMessage()); return env->NewFloatArray(0); } + float values[list.size()]; + for (size_t i = 0; i < list.size(); ++i) { + values[i] = list[i].value; + } + jfloatArray fanSpeeds = env->NewFloatArray(list.size()); + env->SetFloatArrayRegion(fanSpeeds, 0, list.size(), values); + return fanSpeeds; +} - hidl_vec<CoolingDevice> list; - Return<void> ret = gThermalHal->getCoolingDevices( - [&list](ThermalStatus status, hidl_vec<CoolingDevice> devices) { +static jfloatArray getFanSpeedsHidl(JNIEnv *env) { + hidl_vec<hardware::thermal::V1_0::CoolingDevice> list; + Return<void> ret = gThermalHidlHal->getCoolingDevices( + [&list](ThermalStatus status, + hidl_vec<hardware::thermal::V1_0::CoolingDevice> devices) { if (status.code == ThermalStatusCode::SUCCESS) { list = std::move(devices); } else { @@ -137,9 +178,9 @@ static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) { }); if (!ret.isOk()) { - ALOGE("getCoolingDevices failed status: %s", ret.description().c_str()); + ALOGE("getFanSpeeds failed status: %s", ret.description().c_str()); + return env->NewFloatArray(0); } - float values[list.size()]; for (size_t i = 0; i < list.size(); ++i) { values[i] = list[i].currentValue; @@ -149,17 +190,79 @@ static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) { return fanSpeeds; } -static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, int type, - int source) { +static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) { std::lock_guard<std::mutex> lock(gThermalHalMutex); getThermalHalLocked(); - if (gThermalHal == nullptr) { - ALOGE("Couldn't get device temperatures because of HAL error."); + if (!gThermalHidlHal && !gThermalAidlHal) { + ALOGE("Couldn't get fan speeds because of HAL error."); return env->NewFloatArray(0); } - hidl_vec<Temperature> list; - Return<void> ret = gThermalHal->getTemperatures( - [&list](ThermalStatus status, hidl_vec<Temperature> temperatures) { + if (gThermalAidlHal) { + return getFanSpeedsAidl(env); + } + return getFanSpeedsHidl(env); +} + +static jfloatArray getDeviceTemperaturesAidl(JNIEnv *env, int type, int source) { + jfloat *values; + size_t length = 0; + if (source == TEMPERATURE_CURRENT) { + std::vector<Temperature> list; + auto status = + gThermalAidlHal->getTemperaturesWithType(static_cast<TemperatureType>(type), &list); + + if (!status.isOk()) { + ALOGE("getDeviceTemperatures failed status: %s", status.getMessage()); + return env->NewFloatArray(0); + } + values = new jfloat[list.size()]; + for (const auto &temp : list) { + if (static_cast<int>(temp.type) == type) { + values[length++] = finalizeTemperature(temp.value); + } + } + } else if (source == TEMPERATURE_THROTTLING_BELOW_VR_MIN) { + values = new jfloat[1]; + values[length++] = gUndefinedTemperature; + } else { + std::vector<TemperatureThreshold> list; + auto status = + gThermalAidlHal->getTemperatureThresholdsWithType(static_cast<TemperatureType>( + type), + &list); + + if (!status.isOk()) { + ALOGE("getDeviceTemperatures failed status: %s", status.getMessage()); + return env->NewFloatArray(0); + } + values = new jfloat[list.size()]; + for (auto &t : list) { + if (static_cast<int>(t.type) == type) { + switch (source) { + case TEMPERATURE_THROTTLING: + values[length++] = + finalizeTemperature(t.hotThrottlingThresholds[static_cast<int>( + ThrottlingSeverity::SEVERE)]); + break; + case TEMPERATURE_SHUTDOWN: + values[length++] = + finalizeTemperature(t.hotThrottlingThresholds[static_cast<int>( + ThrottlingSeverity::SHUTDOWN)]); + break; + } + } + } + } + jfloatArray deviceTemps = env->NewFloatArray(length); + env->SetFloatArrayRegion(deviceTemps, 0, length, values); + return deviceTemps; +} + +static jfloatArray getDeviceTemperaturesHidl(JNIEnv *env, int type, int source) { + hidl_vec<hardware::thermal::V1_0::Temperature> list; + Return<void> ret = gThermalHidlHal->getTemperatures( + [&list](ThermalStatus status, + hidl_vec<hardware::thermal::V1_0::Temperature> temperatures) { if (status.code == ThermalStatusCode::SUCCESS) { list = std::move(temperatures); } else { @@ -170,9 +273,9 @@ static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, if (!ret.isOk()) { ALOGE("getDeviceTemperatures failed status: %s", ret.description().c_str()); + return env->NewFloatArray(0); } - - jfloat values[list.size()]; + float values[list.size()]; size_t length = 0; for (size_t i = 0; i < list.size(); ++i) { if (static_cast<int>(list[i].type) == type) { @@ -197,16 +300,34 @@ static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, return deviceTemps; } +static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, int type, + int source) { + std::lock_guard<std::mutex> lock(gThermalHalMutex); + getThermalHalLocked(); + if (!gThermalHidlHal && !gThermalAidlHal) { + ALOGE("Couldn't get device temperatures because of HAL error."); + return env->NewFloatArray(0); + } + if (gThermalAidlHal) { + return getDeviceTemperaturesAidl(env, type, source); + } + return getDeviceTemperaturesHidl(env, type, source); +} + static jobjectArray nativeGetCpuUsages(JNIEnv *env, jclass /* clazz */) { std::lock_guard<std::mutex> lock(gThermalHalMutex); getThermalHalLocked(); - if (gThermalHal == nullptr || !gCpuUsageInfoClassInfo.initMethod) { + if (gThermalAidlHal) { + ALOGW("getCpuUsages is not supported"); + return env->NewObjectArray(0, gCpuUsageInfoClassInfo.clazz, nullptr); + } + if (gThermalHidlHal == nullptr || !gCpuUsageInfoClassInfo.initMethod) { ALOGE("Couldn't get CPU usages because of HAL error."); return env->NewObjectArray(0, gCpuUsageInfoClassInfo.clazz, nullptr); } - hidl_vec<CpuUsage> list; - Return<void> ret = gThermalHal->getCpuUsages( - [&list](ThermalStatus status, hidl_vec<CpuUsage> cpuUsages) { + hidl_vec<hardware::thermal::V1_0::CpuUsage> list; + Return<void> ret = gThermalHidlHal->getCpuUsages( + [&list](ThermalStatus status, hidl_vec<hardware::thermal::V1_0::CpuUsage> cpuUsages) { if (status.code == ThermalStatusCode::SUCCESS) { list = std::move(cpuUsages); } else { @@ -217,6 +338,7 @@ static jobjectArray nativeGetCpuUsages(JNIEnv *env, jclass /* clazz */) { if (!ret.isOk()) { ALOGE("getCpuUsages failed status: %s", ret.description().c_str()); + return env->NewObjectArray(0, gCpuUsageInfoClassInfo.clazz, nullptr); } jobjectArray cpuUsages = env->NewObjectArray(list.size(), gCpuUsageInfoClassInfo.clazz, diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp index 356e9a95e311..a916b64fc0bd 100644 --- a/services/core/jni/com_android_server_sensor_SensorService.cpp +++ b/services/core/jni/com_android_server_sensor_SensorService.cpp @@ -17,10 +17,13 @@ #define LOG_TAG "NativeSensorService" #include <android-base/properties.h> +#include <android_os_NativeHandle.h> #include <android_runtime/AndroidRuntime.h> #include <core_jni_helpers.h> +#include <cutils/native_handle.h> #include <cutils/properties.h> #include <jni.h> +#include <nativehelper/JNIPlatformHelp.h> #include <sensorservice/SensorService.h> #include <string.h> #include <utils/Log.h> @@ -28,6 +31,8 @@ #include <mutex> +#include "android_util_Binder.h" + #define PROXIMITY_ACTIVE_CLASS \ "com/android/server/sensors/SensorManagerInternal$ProximityActiveListener" @@ -38,7 +43,10 @@ namespace android { static JavaVM* sJvm = nullptr; static jmethodID sMethodIdOnProximityActive; -static jmethodID sMethodIdOnConfigurationChanged; +static jmethodID sMethodIdRuntimeSensorOnConfigurationChanged; +static jmethodID sMethodIdRuntimeSensorOnDirectChannelCreated; +static jmethodID sMethodIdRuntimeSensorOnDirectChannelDestroyed; +static jmethodID sMethodIdRuntimeSensorOnDirectChannelConfigured; class NativeSensorService { public: @@ -47,7 +55,7 @@ public: void registerProximityActiveListener(); void unregisterProximityActiveListener(); jint registerRuntimeSensor(JNIEnv* env, jint deviceId, jint type, jstring name, jstring vendor, - jobject callback); + jint flags, jobject callback); void unregisterRuntimeSensor(jint handle); jboolean sendRuntimeSensorEvent(JNIEnv* env, jint handle, jint type, jlong timestamp, jfloatArray values); @@ -74,6 +82,9 @@ private: status_t onConfigurationChanged(int32_t handle, bool enabled, int64_t samplingPeriodNs, int64_t batchReportLatencyNs) override; + int onDirectChannelCreated(int fd) override; + void onDirectChannelDestroyed(int channelHandle) override; + int onDirectChannelConfigured(int channelHandle, int sensorHandle, int rateLevel) override; private: jobject mCallback; @@ -108,7 +119,7 @@ void NativeSensorService::unregisterProximityActiveListener() { } jint NativeSensorService::registerRuntimeSensor(JNIEnv* env, jint deviceId, jint type, jstring name, - jstring vendor, jobject callback) { + jstring vendor, jint flags, jobject callback) { if (mService == nullptr) { ALOGD("Dropping registerRuntimeSensor, sensor service not available."); return -1; @@ -119,6 +130,11 @@ jint NativeSensorService::registerRuntimeSensor(JNIEnv* env, jint deviceId, jint .vendor = env->GetStringUTFChars(vendor, 0), .version = sizeof(sensor_t), .type = type, +#ifdef __LP64__ + .flags = static_cast<uint64_t>(flags), +#else + .flags = static_cast<uint32_t>(flags), +#endif }; sp<RuntimeSensorCallbackDelegate> callbackDelegate( @@ -234,12 +250,39 @@ NativeSensorService::RuntimeSensorCallbackDelegate::~RuntimeSensorCallbackDelega status_t NativeSensorService::RuntimeSensorCallbackDelegate::onConfigurationChanged( int32_t handle, bool enabled, int64_t samplingPeriodNs, int64_t batchReportLatencyNs) { auto jniEnv = GetOrAttachJNIEnvironment(sJvm); - return jniEnv->CallIntMethod(mCallback, sMethodIdOnConfigurationChanged, + return jniEnv->CallIntMethod(mCallback, sMethodIdRuntimeSensorOnConfigurationChanged, static_cast<jint>(handle), static_cast<jboolean>(enabled), static_cast<jint>(ns2us(samplingPeriodNs)), static_cast<jint>(ns2us(batchReportLatencyNs))); } +int NativeSensorService::RuntimeSensorCallbackDelegate::onDirectChannelCreated(int fd) { + if (fd <= 0) { + return 0; + } + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + jobject jfd = jniCreateFileDescriptor(jniEnv, fd); + jobject parcelFileDescriptor = newParcelFileDescriptor(jniEnv, jfd); + return jniEnv->CallIntMethod(mCallback, sMethodIdRuntimeSensorOnDirectChannelCreated, + parcelFileDescriptor); +} + +void NativeSensorService::RuntimeSensorCallbackDelegate::onDirectChannelDestroyed( + int channelHandle) { + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + return jniEnv->CallVoidMethod(mCallback, sMethodIdRuntimeSensorOnDirectChannelDestroyed, + static_cast<jint>(channelHandle)); +} + +int NativeSensorService::RuntimeSensorCallbackDelegate::onDirectChannelConfigured(int channelHandle, + int sensorHandle, + int rateLevel) { + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + return jniEnv->CallIntMethod(mCallback, sMethodIdRuntimeSensorOnDirectChannelConfigured, + static_cast<jint>(channelHandle), static_cast<jint>(sensorHandle), + static_cast<jint>(rateLevel)); +} + static jlong startSensorServiceNative(JNIEnv* env, jclass, jobject listener) { NativeSensorService* service = new NativeSensorService(env, listener); return reinterpret_cast<jlong>(service); @@ -256,9 +299,10 @@ static void unregisterProximityActiveListenerNative(JNIEnv* env, jclass, jlong p } static jint registerRuntimeSensorNative(JNIEnv* env, jclass, jlong ptr, jint deviceId, jint type, - jstring name, jstring vendor, jobject callback) { + jstring name, jstring vendor, jint flags, + jobject callback) { auto* service = reinterpret_cast<NativeSensorService*>(ptr); - return service->registerRuntimeSensor(env, deviceId, type, name, vendor, callback); + return service->registerRuntimeSensor(env, deviceId, type, name, vendor, flags, callback); } static void unregisterRuntimeSensorNative(JNIEnv* env, jclass, jlong ptr, jint handle) { @@ -280,7 +324,7 @@ static const JNINativeMethod methods[] = { {"unregisterProximityActiveListenerNative", "(J)V", reinterpret_cast<void*>(unregisterProximityActiveListenerNative)}, {"registerRuntimeSensorNative", - "(JIILjava/lang/String;Ljava/lang/String;L" RUNTIME_SENSOR_CALLBACK_CLASS ";)I", + "(JIILjava/lang/String;Ljava/lang/String;IL" RUNTIME_SENSOR_CALLBACK_CLASS ";)I", reinterpret_cast<void*>(registerRuntimeSensorNative)}, {"unregisterRuntimeSensorNative", "(JI)V", reinterpret_cast<void*>(unregisterRuntimeSensorNative)}, @@ -293,8 +337,17 @@ int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) { jclass listenerClass = FindClassOrDie(env, PROXIMITY_ACTIVE_CLASS); sMethodIdOnProximityActive = GetMethodIDOrDie(env, listenerClass, "onProximityActive", "(Z)V"); jclass runtimeSensorCallbackClass = FindClassOrDie(env, RUNTIME_SENSOR_CALLBACK_CLASS); - sMethodIdOnConfigurationChanged = + sMethodIdRuntimeSensorOnConfigurationChanged = GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onConfigurationChanged", "(IZII)I"); + sMethodIdRuntimeSensorOnDirectChannelCreated = + GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onDirectChannelCreated", + "(Landroid/os/ParcelFileDescriptor;)I"); + sMethodIdRuntimeSensorOnDirectChannelDestroyed = + GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onDirectChannelDestroyed", "(I)V"); + sMethodIdRuntimeSensorOnDirectChannelConfigured = + GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onDirectChannelConfigured", + "(III)I"); + return jniRegisterNativeMethods(env, "com/android/server/sensors/SensorService", methods, NELEM(methods)); } diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java index f8b1e6fc927c..6bf18c27ee05 100644 --- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java @@ -29,6 +29,9 @@ import android.service.credentials.CallingAppInfo; import android.service.credentials.CredentialProviderInfo; import android.util.Log; +import com.android.server.credentials.metrics.ApiName; +import com.android.server.credentials.metrics.ApiStatus; + import java.util.ArrayList; /** @@ -119,18 +122,21 @@ public final class ClearRequestSession extends RequestSession<ClearCredentialSta // TODO: Differentiate btw cancelled and false mChosenProviderMetric.setChosenProviderStatus( MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS); - logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ true); + logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED); finishSession(/*propagateCancellation=*/true); return; } try { mClientCallback.onSuccess(); - logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ true); + logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_SUCCESS); } catch (RemoteException e) { mChosenProviderMetric.setChosenProviderStatus( MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_FAILURE); Log.i(TAG, "Issue while propagating the response to the client"); - logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ false); + logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_FAILURE); } finishSession(/*propagateCancellation=*/false); } @@ -139,7 +145,8 @@ public final class ClearRequestSession extends RequestSession<ClearCredentialSta Log.i(TAG, "respondToClientWithErrorAndFinish"); if (isSessionCancelled()) { // TODO: Differentiate btw cancelled and false - logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ true); + logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED); finishSession(/*propagateCancellation=*/true); return; } @@ -148,7 +155,8 @@ public final class ClearRequestSession extends RequestSession<ClearCredentialSta } catch (RemoteException e) { e.printStackTrace(); } - logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ false); + logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_FAILURE); finishSession(/*propagateCancellation=*/false); } diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java index 4dd0c84d64f6..656e44c4bff2 100644 --- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java @@ -36,6 +36,9 @@ import android.service.credentials.CallingAppInfo; import android.service.credentials.CredentialProviderInfo; import android.util.Log; +import com.android.server.credentials.metrics.ApiName; +import com.android.server.credentials.metrics.ApiStatus; + import java.util.ArrayList; /** @@ -141,16 +144,19 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR } if (isSessionCancelled()) { // TODO: Differentiate btw cancelled and false - logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ true); + logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED); finishSession(/*propagateCancellation=*/true); return; } try { mClientCallback.onResponse(response); - logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ true); + logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_SUCCESS); } catch (RemoteException e) { Log.i(TAG, "Issue while responding to client: " + e.getMessage()); - logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ false); + logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_FAILURE); } finishSession(/*propagateCancellation=*/false); } @@ -163,7 +169,8 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR } if (isSessionCancelled()) { // TODO: Differentiate btw cancelled and false - logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ true); + logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED); finishSession(/*propagateCancellation=*/true); return; } @@ -172,10 +179,20 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR } catch (RemoteException e) { Log.i(TAG, "Issue while responding to client: " + e.getMessage()); } - logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ false); + logFailureOrUserCancel(errorType); finishSession(/*propagateCancellation=*/false); } + private void logFailureOrUserCancel(String errorType) { + if (CreateCredentialException.TYPE_USER_CANCELED.equals(errorType)) { + logApiCall(ApiName.CREATE_CREDENTIAL, + /* apiStatus */ ApiStatus.METRICS_API_STATUS_USER_CANCELED); + } else { + logApiCall(ApiName.CREATE_CREDENTIAL, + /* apiStatus */ ApiStatus.METRICS_API_STATUS_FAILURE); + } + } + @Override public void onProviderStatusChanged(ProviderSession.Status status, ComponentName componentName) { diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index 7a4e7dfd56a3..41ae9118d965 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -64,6 +64,8 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.server.credentials.metrics.ApiName; +import com.android.server.credentials.metrics.ApiStatus; import com.android.server.infra.AbstractMasterSystemService; import com.android.server.infra.SecureSettingsServiceNameResolver; @@ -84,7 +86,7 @@ import java.util.stream.Collectors; */ public final class CredentialManagerService extends AbstractMasterSystemService< - CredentialManagerService, CredentialManagerServiceImpl> { + CredentialManagerService, CredentialManagerServiceImpl> { private static final String TAG = "CredManSysService"; private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API = @@ -267,7 +269,8 @@ public final class CredentialManagerService final long origId = Binder.clearCallingIdentity(); try { return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API, false); + DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API, + false); } finally { Binder.restoreCallingIdentity(origId); } @@ -317,8 +320,8 @@ public final class CredentialManagerService Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>> result = new HashSet<>(); - for (CredentialDescriptionRegistry.FilterResult filterResult: filterResults) { - for (CredentialOption credentialOption: options) { + for (CredentialDescriptionRegistry.FilterResult filterResult : filterResults) { + for (CredentialOption credentialOption : options) { if (filterResult.mFlattenedRequest.equals(credentialOption .getCredentialRetrievalData() .getString(CredentialOption.FLATTENED_REQUEST))) { @@ -364,13 +367,13 @@ public final class CredentialManagerService CallingAppInfo callingAppInfo; try { packageInfo = - getContext() - .getPackageManager() - .getPackageInfoAsUser( - realPackageName, - PackageManager.PackageInfoFlags.of( - PackageManager.GET_SIGNING_CERTIFICATES), - userId); + getContext() + .getPackageManager() + .getPackageInfoAsUser( + realPackageName, + PackageManager.PackageInfoFlags.of( + PackageManager.GET_SIGNING_CERTIFICATES), + userId); callingAppInfo = new CallingAppInfo(realPackageName, packageInfo.signingInfo, origin); } catch (PackageManager.NameNotFoundException e) { Log.i(TAG, "Issue while retrieving signatureInfo : " + e.getMessage()); @@ -421,41 +424,41 @@ public final class CredentialManagerService if (isCredentialDescriptionApiEnabled()) { List<CredentialOption> optionsThatRequireActiveCredentials = request.getCredentialOptions().stream() - .filter( - getCredentialOption -> - !TextUtils.isEmpty( - getCredentialOption - .getCredentialRetrievalData() - .getString( - CredentialOption - .FLATTENED_REQUEST, - null))) - .toList(); + .filter( + getCredentialOption -> + !TextUtils.isEmpty( + getCredentialOption + .getCredentialRetrievalData() + .getString( + CredentialOption + .FLATTENED_REQUEST, + null))) + .toList(); List<CredentialOption> optionsThatDoNotRequireActiveCredentials = request.getCredentialOptions().stream() - .filter( - getCredentialOption -> - TextUtils.isEmpty( - getCredentialOption - .getCredentialRetrievalData() - .getString( - CredentialOption - .FLATTENED_REQUEST, - null))) - .toList(); + .filter( + getCredentialOption -> + TextUtils.isEmpty( + getCredentialOption + .getCredentialRetrievalData() + .getString( + CredentialOption + .FLATTENED_REQUEST, + null))) + .toList(); List<ProviderSession> sessionsWithoutRemoteService = initiateProviderSessionsWithActiveContainers( - session, - getFilteredResultFromRegistry(optionsThatRequireActiveCredentials)); + session, + getFilteredResultFromRegistry(optionsThatRequireActiveCredentials)); List<ProviderSession> sessionsWithRemoteService = initiateProviderSessions( - session, - optionsThatDoNotRequireActiveCredentials.stream() - .map(CredentialOption::getType) - .collect(Collectors.toList())); + session, + optionsThatDoNotRequireActiveCredentials.stream() + .map(CredentialOption::getType) + .collect(Collectors.toList())); Set<ProviderSession> all = new LinkedHashSet<>(); all.addAll(sessionsWithRemoteService); @@ -465,11 +468,11 @@ public final class CredentialManagerService } else { // Initiate all provider sessions providerSessions = - initiateProviderSessions( - session, - request.getCredentialOptions().stream() - .map(CredentialOption::getType) - .collect(Collectors.toList())); + initiateProviderSessions( + session, + request.getCredentialOptions().stream() + .map(CredentialOption::getType) + .collect(Collectors.toList())); } if (providerSessions.isEmpty()) { @@ -481,8 +484,8 @@ public final class CredentialManagerService Log.i( TAG, "Issue invoking onError on IGetCredentialCallback " - + "callback: " - + e.getMessage()); + + "callback: " + + e.getMessage()); } } providerSessions.forEach(ProviderSession::invokeSession); @@ -538,8 +541,8 @@ public final class CredentialManagerService Log.i( TAG, "Issue invoking onError on ICreateCredentialCallback " - + "callback: " - + e.getMessage()); + + "callback: " + + e.getMessage()); } } @@ -654,13 +657,17 @@ public final class CredentialManagerService if (serviceComponentName.equals(componentName)) { if (!s.getServicePackageName().equals(callingPackage)) { // The component name and the package name do not match. + MetricUtilities.logApiCalled( + ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE, + ApiStatus.METRICS_API_STATUS_FAILURE, callingUid); Log.w( TAG, "isEnabledCredentialProviderService: Component name does not" + " match package name."); return false; } - + MetricUtilities.logApiCalled(ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE, + ApiStatus.METRICS_API_STATUS_SUCCESS, callingUid); return true; } } @@ -745,7 +752,7 @@ public final class CredentialManagerService enforceCallingPackage(callingPackage, Binder.getCallingUid()); List<CredentialProviderInfo> services = - getServicesForCredentialDescription(UserHandle.getCallingUserId()); + getServicesForCredentialDescription(UserHandle.getCallingUserId()); List<String> providers = services.stream() @@ -801,7 +808,7 @@ public final class CredentialManagerService enforceCallingPackage(callingPackage, Binder.getCallingUid()); List<CredentialProviderInfo> services = - getServicesForCredentialDescription(UserHandle.getCallingUserId()); + getServicesForCredentialDescription(UserHandle.getCallingUserId()); List<String> providers = services.stream() diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java index 32b14d773b95..ce26c885d55f 100644 --- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java @@ -34,6 +34,9 @@ import android.service.credentials.CallingAppInfo; import android.service.credentials.CredentialProviderInfo; import android.util.Log; +import com.android.server.credentials.metrics.ApiName; +import com.android.server.credentials.metrics.ApiStatus; + import java.util.ArrayList; /** @@ -117,16 +120,19 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest } if (isSessionCancelled()) { // TODO: Differentiate btw cancelled and false - logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ false); + logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED); finishSession(/*propagateCancellation=*/true); return; } try { mClientCallback.onResponse(response); - logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ true); + logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_SUCCESS); } catch (RemoteException e) { Log.i(TAG, "Issue while responding to client with a response : " + e.getMessage()); - logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ false); + logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_FAILURE); } finishSession(/*propagateCancellation=*/false); } @@ -137,7 +143,8 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest return; } if (isSessionCancelled()) { - logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ false); + logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */ + ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED); finishSession(/*propagateCancellation=*/true); return; } @@ -147,10 +154,20 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest } catch (RemoteException e) { Log.i(TAG, "Issue while responding to client with error : " + e.getMessage()); } - logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ false); + logFailureOrUserCancel(errorType); finishSession(/*propagateCancellation=*/false); } + private void logFailureOrUserCancel(String errorType) { + if (GetCredentialException.TYPE_USER_CANCELED.equals(errorType)) { + logApiCall(ApiName.GET_CREDENTIAL, + /* apiStatus */ ApiStatus.METRICS_API_STATUS_USER_CANCELED); + } else { + logApiCall(ApiName.GET_CREDENTIAL, + /* apiStatus */ ApiStatus.METRICS_API_STATUS_FAILURE); + } + } + @Override public void onUiCancellation(boolean isUserCancellation) { if (isUserCancellation) { diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java index 27d9836d9b66..f75a9b685d10 100644 --- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java +++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java @@ -16,14 +16,6 @@ package com.android.server.credentials; -import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CLEAR_CREDENTIAL; -import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CREATE_CREDENTIAL; -import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_GET_CREDENTIAL; -import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_UNKNOWN; -import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_CLIENT_CANCELED; -import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE; -import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS; -import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED; import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE; import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_SUCCESS; import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_FAILURE; @@ -35,6 +27,14 @@ import android.content.Context; import android.content.pm.PackageManager; import android.util.Log; +import com.android.internal.util.FrameworkStatsLog; +import com.android.server.credentials.metrics.ApiName; +import com.android.server.credentials.metrics.ApiStatus; +import com.android.server.credentials.metrics.CandidateProviderMetric; +import com.android.server.credentials.metrics.ChosenProviderMetric; + +import java.util.Map; + /** * For all future metric additions, this will contain their names for local usage after importing * from {@link com.android.internal.util.FrameworkStatsLog}. @@ -43,24 +43,10 @@ public class MetricUtilities { private static final String TAG = "MetricUtilities"; - // Metrics constants - protected static final int METRICS_API_NAME_UNKNOWN = - CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_UNKNOWN; - protected static final int METRICS_API_NAME_GET_CREDENTIAL = - CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_GET_CREDENTIAL; - protected static final int METRICS_API_NAME_CREATE_CREDENTIAL = - CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CREATE_CREDENTIAL; - protected static final int METRICS_API_NAME_CLEAR_CREDENTIAL = - CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CLEAR_CREDENTIAL; - // TODO add isEnabled - protected static final int METRICS_API_STATUS_SUCCESS = - CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS; - protected static final int METRICS_API_STATUS_FAILURE = - CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE; - protected static final int METRICS_API_STATUS_CLIENT_CANCEL = - CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_CLIENT_CANCELED; - protected static final int METRICS_API_STATUS_USER_CANCEL = - CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED; + private static final int DEFAULT_INT_32 = -1; + private static final int[] DEFAULT_REPEATED_INT_32 = new int[0]; + + // Metrics constants TODO(b/269290341) migrate to enums eventually to improve protected static final int METRICS_PROVIDER_STATUS_FINAL_FAILURE = CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE; protected static final int METRICS_PROVIDER_STATUS_QUERY_FAILURE = @@ -77,6 +63,7 @@ public class MetricUtilities { * This retrieves the uid of any package name, given a context and a component name for the * package. By default, if the desired package uid cannot be found, it will fall back to a * bogus uid. + * * @return the uid of a given package */ protected static int getPackageUid(Context context, ComponentName componentName) { @@ -92,4 +79,75 @@ public class MetricUtilities { return sessUid; } + /** + * The most common logging helper, handles the overall status of the API request with the + * provider status and latencies. Other versions of this method may be more useful depending + * on the situation, as this is geared towards the logging of {@link ProviderSession} types. + * + * @param apiName the api type to log + * @param apiStatus the api status to log + * @param providers a map with known providers + * @param callingUid the calling UID of the client app + * @param chosenProviderMetric the metric data type of the final chosen provider + */ + protected static void logApiCalled(ApiName apiName, ApiStatus apiStatus, + Map<String, ProviderSession> providers, int callingUid, + ChosenProviderMetric chosenProviderMetric) { + var providerSessions = providers.values(); + int providerSize = providerSessions.size(); + int[] candidateUidList = new int[providerSize]; + int[] candidateQueryRoundTripTimeList = new int[providerSize]; + int[] candidateStatusList = new int[providerSize]; + int index = 0; + for (var session : providerSessions) { + CandidateProviderMetric metric = session.mCandidateProviderMetric; + candidateUidList[index] = metric.getCandidateUid(); + candidateQueryRoundTripTimeList[index] = metric.getQueryLatencyMs(); + candidateStatusList[index] = metric.getProviderQueryStatus(); + index++; + } + FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED, + /* api_name */apiName.getMetricCode(), + /* caller_uid */ callingUid, + /* api_status */ apiStatus.getMetricCode(), + /* repeated_candidate_provider_uid */ candidateUidList, + /* repeated_candidate_provider_round_trip_time_query_microseconds */ + candidateQueryRoundTripTimeList, + /* repeated_candidate_provider_status */ candidateStatusList, + /* chosen_provider_uid */ chosenProviderMetric.getChosenUid(), + /* chosen_provider_round_trip_time_overall_microseconds */ + chosenProviderMetric.getEntireProviderLatencyMs(), + /* chosen_provider_final_phase_microseconds */ + chosenProviderMetric.getFinalPhaseLatencyMs(), + /* chosen_provider_status */ chosenProviderMetric.getChosenProviderStatus()); + } + + /** + * This is useful just to record an API calls' final event, and for no other purpose. It will + * contain default values for all other optional parameters. + * + * TODO(b/271135048) - given space requirements, this may be a good candidate for another atom + * + * @param apiName the api name to log + * @param apiStatus the status to log + * @param callingUid the calling uid + */ + protected static void logApiCalled(ApiName apiName, ApiStatus apiStatus, + int callingUid) { + FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED, + /* api_name */apiName.getMetricCode(), + /* caller_uid */ callingUid, + /* api_status */ apiStatus.getMetricCode(), + /* repeated_candidate_provider_uid */ DEFAULT_REPEATED_INT_32, + /* repeated_candidate_provider_round_trip_time_query_microseconds */ + DEFAULT_REPEATED_INT_32, + /* repeated_candidate_provider_status */ DEFAULT_REPEATED_INT_32, + /* chosen_provider_uid */ DEFAULT_INT_32, + /* chosen_provider_round_trip_time_overall_microseconds */ + DEFAULT_INT_32, + /* chosen_provider_final_phase_microseconds */ + DEFAULT_INT_32, + /* chosen_provider_status */ DEFAULT_INT_32); + } + } diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index 8c94b0ada2a8..6498b6afe208 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -415,25 +415,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential /** Returns true if either an exception or a response is found. */ private void onActionEntrySelected(ProviderPendingIntentResponse providerPendingIntentResponse) { - // Action entry is expected to either contain the final GetCredentialResponse, or it is - // also acceptable if it does not contain anything. In the second case, we re-show this - // action on the UI. - if (providerPendingIntentResponse == null) { - Log.i(TAG, "providerPendingIntentResponse is null"); - return; - } - - GetCredentialException exception = maybeGetPendingIntentException( - providerPendingIntentResponse); - if (exception != null) { - invokeCallbackWithError(exception.getType(), exception.getMessage()); - } - GetCredentialResponse response = PendingIntentResultHandler - .extractGetCredentialResponse( - providerPendingIntentResponse.getResultData()); - if (response != null) { - mCallbacks.onFinalResponseReceived(mComponentName, response); - } + Log.i(TAG, "onActionEntrySelected"); + onCredentialEntrySelected(providerPendingIntentResponse); } @@ -450,11 +433,11 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential /** * When an invalid state occurs, e.g. entry mismatch or no response from provider, - * we send back a TYPE_UNKNOWN error as to the developer. + * we send back a TYPE_NO_CREDENTIAL error as to the developer. */ private void invokeCallbackOnInternalInvalidState() { mCallbacks.onFinalErrorReceived(mComponentName, - GetCredentialException.TYPE_UNKNOWN, null); + GetCredentialException.TYPE_NO_CREDENTIAL, null); } /** Update auth entries status based on an auth entry selected from a different session. */ diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java index f8bbfcfb6a4d..86e05cf62f5f 100644 --- a/services/credentials/java/com/android/server/credentials/RequestSession.java +++ b/services/credentials/java/com/android/server/credentials/RequestSession.java @@ -16,12 +16,7 @@ package com.android.server.credentials; -import static com.android.server.credentials.MetricUtilities.METRICS_API_NAME_CLEAR_CREDENTIAL; -import static com.android.server.credentials.MetricUtilities.METRICS_API_NAME_CREATE_CREDENTIAL; -import static com.android.server.credentials.MetricUtilities.METRICS_API_NAME_GET_CREDENTIAL; -import static com.android.server.credentials.MetricUtilities.METRICS_API_NAME_UNKNOWN; -import static com.android.server.credentials.MetricUtilities.METRICS_API_STATUS_FAILURE; -import static com.android.server.credentials.MetricUtilities.METRICS_API_STATUS_SUCCESS; +import static com.android.server.credentials.MetricUtilities.logApiCalled; import android.annotation.NonNull; import android.annotation.UserIdInt; @@ -39,7 +34,8 @@ import android.service.credentials.CredentialProviderInfo; import android.util.Log; import com.android.internal.R; -import com.android.internal.util.FrameworkStatsLog; +import com.android.server.credentials.metrics.ApiName; +import com.android.server.credentials.metrics.ApiStatus; import com.android.server.credentials.metrics.CandidateProviderMetric; import com.android.server.credentials.metrics.ChosenProviderMetric; @@ -162,50 +158,10 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan } return false; } - // TODO: move these definitions to a separate logging focused class. - enum RequestType { - GET_CREDENTIALS, - CREATE_CREDENTIALS, - CLEAR_CREDENTIALS, - } - private static int getApiNameFromRequestType(RequestType requestType) { - switch (requestType) { - case GET_CREDENTIALS: - return METRICS_API_NAME_GET_CREDENTIAL; - case CREATE_CREDENTIALS: - return METRICS_API_NAME_CREATE_CREDENTIAL; - case CLEAR_CREDENTIALS: - return METRICS_API_NAME_CLEAR_CREDENTIAL; - default: - return METRICS_API_NAME_UNKNOWN; - } - } - - protected void logApiCalled(RequestType requestType, boolean isSuccessfulOverall) { - var providerSessions = mProviders.values(); - int providerSize = providerSessions.size(); - int[] candidateUidList = new int[providerSize]; - int[] candidateQueryRoundTripTimeList = new int[providerSize]; - int[] candidateStatusList = new int[providerSize]; - int index = 0; - for (var session : providerSessions) { - CandidateProviderMetric metric = session.mCandidateProviderMetric; - candidateUidList[index] = metric.getCandidateUid(); - candidateQueryRoundTripTimeList[index] = metric.getQueryLatencyMs(); - candidateStatusList[index] = metric.getProviderQueryStatus(); - index++; - } - FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED, - /* api_name */getApiNameFromRequestType(requestType), /* caller_uid */ - mCallingUid, /* api_status */ - isSuccessfulOverall ? METRICS_API_STATUS_SUCCESS : METRICS_API_STATUS_FAILURE, - candidateUidList, - candidateQueryRoundTripTimeList, - candidateStatusList, mChosenProviderMetric.getChosenUid(), - mChosenProviderMetric.getEntireProviderLatencyMs(), - mChosenProviderMetric.getFinalPhaseLatencyMs(), - mChosenProviderMetric.getChosenProviderStatus()); + protected void logApiCall(ApiName apiName, ApiStatus apiStatus) { + logApiCalled(apiName, apiStatus, mProviders, mCallingUid, + mChosenProviderMetric); } protected boolean isSessionCancelled() { diff --git a/services/credentials/java/com/android/server/credentials/metrics/ApiName.java b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java new file mode 100644 index 000000000000..d4b51dfcc299 --- /dev/null +++ b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.credentials.metrics; + +import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CLEAR_CREDENTIAL; +import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CREATE_CREDENTIAL; +import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_GET_CREDENTIAL; +import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE; +import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_UNKNOWN; + +public enum ApiName { + UNKNOWN(CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_UNKNOWN), + GET_CREDENTIAL(CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_GET_CREDENTIAL), + CREATE_CREDENTIAL(CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CREATE_CREDENTIAL), + CLEAR_CREDENTIAL(CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CLEAR_CREDENTIAL), + IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE( + CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE + ); + + private final int mInnerMetricCode; + + ApiName(int innerMetricCode) { + this.mInnerMetricCode = innerMetricCode; + } + + /** + * Gives the West-world version of the metric name. + * + * @return a code corresponding to the west world metric name + */ + public int getMetricCode() { + return this.mInnerMetricCode; + } +} diff --git a/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java b/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java new file mode 100644 index 000000000000..36a1f2df6d24 --- /dev/null +++ b/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.credentials.metrics; + +import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_CLIENT_CANCELED; +import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE; +import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS; +import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED; + +public enum ApiStatus { + METRICS_API_STATUS_SUCCESS(CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS), + METRICS_API_STATUS_FAILURE(CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE), + METRICS_API_STATUS_CLIENT_CANCELED( + CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_CLIENT_CANCELED), + METRICS_API_STATUS_USER_CANCELED( + CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED); + + private final int mInnerMetricCode; + + ApiStatus(int innerMetricCode) { + this.mInnerMetricCode = innerMetricCode; + } + + /** + * Gives the West-world version of the metric name. + * + * @return a code corresponding to the west world metric name + */ + public int getMetricCode() { + return this.mInnerMetricCode; + } +} diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING index 3976a70a28e5..be7feb5c9bf7 100644 --- a/services/incremental/TEST_MAPPING +++ b/services/incremental/TEST_MAPPING @@ -16,13 +16,13 @@ }, { "name": "service.incremental_test" + }, + { + "name": "CtsInstalledLoadingProgressHostTests" } ], "presubmit-large": [ { - "name": "CtsInstalledLoadingProgressHostTests" - }, - { "name": "CtsContentTestCases", "options": [ { diff --git a/services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java b/services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java new file mode 100644 index 000000000000..0ad418427183 --- /dev/null +++ b/services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.when; + +import android.app.Application; +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.content.Intent; +import android.media.AudioManager; +import android.media.MediaRoute2Info; +import android.os.UserHandle; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.Shadows; +import org.robolectric.shadows.ShadowBluetoothAdapter; +import org.robolectric.shadows.ShadowBluetoothDevice; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +@RunWith(RobolectricTestRunner.class) +public class AudioPoliciesBluetoothRouteControllerTest { + + private static final String DEVICE_ADDRESS_UNKNOWN = ":unknown:ip:address:"; + private static final String DEVICE_ADDRESS_SAMPLE_1 = "30:59:8B:E4:C6:35"; + private static final String DEVICE_ADDRESS_SAMPLE_2 = "0D:0D:A6:FF:8D:B6"; + private static final String DEVICE_ADDRESS_SAMPLE_3 = "2D:9B:0C:C2:6F:78"; + private static final String DEVICE_ADDRESS_SAMPLE_4 = "66:88:F9:2D:A8:1E"; + + private Context mContext; + + private ShadowBluetoothAdapter mShadowBluetoothAdapter; + + @Mock + private BluetoothRouteController.BluetoothRoutesUpdatedListener mListener; + + @Mock + private BluetoothProfileMonitor mBluetoothProfileMonitor; + + private AudioPoliciesBluetoothRouteController mAudioPoliciesBluetoothRouteController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + Application application = ApplicationProvider.getApplicationContext(); + mContext = application; + + BluetoothManager bluetoothManager = (BluetoothManager) + mContext.getSystemService(Context.BLUETOOTH_SERVICE); + + BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); + mShadowBluetoothAdapter = Shadows.shadowOf(bluetoothAdapter); + + mAudioPoliciesBluetoothRouteController = + new AudioPoliciesBluetoothRouteController(mContext, bluetoothAdapter, + mBluetoothProfileMonitor, mListener) { + @Override + boolean isDeviceConnected(BluetoothDevice device) { + return true; + } + }; + + // Enable A2DP profile. + when(mBluetoothProfileMonitor.isProfileSupported(eq(BluetoothProfile.A2DP), any())) + .thenReturn(true); + mShadowBluetoothAdapter.setProfileConnectionState(BluetoothProfile.A2DP, + BluetoothProfile.STATE_CONNECTED); + + mAudioPoliciesBluetoothRouteController.start(UserHandle.of(0)); + } + + @Test + public void getSelectedRoute_noBluetoothRoutesAvailable_returnsNull() { + assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()).isNull(); + } + + @Test + public void selectRoute_noBluetoothRoutesAvailable_returnsFalse() { + assertThat(mAudioPoliciesBluetoothRouteController + .selectRoute(DEVICE_ADDRESS_UNKNOWN)).isFalse(); + } + + @Test + public void selectRoute_noDeviceWithGivenAddress_returnsFalse() { + Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet( + DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_3); + + mShadowBluetoothAdapter.setBondedDevices(devices); + + assertThat(mAudioPoliciesBluetoothRouteController + .selectRoute(DEVICE_ADDRESS_SAMPLE_2)).isFalse(); + } + + @Test + public void selectRoute_deviceIsInDevicesSet_returnsTrue() { + Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet( + DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2); + + mShadowBluetoothAdapter.setBondedDevices(devices); + + assertThat(mAudioPoliciesBluetoothRouteController + .selectRoute(DEVICE_ADDRESS_SAMPLE_1)).isTrue(); + } + + @Test + public void selectRoute_resetSelectedDevice_returnsTrue() { + Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet( + DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2); + + mShadowBluetoothAdapter.setBondedDevices(devices); + + mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_1); + assertThat(mAudioPoliciesBluetoothRouteController.selectRoute(null)).isTrue(); + } + + @Test + public void selectRoute_noSelectedDevice_returnsTrue() { + Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet( + DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2); + + mShadowBluetoothAdapter.setBondedDevices(devices); + + assertThat(mAudioPoliciesBluetoothRouteController.selectRoute(null)).isTrue(); + } + + @Test + public void getSelectedRoute_updateRouteFailed_returnsNull() { + Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet( + DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2); + + mShadowBluetoothAdapter.setBondedDevices(devices); + mAudioPoliciesBluetoothRouteController + .selectRoute(DEVICE_ADDRESS_SAMPLE_3); + + assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()).isNull(); + } + + @Test + public void getSelectedRoute_updateRouteSuccessful_returnsUpdateDevice() { + Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet( + DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4); + + assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()).isNull(); + + mShadowBluetoothAdapter.setBondedDevices(devices); + + assertThat(mAudioPoliciesBluetoothRouteController + .selectRoute(DEVICE_ADDRESS_SAMPLE_4)).isTrue(); + + MediaRoute2Info selectedRoute = mAudioPoliciesBluetoothRouteController.getSelectedRoute(); + assertThat(selectedRoute.getAddress()).isEqualTo(DEVICE_ADDRESS_SAMPLE_4); + } + + @Test + public void getSelectedRoute_resetSelectedRoute_returnsNull() { + Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet( + DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4); + + mShadowBluetoothAdapter.setBondedDevices(devices); + + // Device is not null now. + mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4); + // Rest the device. + mAudioPoliciesBluetoothRouteController.selectRoute(null); + + assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()) + .isNull(); + } + + @Test + public void getTransferableRoutes_noSelectedRoute_returnsAllBluetoothDevices() { + String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1, + DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 }; + Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses); + mShadowBluetoothAdapter.setBondedDevices(devices); + + // Force route controller to update bluetooth devices list. + sendBluetoothDevicesChangedBroadcast(); + + Set<String> transferableDevices = extractAddressesListFrom( + mAudioPoliciesBluetoothRouteController.getTransferableRoutes()); + assertThat(transferableDevices).containsExactlyElementsIn(addresses); + } + + @Test + public void getTransferableRoutes_hasSelectedRoute_returnsRoutesWithoutSelectedDevice() { + String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1, + DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 }; + Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses); + mShadowBluetoothAdapter.setBondedDevices(devices); + + // Force route controller to update bluetooth devices list. + sendBluetoothDevicesChangedBroadcast(); + mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4); + + Set<String> transferableDevices = extractAddressesListFrom( + mAudioPoliciesBluetoothRouteController.getTransferableRoutes()); + assertThat(transferableDevices).containsExactly(DEVICE_ADDRESS_SAMPLE_1, + DEVICE_ADDRESS_SAMPLE_2); + } + + @Test + public void getAllBluetoothRoutes_hasSelectedRoute_returnsAllRoutes() { + String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1, + DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 }; + Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses); + mShadowBluetoothAdapter.setBondedDevices(devices); + + // Force route controller to update bluetooth devices list. + sendBluetoothDevicesChangedBroadcast(); + mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4); + + Set<String> bluetoothDevices = extractAddressesListFrom( + mAudioPoliciesBluetoothRouteController.getAllBluetoothRoutes()); + assertThat(bluetoothDevices).containsExactlyElementsIn(addresses); + } + + @Test + public void updateVolumeForDevice_setVolumeForA2DPTo25_selectedRouteVolumeIsUpdated() { + String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1, + DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 }; + Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses); + mShadowBluetoothAdapter.setBondedDevices(devices); + + // Force route controller to update bluetooth devices list. + sendBluetoothDevicesChangedBroadcast(); + mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4); + + mAudioPoliciesBluetoothRouteController.updateVolumeForDevices( + AudioManager.DEVICE_OUT_BLUETOOTH_A2DP, 25); + + MediaRoute2Info selectedRoute = mAudioPoliciesBluetoothRouteController.getSelectedRoute(); + assertThat(selectedRoute.getVolume()).isEqualTo(25); + } + + private void sendBluetoothDevicesChangedBroadcast() { + Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); + mContext.sendBroadcast(intent); + } + + private static Set<String> extractAddressesListFrom(Collection<MediaRoute2Info> routes) { + Set<String> addresses = new HashSet<>(); + + for (MediaRoute2Info route: routes) { + addresses.add(route.getAddress()); + } + + return addresses; + } + + private static Set<BluetoothDevice> generateFakeBluetoothDevicesSet(String... addresses) { + Set<BluetoothDevice> devices = new HashSet<>(); + + for (String address: addresses) { + devices.add(ShadowBluetoothDevice.newInstance(address)); + } + + return devices; + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java index 95c2ed2f841f..99da415380cd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -759,40 +759,6 @@ public final class BroadcastQueueModernImplTest { } /** - * Verify that sending a broadcast that removes any matching pending - * broadcasts is applied as expected. - */ - @Test - public void testRemoveMatchingFilter() { - final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON); - final BroadcastOptions optionsOn = BroadcastOptions.makeBasic(); - optionsOn.setRemoveMatchingFilter(new IntentFilter(Intent.ACTION_SCREEN_OFF)); - - final Intent screenOff = new Intent(Intent.ACTION_SCREEN_OFF); - final BroadcastOptions optionsOff = BroadcastOptions.makeBasic(); - optionsOff.setRemoveMatchingFilter(new IntentFilter(Intent.ACTION_SCREEN_ON)); - - // Halt all processing so that we get a consistent view - mHandlerThread.getLooper().getQueue().postSyncBarrier(); - - mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, optionsOn)); - mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, optionsOff)); - mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, optionsOn)); - mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, optionsOff)); - - // While we're here, give our health check some test coverage - mImpl.checkHealthLocked(); - - // Marching through the queue we should only have one SCREEN_OFF - // broadcast, since that's the last state we dispatched - final BroadcastProcessQueue queue = mImpl.getProcessQueue(PACKAGE_GREEN, - getUidForPackage(PACKAGE_GREEN)); - queue.makeActiveNextPending(); - assertEquals(Intent.ACTION_SCREEN_OFF, queue.getActive().intent.getAction()); - assertTrue(queue.isEmpty()); - } - - /** * Verify that sending a broadcast with DELIVERY_GROUP_POLICY_MOST_RECENT works as expected. */ @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java index 96b63455598d..3e0e5a8414dd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java @@ -64,6 +64,7 @@ import com.android.server.am.BatteryStatsService; import com.android.server.display.RampAnimator.DualRampAnimator; import com.android.server.display.brightness.BrightnessEvent; import com.android.server.display.color.ColorDisplayService; +import com.android.server.display.layout.Layout; import com.android.server.display.whitebalance.DisplayWhiteBalanceController; import com.android.server.policy.WindowManagerPolicy; import com.android.server.testutils.OffsettableClock; @@ -240,12 +241,15 @@ public final class DisplayPowerController2Test { boolean isEnabled) { DisplayInfo info = new DisplayInfo(); DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); + deviceInfo.uniqueId = uniqueId; when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock); when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled); when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); + when(logicalDisplayMock.getBrightnessThrottlingDataIdLocked()).thenReturn( + DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID); when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); @@ -626,6 +630,19 @@ public final class DisplayPowerController2Test { .setLightSensorEnabled(false); } + @Test + public void testStopScreenOffBrightnessSensorControllerWhenDisplayDeviceChanges() { + setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), + mock(DisplayDeviceConfig.class), /* isEnabled= */ true); + + mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.screenOffBrightnessSensorController).stop(); + } + private DisplayPowerControllerHolder createDisplayPowerController(int displayId, String uniqueId) { return createDisplayPowerController(displayId, uniqueId, /* isEnabled= */ true); @@ -662,8 +679,8 @@ public final class DisplayPowerController2Test { mBrightnessTrackerMock, brightnessSetting, () -> {}, hbmMetadata, /* bootCompleted= */ false); - return new DisplayPowerControllerHolder(dpc, displayPowerState, brightnessSetting, animator, - automaticBrightnessController, wakelockController, + return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting, + animator, automaticBrightnessController, wakelockController, screenOffBrightnessSensorController, hbmMetadata); } @@ -673,6 +690,7 @@ public final class DisplayPowerController2Test { */ private static class DisplayPowerControllerHolder { public final DisplayPowerController2 dpc; + public final LogicalDisplay display; public final DisplayPowerState displayPowerState; public final BrightnessSetting brightnessSetting; public final DualRampAnimator<DisplayPowerState> animator; @@ -681,7 +699,7 @@ public final class DisplayPowerController2Test { public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController; public final HighBrightnessModeMetadata hbmMetadata; - DisplayPowerControllerHolder(DisplayPowerController2 dpc, + DisplayPowerControllerHolder(DisplayPowerController2 dpc, LogicalDisplay display, DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting, DualRampAnimator<DisplayPowerState> animator, AutomaticBrightnessController automaticBrightnessController, @@ -689,6 +707,7 @@ public final class DisplayPowerController2Test { ScreenOffBrightnessSensorController screenOffBrightnessSensorController, HighBrightnessModeMetadata hbmMetadata) { this.dpc = dpc; + this.display = display; this.displayPowerState = displayPowerState; this.brightnessSetting = brightnessSetting; this.animator = animator; diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java index 28319acc5f1b..6c4afd37d8b1 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -64,6 +64,7 @@ import com.android.server.am.BatteryStatsService; import com.android.server.display.RampAnimator.DualRampAnimator; import com.android.server.display.brightness.BrightnessEvent; import com.android.server.display.color.ColorDisplayService; +import com.android.server.display.layout.Layout; import com.android.server.display.whitebalance.DisplayWhiteBalanceController; import com.android.server.policy.WindowManagerPolicy; import com.android.server.testutils.OffsettableClock; @@ -243,12 +244,15 @@ public final class DisplayPowerControllerTest { boolean isEnabled) { DisplayInfo info = new DisplayInfo(); DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); + deviceInfo.uniqueId = uniqueId; when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock); when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled); when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); + when(logicalDisplayMock.getBrightnessThrottlingDataIdLocked()).thenReturn( + DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID); when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); @@ -630,6 +634,19 @@ public final class DisplayPowerControllerTest { .setLightSensorEnabled(false); } + @Test + public void testStopScreenOffBrightnessSensorControllerWhenDisplayDeviceChanges() { + setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), + mock(DisplayDeviceConfig.class), /* isEnabled= */ true); + + mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.screenOffBrightnessSensorController).stop(); + } + private DisplayPowerControllerHolder createDisplayPowerController(int displayId, String uniqueId) { return createDisplayPowerController(displayId, uniqueId, /* isEnabled= */ true); @@ -665,8 +682,9 @@ public final class DisplayPowerControllerTest { mBrightnessTrackerMock, brightnessSetting, () -> {}, hbmMetadata, /* bootCompleted= */ false); - return new DisplayPowerControllerHolder(dpc, displayPowerState, brightnessSetting, animator, - automaticBrightnessController, screenOffBrightnessSensorController, hbmMetadata); + return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting, + animator, automaticBrightnessController, screenOffBrightnessSensorController, + hbmMetadata); } /** @@ -675,6 +693,7 @@ public final class DisplayPowerControllerTest { */ private static class DisplayPowerControllerHolder { public final DisplayPowerController dpc; + public final LogicalDisplay display; public final DisplayPowerState displayPowerState; public final BrightnessSetting brightnessSetting; public final DualRampAnimator<DisplayPowerState> animator; @@ -682,13 +701,14 @@ public final class DisplayPowerControllerTest { public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController; public final HighBrightnessModeMetadata hbmMetadata; - DisplayPowerControllerHolder(DisplayPowerController dpc, + DisplayPowerControllerHolder(DisplayPowerController dpc, LogicalDisplay display, DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting, DualRampAnimator<DisplayPowerState> animator, AutomaticBrightnessController automaticBrightnessController, ScreenOffBrightnessSensorController screenOffBrightnessSensorController, HighBrightnessModeMetadata hbmMetadata) { this.dpc = dpc; + this.display = display; this.displayPowerState = displayPowerState; this.brightnessSetting = brightnessSetting; this.animator = animator; diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index 95a588497980..5f82ec1dde02 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -53,6 +53,7 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.internal.R; import com.android.server.LocalServices; import com.android.server.display.LocalDisplayAdapter.BacklightAdapter; +import com.android.server.display.mode.DisplayModeDirector; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index 3f5d1139c9db..06ba5dd6069b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -664,7 +664,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { private fun mockQueryActivities(action: String, vararg activities: ActivityInfo) { whenever(mocks.componentResolver.queryActivities(any(), argThat { intent: Intent? -> intent != null && (action == intent.action) }, - nullable(), anyLong(), anyInt(), anyInt())) { + nullable(), anyLong(), anyInt())) { ArrayList(activities.asList().map { info: ActivityInfo? -> ResolveInfo().apply { activityInfo = info } }) @@ -674,7 +674,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { private fun mockQueryServices(action: String, vararg services: ServiceInfo) { whenever(mocks.componentResolver.queryServices(any(), argThat { intent: Intent? -> intent != null && (action == intent.action) }, - nullable(), anyLong(), anyInt(), anyInt())) { + nullable(), anyLong(), anyInt())) { ArrayList(services.asList().map { info -> ResolveInfo().apply { serviceInfo = info } }) diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 503e579062d4..96eca7171af0 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -289,7 +289,7 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test - public void testRegisterProxyWithoutPermission() throws Exception { + public void testRegisterProxyWithoutA11yPermission() throws Exception { doThrow(SecurityException.class).when(mMockSecurityPolicy) .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); @@ -301,6 +301,18 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test + public void testRegisterProxyWithoutDevicePermission() throws Exception { + doThrow(SecurityException.class).when(mMockSecurityPolicy) + .enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); + + assertThrows(SecurityException.class, + () -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY)); + verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(), + any(), any(), any()); + } + + @SmallTest + @Test public void testRegisterProxyForDefaultDisplay() throws Exception { assertThrows(IllegalArgumentException.class, () -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY)); @@ -328,7 +340,7 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test - public void testUnRegisterProxyWithoutPermission() throws Exception { + public void testUnRegisterProxyWithoutA11yPermission() { doThrow(SecurityException.class).when(mMockSecurityPolicy) .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); @@ -339,6 +351,17 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test + public void testUnRegisterProxyWithoutDevicePermission() { + doThrow(SecurityException.class).when(mMockSecurityPolicy) + .enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); + + assertThrows(SecurityException.class, + () -> mA11yms.unregisterProxyForDisplay(TEST_DISPLAY)); + verify(mProxyManager, never()).unregisterProxy(TEST_DISPLAY); + } + + @SmallTest + @Test public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() { final AccessibilityUserState userState = mA11yms.mUserStates.get( mA11yms.getCurrentUserIdLocked()); diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 1298e7bbd344..24b003c2c011 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -24,6 +24,7 @@ import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.nullable; import static org.mockito.Mockito.times; @@ -37,6 +38,7 @@ import android.accounts.AccountManagerInternal; import android.accounts.CantAddAccountActivity; import android.accounts.IAccountManagerResponse; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.app.INotificationManager; import android.app.PropertyInvalidatedCache; import android.app.admin.DevicePolicyManager; @@ -171,6 +173,16 @@ public class AccountManagerServiceTest extends AndroidTestCase { setContext(mockContext); mTestInjector = new TestInjector(realTestContext, mockContext, mMockNotificationManager); mAms = new AccountManagerService(mTestInjector); + doAnswer(invocation -> { + final Intent intent = invocation.getArgument(0); + final Bundle options = invocation.getArgument(3); + if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.endsWith(intent.getAction())) { + final BroadcastOptions bOptions = new BroadcastOptions(options); + assertEquals(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT, + bOptions.getDeliveryGroupPolicy()); + } + return null; + }).when(mMockContext).sendBroadcastAsUser(any(), any(), any(), any()); } @Override @@ -3142,7 +3154,7 @@ public class AccountManagerServiceTest extends AndroidTestCase { mAccountRemovedBroadcasts = 0; ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); verify(mMockContext, atLeast(expectedBroadcasts)).sendBroadcastAsUser(captor.capture(), - any(UserHandle.class)); + any(UserHandle.class), any(), any()); for (Intent intent : captor.getAllValues()) { if (AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED.equals(intent.getAction())) { mVisibleAccountsChangedBroadcasts++; @@ -3499,7 +3511,19 @@ public class AccountManagerServiceTest extends AndroidTestCase { @Override public void sendBroadcastAsUser(Intent intent, UserHandle user) { - mMockContext.sendBroadcastAsUser(intent, user); + sendBroadcastAsUser(intent, user, null, null); + } + + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, + Bundle options) { + mMockContext.sendBroadcastAsUser(intent, user, receiverPermission, options); + } + + @Override + public Intent registerReceiver(BroadcastReceiver receiver, + IntentFilter filter, String broadcastPermission, Handler scheduler) { + return mMockContext.registerReceiver(receiver, filter, broadcastPermission, scheduler); } @Override diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java index 4412cfe4a3b5..8cbed2c7ffb8 100644 --- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java +++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java @@ -305,60 +305,4 @@ public class OomAdjusterTests { assertEquals("Interaction event time was not updated correctly.", interactionEventTime, mProcessRecord.mState.getInteractionEventTime()); } - - private void updateShortFgsOwner(int uid, int pid, boolean add) { - sService.mOomAdjuster.updateShortFgsOwner(uid, pid, add); - } - - private void assertHasUidShortForegroundService(int uid, boolean expected) { - assertEquals(expected, sService.mOomAdjuster.hasUidShortForegroundService(uid)); - } - - @Test - public void testHasUidShortForegroundService() { - assertHasUidShortForegroundService(1, false); - assertHasUidShortForegroundService(2, false); - assertHasUidShortForegroundService(3, false); - assertHasUidShortForegroundService(100, false); - assertHasUidShortForegroundService(101, false); - - updateShortFgsOwner(1, 100, true); - assertHasUidShortForegroundService(1, true); - assertHasUidShortForegroundService(100, false); - assertHasUidShortForegroundService(2, false); - - updateShortFgsOwner(1, 101, true); - assertHasUidShortForegroundService(1, true); - assertHasUidShortForegroundService(101, false); - assertHasUidShortForegroundService(2, false); - - updateShortFgsOwner(2, 200, true); - assertHasUidShortForegroundService(1, true); - assertHasUidShortForegroundService(2, true); - assertHasUidShortForegroundService(200, false); - - updateShortFgsOwner(1, 101, false); - assertHasUidShortForegroundService(1, true); - assertHasUidShortForegroundService(2, true); - - updateShortFgsOwner(1, 99, false); // unused PID - assertHasUidShortForegroundService(1, true); - assertHasUidShortForegroundService(2, true); - - updateShortFgsOwner(1, 100, false); - assertHasUidShortForegroundService(1, false); - assertHasUidShortForegroundService(2, true); - - updateShortFgsOwner(1, 100, true); - assertHasUidShortForegroundService(1, true); - assertHasUidShortForegroundService(2, true); - - updateShortFgsOwner(2, 200, false); - assertHasUidShortForegroundService(1, true); - assertHasUidShortForegroundService(2, false); - - updateShortFgsOwner(2, 201, true); - assertHasUidShortForegroundService(1, true); - assertHasUidShortForegroundService(2, true); - } } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java index 760ed9be6234..7642e7bc3b91 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java @@ -87,7 +87,7 @@ public class InputControllerTest { // Allow virtual devices to be created on the looper thread for testing. final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true; - mInputController = new InputController(new Object(), mNativeWrapperMock, + mInputController = new InputController(mNativeWrapperMock, new Handler(TestableLooper.get(this).getLooper()), InstrumentationRegistry.getTargetContext().getSystemService(WindowManager.class), threadVerifier); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java index 6431e88b1acb..1259d7189a6d 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java @@ -81,7 +81,7 @@ public class SensorControllerTest { @Test public void createSensor_invalidHandle_throwsException() { doReturn(/* handle= */0).when(mSensorManagerInternalMock).createRuntimeSensor( - anyInt(), anyInt(), anyString(), anyString(), any()); + anyInt(), anyInt(), anyString(), anyString(), anyInt(), any()); Throwable thrown = assertThrows( RuntimeException.class, @@ -138,7 +138,7 @@ public class SensorControllerTest { private void doCreateSensorSuccessfully() { doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor( - anyInt(), anyInt(), anyString(), anyString(), any()); + anyInt(), anyInt(), anyString(), anyString(), anyInt(), any()); assertThat(mSensorController.createSensor(mSensorToken, mVirtualSensorConfig)) .isEqualTo(SENSOR_HANDLE); } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index 9910a80919cc..09a84da9406a 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -355,7 +355,7 @@ public class VirtualDeviceManagerServiceTest { TestableLooper.get(this), mNativeWrapperMock, mIInputManagerMock); // Allow virtual devices to be created on the looper thread for testing. final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true; - mInputController = new InputController(new Object(), mNativeWrapperMock, + mInputController = new InputController(mNativeWrapperMock, new Handler(TestableLooper.get(this).getLooper()), mContext.getSystemService(WindowManager.class), threadVerifier); mSensorController = @@ -500,7 +500,7 @@ public class VirtualDeviceManagerServiceTest { .build(); doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor( - anyInt(), anyInt(), anyString(), anyString(), any()); + anyInt(), anyInt(), anyString(), anyString(), anyInt(), any()); mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params); VirtualSensor sensor = mLocalService.getVirtualSensor(VIRTUAL_DEVICE_ID_1, SENSOR_HANDLE); diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java index 800f60bec828..ffe2fec380a8 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java @@ -43,6 +43,7 @@ import com.android.internal.os.BackgroundThread; import com.android.server.display.BrightnessThrottler.Injector; import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData; import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel; +import com.android.server.display.mode.DisplayModeDirectorTest; import org.junit.Before; import org.junit.Test; diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java index 6def7b1c8c35..8981160d1f25 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java @@ -176,21 +176,18 @@ public class BrightnessTrackerTest { assertFalse(mInjector.mColorSamplingEnabled); // Update brightness config to enabled color sampling. - mTracker.setBrightnessConfiguration(buildBrightnessConfiguration( - /* collectColorSamples= */ true)); + mTracker.setShouldCollectColorSample(/* collectColorSamples= */ true); mInjector.waitForHandler(); assertTrue(mInjector.mColorSamplingEnabled); // Update brightness config to disable color sampling. - mTracker.setBrightnessConfiguration(buildBrightnessConfiguration( - /* collectColorSamples= */ false)); + mTracker.setShouldCollectColorSample(/* collectColorSamples= */ false); mInjector.waitForHandler(); assertFalse(mInjector.mColorSamplingEnabled); // Pretend screen is off, update config to turn on color sampling. mInjector.sendScreenChange(/* screenOn= */ false); - mTracker.setBrightnessConfiguration(buildBrightnessConfiguration( - /* collectColorSamples= */ true)); + mTracker.setShouldCollectColorSample(/* collectColorSamples= */ true); mInjector.waitForHandler(); assertFalse(mInjector.mColorSamplingEnabled); @@ -883,7 +880,7 @@ public class BrightnessTrackerTest { private void startTracker(BrightnessTracker tracker, float initialBrightness, boolean collectColorSamples) { tracker.start(initialBrightness); - tracker.setBrightnessConfiguration(buildBrightnessConfiguration(collectColorSamples)); + tracker.setShouldCollectColorSample(collectColorSamples); mInjector.waitForHandler(); } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index 1b6b1430ee45..7971fd71ea09 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -28,6 +28,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyLong; @@ -127,6 +128,10 @@ public class DisplayManagerServiceTest { private Context mContext; + private int mHdrConversionMode; + + private int mPreferredHdrOutputType; + private final DisplayManagerService.Injector mShortMockedInjector = new DisplayManagerService.Injector() { @Override @@ -176,6 +181,8 @@ public class DisplayManagerServiceTest { @Override int setHdrConversionMode(int conversionMode, int preferredHdrOutputType, int[] autoHdrTypes) { + mHdrConversionMode = conversionMode; + mPreferredHdrOutputType = preferredHdrOutputType; return Display.HdrCapabilities.HDR_TYPE_INVALID; } @@ -185,7 +192,7 @@ public class DisplayManagerServiceTest { } boolean getHdrOutputConversionSupport() { - return false; + return true; } } @@ -1541,6 +1548,40 @@ public class DisplayManagerServiceTest { new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM)); } + @Test + public void testCreateHdrConversionMode_withInvalidArguments_throwsException() { + assertThrows( + "preferredHdrOutputType must not be set if the conversion mode is " + + "HDR_CONVERSION_PASSTHROUGH", + IllegalArgumentException.class, + () -> new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_PASSTHROUGH, + Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION)); + } + + @Test + public void testSetHdrConversionModeInternal_withInvalidArguments_throwsException() { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + assertThrows("Expected DisplayManager to throw IllegalArgumentException when " + + "preferredHdrOutputType is set and the conversion mode is " + + "HDR_CONVERSION_SYSTEM", + IllegalArgumentException.class, + () -> displayManager.setHdrConversionModeInternal(new HdrConversionMode( + HdrConversionMode.HDR_CONVERSION_SYSTEM, + Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION))); + } + + @Test + public void testSetAndGetHdrConversionModeInternal() { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + final HdrConversionMode mode = new HdrConversionMode( + HdrConversionMode.HDR_CONVERSION_FORCE, + Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION); + displayManager.setHdrConversionModeInternal(mode); + assertEquals(mode, displayManager.getHdrConversionModeSettingInternal()); + assertEquals(mode.getConversionMode(), mHdrConversionMode); + assertEquals(mode.getPreferredHdrOutputType(), mPreferredHdrOutputType); + } + private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled) throws Exception { DisplayManagerService displayManager = diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index 7e6eeee9e713..f256c8a17c56 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.display; +package com.android.server.display.mode; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS; @@ -27,8 +27,7 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_R import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE; -import static com.android.server.display.DisplayModeDirector.Vote.INVALID_SIZE; -import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID; +import static com.android.server.display.mode.DisplayModeDirector.Vote.INVALID_SIZE; import static com.google.common.truth.Truth.assertThat; @@ -88,9 +87,11 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.LocalServices; -import com.android.server.display.DisplayModeDirector.BrightnessObserver; -import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs; -import com.android.server.display.DisplayModeDirector.Vote; +import com.android.server.display.DisplayDeviceConfig; +import com.android.server.display.TestUtils; +import com.android.server.display.mode.DisplayModeDirector.BrightnessObserver; +import com.android.server.display.mode.DisplayModeDirector.DesiredDisplayModeSpecs; +import com.android.server.display.mode.DisplayModeDirector.Vote; import com.android.server.sensors.SensorManagerInternal; import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener; import com.android.server.statusbar.StatusBarManagerInternal; @@ -126,6 +127,8 @@ public class DisplayModeDirectorTest { private static final int DISPLAY_ID = 0; private static final float TRANSITION_POINT = 0.763f; + private static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY; + private Context mContext; private FakesInjector mInjector; private Handler mHandler; @@ -2234,7 +2237,7 @@ public class DisplayModeDirectorTest { ArgumentCaptor.forClass(IThermalEventListener.class); verify(mThermalServiceMock).registerThermalEventListenerWithType( - thermalEventListener.capture(), eq(Temperature.TYPE_SKIN)); + thermalEventListener.capture(), eq(Temperature.TYPE_SKIN)); final IThermalEventListener listener = thermalEventListener.getValue(); // Verify that there is no skin temperature vote initially. @@ -2548,7 +2551,7 @@ public class DisplayModeDirectorTest { KEY_REFRESH_RATE_IN_HBM_HDR, String.valueOf(fps)); } - void setBrightnessThrottlingData(String brightnessThrottlingData) { + public void setBrightnessThrottlingData(String brightnessThrottlingData) { putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_BRIGHTNESS_THROTTLING_DATA, brightnessThrottlingData); } diff --git a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java new file mode 100644 index 000000000000..24ed42cab63a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import android.annotation.IdRes; +import android.content.Context; +import android.content.res.Resources; +import android.media.AudioManager; +import android.media.AudioRoutesInfo; +import android.media.IAudioRoutesObserver; +import android.media.MediaRoute2Info; +import android.os.RemoteException; +import android.text.TextUtils; + +import com.android.internal.R; +import com.android.server.audio.AudioService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.runners.Parameterized; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.Collection; + +@RunWith(Enclosed.class) +public class DeviceRouteControllerTest { + + private static final String DEFAULT_ROUTE_NAME = "default_route"; + private static final String DEFAULT_HEADPHONES_NAME = "headphone"; + private static final String DEFAULT_HEADSET_NAME = "headset"; + private static final String DEFAULT_DOCK_NAME = "dock"; + private static final String DEFAULT_HDMI_NAME = "hdmi"; + private static final String DEFAULT_USB_NAME = "usb"; + private static final int VOLUME_DEFAULT_VALUE = 0; + private static final int VOLUME_VALUE_SAMPLE_1 = 10; + + private static AudioRoutesInfo createFakeBluetoothAudioRoute() { + AudioRoutesInfo btRouteInfo = new AudioRoutesInfo(); + btRouteInfo.mainType = AudioRoutesInfo.MAIN_SPEAKER; + btRouteInfo.bluetoothName = "bt_device"; + return btRouteInfo; + } + + @RunWith(JUnit4.class) + public static class DefaultDeviceRouteValueTest { + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + private AudioManager mAudioManager; + @Mock + private AudioService mAudioService; + @Mock + private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getResources()).thenReturn(mResources); + } + + @Test + public void initialize_noRoutesInfo_defaultRouteIsNotNull() { + // Mocking default_audio_route_name. + when(mResources.getText(R.string.default_audio_route_name)) + .thenReturn(DEFAULT_ROUTE_NAME); + + // Default route should be initialized even when AudioService returns null. + when(mAudioService.startWatchingRoutes(any())).thenReturn(null); + + DeviceRouteController deviceRouteController = new DeviceRouteController( + mContext, + mAudioManager, + mAudioService, + mOnDeviceRouteChangedListener + ); + + MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute(); + + assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); + assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME)) + .isTrue(); + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); + } + + @Test + public void initialize_bluetoothRouteAvailable_deviceRouteReturnsDefaultRoute() { + // Mocking default_audio_route_name. + when(mResources.getText(R.string.default_audio_route_name)) + .thenReturn(DEFAULT_ROUTE_NAME); + + // This route should be ignored. + AudioRoutesInfo fakeBluetoothAudioRoute = createFakeBluetoothAudioRoute(); + when(mAudioService.startWatchingRoutes(any())).thenReturn(fakeBluetoothAudioRoute); + + DeviceRouteController deviceRouteController = new DeviceRouteController( + mContext, + mAudioManager, + mAudioService, + mOnDeviceRouteChangedListener + ); + + MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute(); + + assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); + assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME)) + .isTrue(); + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); + } + } + + @RunWith(Parameterized.class) + public static class DeviceRouteInitializationTest { + + @Parameterized.Parameters + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] { + { /* expected res */ + com.android.internal.R.string.default_audio_route_name_headphones, + /* expected name */ + DEFAULT_HEADPHONES_NAME, + /* expected type */ + MediaRoute2Info.TYPE_WIRED_HEADPHONES, + /* actual audio route type */ + AudioRoutesInfo.MAIN_HEADPHONES }, + { /* expected res */ + com.android.internal.R.string.default_audio_route_name_headphones, + /* expected name */ + DEFAULT_HEADSET_NAME, + /* expected type */ + MediaRoute2Info.TYPE_WIRED_HEADSET, + /* actual audio route type */ + AudioRoutesInfo.MAIN_HEADSET }, + { /* expected res */ + R.string.default_audio_route_name_dock_speakers, + /* expected name */ + DEFAULT_DOCK_NAME, + /* expected type */ + MediaRoute2Info.TYPE_DOCK, + /* actual audio route type */ + AudioRoutesInfo.MAIN_DOCK_SPEAKERS }, + { /* expected res */ + R.string.default_audio_route_name_external_device, + /* expected name */ + DEFAULT_HDMI_NAME, + /* expected type */ + MediaRoute2Info.TYPE_HDMI, + /* actual audio route type */ + AudioRoutesInfo.MAIN_HDMI }, + { /* expected res */ + R.string.default_audio_route_name_usb, + /* expected name */ + DEFAULT_USB_NAME, + /* expected type */ + MediaRoute2Info.TYPE_USB_DEVICE, + /* actual audio route type */ + AudioRoutesInfo.MAIN_USB } + }); + } + + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + private AudioManager mAudioManager; + @Mock + private AudioService mAudioService; + @Mock + private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener; + + @IdRes + private final int mExpectedRouteNameResource; + private final String mExpectedRouteNameValue; + private final int mExpectedRouteType; + private final int mActualAudioRouteType; + + public DeviceRouteInitializationTest(int expectedRouteNameResource, + String expectedRouteNameValue, + int expectedMediaRouteType, + int actualAudioRouteType) { + this.mExpectedRouteNameResource = expectedRouteNameResource; + this.mExpectedRouteNameValue = expectedRouteNameValue; + this.mExpectedRouteType = expectedMediaRouteType; + this.mActualAudioRouteType = actualAudioRouteType; + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getResources()).thenReturn(mResources); + } + + @Test + public void initialize_wiredRouteAvailable_deviceRouteReturnsWiredRoute() { + // Mocking default_audio_route_name. + when(mResources.getText(R.string.default_audio_route_name)) + .thenReturn(DEFAULT_ROUTE_NAME); + + // At first, WiredRouteController should initialize device + // route based on AudioService response. + AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); + audioRoutesInfo.mainType = mActualAudioRouteType; + when(mAudioService.startWatchingRoutes(any())).thenReturn(audioRoutesInfo); + + when(mResources.getText(mExpectedRouteNameResource)) + .thenReturn(mExpectedRouteNameValue); + + DeviceRouteController deviceRouteController = new DeviceRouteController( + mContext, + mAudioManager, + mAudioService, + mOnDeviceRouteChangedListener + ); + + MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute(); + + assertThat(actualMediaRoute.getType()).isEqualTo(mExpectedRouteType); + assertThat(TextUtils.equals(actualMediaRoute.getName(), mExpectedRouteNameValue)) + .isTrue(); + // Volume did not change, so it should be set to default value (0). + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); + } + } + + @RunWith(JUnit4.class) + public static class VolumeAndDeviceRoutesChangesTest { + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + private AudioManager mAudioManager; + @Mock + private AudioService mAudioService; + @Mock + private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener; + + @Captor + private ArgumentCaptor<IAudioRoutesObserver.Stub> mAudioRoutesObserverCaptor; + + private DeviceRouteController mDeviceRouteController; + private IAudioRoutesObserver.Stub mAudioRoutesObserver; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getResources()).thenReturn(mResources); + + when(mResources.getText(R.string.default_audio_route_name)) + .thenReturn(DEFAULT_ROUTE_NAME); + + // Setting built-in speaker as default speaker. + AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); + audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_SPEAKER; + when(mAudioService.startWatchingRoutes(mAudioRoutesObserverCaptor.capture())) + .thenReturn(audioRoutesInfo); + + mDeviceRouteController = new DeviceRouteController( + mContext, + mAudioManager, + mAudioService, + mOnDeviceRouteChangedListener + ); + + mAudioRoutesObserver = mAudioRoutesObserverCaptor.getValue(); + } + + @Test + public void newDeviceConnects_wiredDevice_deviceRouteReturnsWiredDevice() { + // Connecting wired headset + AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); + audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; + + when(mResources.getText( + com.android.internal.R.string.default_audio_route_name_headphones)) + .thenReturn(DEFAULT_HEADPHONES_NAME); + + // Simulating wired device being connected. + callAudioRoutesObserver(audioRoutesInfo); + + MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute(); + + assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); + assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_HEADPHONES_NAME)) + .isTrue(); + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); + } + + @Test + public void newDeviceConnects_bluetoothDevice_deviceRouteReturnsBluetoothDevice() { + // Simulating bluetooth speaker being connected. + AudioRoutesInfo fakeBluetoothAudioRoute = createFakeBluetoothAudioRoute(); + callAudioRoutesObserver(fakeBluetoothAudioRoute); + + MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute(); + + assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); + assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME)) + .isTrue(); + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); + } + + @Test + public void updateVolume_differentValue_updatesDeviceRouteVolume() { + MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute(); + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); + + assertThat(mDeviceRouteController.updateVolume(VOLUME_VALUE_SAMPLE_1)).isTrue(); + + actualMediaRoute = mDeviceRouteController.getDeviceRoute(); + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_VALUE_SAMPLE_1); + } + + @Test + public void updateVolume_sameValue_returnsFalse() { + assertThat(mDeviceRouteController.updateVolume(VOLUME_VALUE_SAMPLE_1)).isTrue(); + assertThat(mDeviceRouteController.updateVolume(VOLUME_VALUE_SAMPLE_1)).isFalse(); + } + + /** + * Simulates {@link IAudioRoutesObserver.Stub#dispatchAudioRoutesChanged(AudioRoutesInfo)} + * from {@link AudioService}. This happens when there is a wired route change, + * like a wired headset being connected. + * + * @param audioRoutesInfo updated state of connected wired device + */ + private void callAudioRoutesObserver(AudioRoutesInfo audioRoutesInfo) { + try { + // this is a captured observer implementation + // from WiredRoutesController's AudioService#startWatchingRoutes call + mAudioRoutesObserver.dispatchAudioRoutesChanged(audioRoutesInfo); + } catch (RemoteException exception) { + // Should not happen since the object is mocked. + assertWithMessage("An unexpected RemoteException happened.").fail(); + } + } + } + +} diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceShellCommandTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceShellCommandTest.java index 4434a32f733b..32c9e75e1288 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceShellCommandTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceShellCommandTest.java @@ -121,4 +121,31 @@ public class UserManagerServiceShellCommandTest { assertEquals("Couldn't get main user.", mOutStream.toString().trim()); } + @Test + public void testCanSwitchToHeadlessSystemUser() { + doReturn(true).when(mUserManagerService).canSwitchToHeadlessSystemUser(); + doReturn(mWriter).when(mCommand).getOutPrintWriter(); + + assertEquals(0, mCommand.exec(mBinder, in, out, err, + new String[]{"can-switch-to-headless-system-user"}, + mShellCallback, mResultReceiver)); + + mWriter.flush(); + assertEquals("true", mOutStream.toString().trim()); + } + + + @Test + public void testIsMainUserPermanentAdmin() { + doReturn(false).when(mUserManagerService).isMainUserPermanentAdmin(); + doReturn(mWriter).when(mCommand).getOutPrintWriter(); + + assertEquals(0, mCommand.exec(mBinder, in, out, err, + new String[]{"is-main-user-permanent-admin"}, mShellCallback, mResultReceiver)); + + mWriter.flush(); + assertEquals("false", mOutStream.toString().trim()); + } + + } diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index d71deaf3a88d..a0fb3deeb131 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -16,7 +16,6 @@ package com.android.server.power; -import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; import static android.app.AppOpsManager.MODE_ALLOWED; @@ -30,8 +29,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.AdditionalMatchers.gt; -import static org.mockito.AdditionalMatchers.leq; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -153,9 +150,6 @@ public class PowerManagerServiceTest { @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; - @Mock - private ActivityManagerInternal mActivityManagerInternal; - private PowerManagerService mService; private ContextWrapper mContextSpy; private BatteryReceiver mBatteryReceiver; @@ -211,7 +205,6 @@ public class PowerManagerServiceTest { addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock); addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock); addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock); - addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternal); mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); mResourcesSpy = spy(mContextSpy.getResources()); @@ -228,14 +221,6 @@ public class PowerManagerServiceTest { mClock = new OffsettableClock.Stopped(); mTestLooper = new TestLooper(mClock::now); - - // Set up canHoldWakeLocksInDeepDoze. - // - procstate <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE -> true - // - procstate > PROCESS_STATE_BOUND_FOREGROUND_SERVICE -> false - when(mActivityManagerInternal.canHoldWakeLocksInDeepDoze( - anyInt(), leq(PROCESS_STATE_BOUND_FOREGROUND_SERVICE))).thenReturn(true); - when(mActivityManagerInternal.canHoldWakeLocksInDeepDoze( - anyInt(), gt(PROCESS_STATE_BOUND_FOREGROUND_SERVICE))).thenReturn(false); } private PowerManagerService createService() { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 12f124e385f3..6f6e22428eb4 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -38,11 +38,12 @@ import static android.service.notification.Condition.STATE_TRUE; import static android.util.StatsLog.ANNOTATION_ID_IS_UID; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; -import static com.android.os.AtomsProto.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER; -import static com.android.os.AtomsProto.DNDModeProto.ENABLED_FIELD_NUMBER; -import static com.android.os.AtomsProto.DNDModeProto.ID_FIELD_NUMBER; -import static com.android.os.AtomsProto.DNDModeProto.UID_FIELD_NUMBER; -import static com.android.os.AtomsProto.DNDModeProto.ZEN_MODE_FIELD_NUMBER; +import static com.android.os.dnd.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER; +import static com.android.os.dnd.DNDModeProto.ENABLED_FIELD_NUMBER; +import static com.android.os.dnd.DNDModeProto.ID_FIELD_NUMBER; +import static com.android.os.dnd.DNDModeProto.UID_FIELD_NUMBER; +import static com.android.os.dnd.DNDModeProto.ZEN_MODE_FIELD_NUMBER; +import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG; import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE; import static junit.framework.Assert.assertEquals; @@ -95,7 +96,6 @@ import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.Global; import android.service.notification.Condition; -import android.service.notification.DNDModeProto; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.ScheduleInfo; import android.service.notification.ZenPolicy; @@ -898,7 +898,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertEquals(n + 1, events.size()); for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) { if (builder.getAtomId() == DND_MODE_RULE) { - if (builder.getInt(ZEN_MODE_FIELD_NUMBER) == DNDModeProto.ROOT_CONFIG) { + if (builder.getInt(ZEN_MODE_FIELD_NUMBER) == ROOT_CONFIG) { assertTrue(builder.getBoolean(ENABLED_FIELD_NUMBER)); assertFalse(builder.getBoolean(CHANNELS_BYPASSING_FIELD_NUMBER)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index d7e4c5523eee..169586e2d9dc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -1023,7 +1023,7 @@ public class WindowOrganizerTests extends WindowTestsBase { RunningTaskInfo mInfo; @Override - public void addStartingWindow(StartingWindowInfo info) { } + public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { } @Override public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index fafe90a5df55..323894ca76df 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1583,10 +1583,10 @@ class WindowTestsBase extends SystemServiceTestsBase { } @Override - public void addStartingWindow(StartingWindowInfo info) { + public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { synchronized (mWMService.mGlobalLock) { final ActivityRecord activity = mWMService.mRoot.getActivityRecord( - info.appToken); + appToken); IWindow iWindow = mock(IWindow.class); doReturn(mock(IBinder.class)).when(iWindow).asBinder(); final WindowState window = WindowTestsBase.createWindow(null, @@ -1596,8 +1596,8 @@ class WindowTestsBase extends SystemServiceTestsBase { iWindow, mPowerManagerWrapper); activity.mStartingWindow = window; - mAppWindowMap.put(info.appToken, window); - mTaskAppMap.put(info.taskInfo.taskId, info.appToken); + mAppWindowMap.put(appToken, window); + mTaskAppMap.put(info.taskInfo.taskId, appToken); } if (mRunnableWhenAddingSplashScreen != null) { mRunnableWhenAddingSplashScreen.run(); diff --git a/services/usage/java/com/android/server/usage/TEST_MAPPING b/services/usage/java/com/android/server/usage/TEST_MAPPING index 1c0c71b65fd7..a3fe6f2876d7 100644 --- a/services/usage/java/com/android/server/usage/TEST_MAPPING +++ b/services/usage/java/com/android/server/usage/TEST_MAPPING @@ -18,9 +18,7 @@ "exclude-filter": "com.android.server.usage.StorageStatsServiceTest" } ] - } - ], - "presubmit-large": [ + }, { "name": "CtsUsageStatsTestCases", "options": [ diff --git a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java index 0dcf8ce96192..b1cd99420737 100644 --- a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java +++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java @@ -19,7 +19,6 @@ package com.android.server.usb; import android.annotation.NonNull; import android.content.Context; import android.hardware.usb.UsbConfiguration; -import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; @@ -35,9 +34,12 @@ import android.os.Bundle; import android.service.usb.UsbDirectMidiDeviceProto; import android.util.Log; +import com.android.internal.midi.MidiEventMultiScheduler; import com.android.internal.midi.MidiEventScheduler; import com.android.internal.midi.MidiEventScheduler.MidiEvent; import com.android.internal.util.dump.DualDumpOutputStream; +import com.android.server.usb.descriptors.UsbACMidi10Endpoint; +import com.android.server.usb.descriptors.UsbDescriptor; import com.android.server.usb.descriptors.UsbDescriptorParser; import com.android.server.usb.descriptors.UsbEndpointDescriptor; import com.android.server.usb.descriptors.UsbInterfaceDescriptor; @@ -45,6 +47,7 @@ import com.android.server.usb.descriptors.UsbMidiBlockParser; import libcore.io.IoUtils; +import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; @@ -56,7 +59,7 @@ import java.util.ArrayList; */ public final class UsbDirectMidiDevice implements Closeable { private static final String TAG = "UsbDirectMidiDevice"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; private Context mContext; private String mName; @@ -74,9 +77,6 @@ public final class UsbDirectMidiDevice implements Closeable { private MidiDeviceServer mServer; - // event schedulers for each input port of the physical device - private MidiEventScheduler[] mEventSchedulers; - // Timeout for sending a packet to a device. // If bulkTransfer times out, retry sending the packet up to 20 times. private static final int BULK_TRANSFER_TIMEOUT_MILLISECONDS = 50; @@ -86,8 +86,21 @@ public final class UsbDirectMidiDevice implements Closeable { private static final int THREAD_JOIN_TIMEOUT_MILLISECONDS = 200; private ArrayList<UsbDeviceConnection> mUsbDeviceConnections; + + // Array of endpoints by device connection. private ArrayList<ArrayList<UsbEndpoint>> mInputUsbEndpoints; private ArrayList<ArrayList<UsbEndpoint>> mOutputUsbEndpoints; + + // Array of cable counts by device connection. + // Each number here maps to an entry in mInputUsbEndpoints or mOutputUsbEndpoints. + // This is needed because this info is part of UsbEndpointDescriptor but not UsbEndpoint. + private ArrayList<ArrayList<Integer>> mInputUsbEndpointCableCounts; + private ArrayList<ArrayList<Integer>> mOutputUsbEndpointCableCounts; + + // Array of event schedulers by device connection. + // Each number here maps to an entry in mOutputUsbEndpoints. + private ArrayList<ArrayList<MidiEventMultiScheduler>> mMidiEventMultiSchedulers; + private ArrayList<Thread> mThreads; private UsbMidiBlockParser mMidiBlockParser = new UsbMidiBlockParser(); @@ -97,8 +110,6 @@ public final class UsbDirectMidiDevice implements Closeable { private boolean mIsOpen; private boolean mServerAvailable; - private UsbMidiPacketConverter mUsbMidiPacketConverter; - private PowerBoostSetter mPowerBoostSetter = null; private static final byte MESSAGE_TYPE_MIDI_1_CHANNEL_VOICE = 0x02; @@ -238,9 +249,9 @@ public final class UsbDirectMidiDevice implements Closeable { interfaceDescriptor.getEndpointDescriptor(endpointIndex); // 0 is output, 1 << 7 is input. if (endpoint.getDirection() == 0) { - numOutputs++; + numOutputs += getNumJacks(endpoint); } else { - numInputs++; + numInputs += getNumJacks(endpoint); } } } @@ -307,19 +318,21 @@ public final class UsbDirectMidiDevice implements Closeable { Log.d(TAG, "openLocked()"); UsbManager manager = mContext.getSystemService(UsbManager.class); - // Converting from raw MIDI to USB MIDI is not thread-safe. - // UsbMidiPacketConverter creates a converter from raw MIDI - // to USB MIDI for each USB output. - mUsbMidiPacketConverter = new UsbMidiPacketConverter(mNumOutputs); - mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>(); mInputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(); mOutputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(); + mInputUsbEndpointCableCounts = new ArrayList<ArrayList<Integer>>(); + mOutputUsbEndpointCableCounts = new ArrayList<ArrayList<Integer>>(); + mMidiEventMultiSchedulers = new ArrayList<ArrayList<MidiEventMultiScheduler>>(); mThreads = new ArrayList<Thread>(); for (int interfaceIndex = 0; interfaceIndex < mUsbInterfaces.size(); interfaceIndex++) { ArrayList<UsbEndpoint> inputEndpoints = new ArrayList<UsbEndpoint>(); ArrayList<UsbEndpoint> outputEndpoints = new ArrayList<UsbEndpoint>(); + ArrayList<Integer> inputEndpointCableCounts = new ArrayList<Integer>(); + ArrayList<Integer> outputEndpointCableCounts = new ArrayList<Integer>(); + ArrayList<MidiEventMultiScheduler> midiEventMultiSchedulers = + new ArrayList<MidiEventMultiScheduler>(); UsbInterfaceDescriptor interfaceDescriptor = mUsbInterfaces.get(interfaceIndex); for (int endpointIndex = 0; endpointIndex < interfaceDescriptor.getNumEndpoints(); endpointIndex++) { @@ -328,8 +341,13 @@ public final class UsbDirectMidiDevice implements Closeable { // 0 is output, 1 << 7 is input. if (endpoint.getDirection() == 0) { outputEndpoints.add(endpoint.toAndroid(mParser)); + outputEndpointCableCounts.add(getNumJacks(endpoint)); + MidiEventMultiScheduler scheduler = + new MidiEventMultiScheduler(getNumJacks(endpoint)); + midiEventMultiSchedulers.add(scheduler); } else { inputEndpoints.add(endpoint.toAndroid(mParser)); + inputEndpointCableCounts.add(getNumJacks(endpoint)); } } if (!outputEndpoints.isEmpty() || !inputEndpoints.isEmpty()) { @@ -341,40 +359,69 @@ public final class UsbDirectMidiDevice implements Closeable { mUsbDeviceConnections.add(connection); mInputUsbEndpoints.add(inputEndpoints); mOutputUsbEndpoints.add(outputEndpoints); + mInputUsbEndpointCableCounts.add(inputEndpointCableCounts); + mOutputUsbEndpointCableCounts.add(outputEndpointCableCounts); + mMidiEventMultiSchedulers.add(midiEventMultiSchedulers); } } - mEventSchedulers = new MidiEventScheduler[mNumOutputs]; - - for (int i = 0; i < mNumOutputs; i++) { - MidiEventScheduler scheduler = new MidiEventScheduler(); - mEventSchedulers[i] = scheduler; - mMidiInputPortReceivers[i].setReceiver(scheduler.getReceiver()); + // Set up event schedulers + int outputIndex = 0; + for (int connectionIndex = 0; connectionIndex < mMidiEventMultiSchedulers.size(); + connectionIndex++) { + for (int endpointIndex = 0; + endpointIndex < mMidiEventMultiSchedulers.get(connectionIndex).size(); + endpointIndex++) { + int cableCount = + mOutputUsbEndpointCableCounts.get(connectionIndex).get(endpointIndex); + MidiEventMultiScheduler multiScheduler = + mMidiEventMultiSchedulers.get(connectionIndex).get(endpointIndex); + for (int cableNumber = 0; cableNumber < cableCount; cableNumber++) { + MidiEventScheduler scheduler = multiScheduler.getEventScheduler(cableNumber); + mMidiInputPortReceivers[outputIndex].setReceiver(scheduler.getReceiver()); + outputIndex++; + } + } } final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers(); // Create input thread for each input port of the physical device - int portNumber = 0; + int portStartNumber = 0; for (int connectionIndex = 0; connectionIndex < mInputUsbEndpoints.size(); connectionIndex++) { for (int endpointIndex = 0; endpointIndex < mInputUsbEndpoints.get(connectionIndex).size(); endpointIndex++) { + // Each USB endpoint maps to one or more outputReceivers. USB MIDI data from an + // endpoint should be sent to the appropriate outputReceiver. A new thread is + // created and waits for incoming USB data. Once the data is received, it is added + // to the packet converter. The packet converter acts as an inverse multiplexer. + // With a for loop, data is pulled per cable and sent to the correct output + // receiver. The first byte of each legacy MIDI 1.0 USB message indicates which + // cable the data should be used and is how the reverse multiplexer directs data. + // For MIDI UMP endpoints, a multiplexer is not needed as we are just swapping + // the endianness of the packets. final UsbDeviceConnection connectionFinal = mUsbDeviceConnections.get(connectionIndex); final UsbEndpoint endpointFinal = mInputUsbEndpoints.get(connectionIndex).get(endpointIndex); - final int portFinal = portNumber; + final int portStartFinal = portStartNumber; + final int cableCountFinal = + mInputUsbEndpointCableCounts.get(connectionIndex).get(endpointIndex); - Thread newThread = new Thread("UsbDirectMidiDevice input thread " + portFinal) { + Thread newThread = new Thread("UsbDirectMidiDevice input thread " + + portStartFinal) { @Override public void run() { final UsbRequest request = new UsbRequest(); + final UsbMidiPacketConverter packetConverter = new UsbMidiPacketConverter(); + packetConverter.createDecoders(cableCountFinal); try { request.initialize(connectionFinal, endpointFinal); byte[] inputBuffer = new byte[endpointFinal.getMaxPacketSize()]; - while (true) { + boolean keepGoing = true; + while (keepGoing) { if (Thread.currentThread().interrupted()) { Log.w(TAG, "input thread interrupted"); break; @@ -404,42 +451,56 @@ public final class UsbDirectMidiDevice implements Closeable { logByteArray("Input before conversion ", inputBuffer, 0, bytesRead); } + + // Add packets into the packet decoder. + if (!mIsUniversalMidiDevice) { + packetConverter.decodeMidiPackets(inputBuffer, bytesRead); + } + byte[] convertedArray; - if (mIsUniversalMidiDevice) { - // For USB, each 32 bit word of a UMP is - // sent with the least significant byte first. - convertedArray = swapEndiannessPerWord(inputBuffer, - bytesRead); - } else { - if (mUsbMidiPacketConverter == null) { - Log.w(TAG, "mUsbMidiPacketConverter is null"); - break; + for (int cableNumber = 0; cableNumber < cableCountFinal; + cableNumber++) { + if (mIsUniversalMidiDevice) { + // For USB, each 32 bit word of a UMP is + // sent with the least significant byte first. + convertedArray = swapEndiannessPerWord(inputBuffer, + bytesRead); + } else { + convertedArray = + packetConverter.pullDecodedMidiPackets( + cableNumber); } - convertedArray = - mUsbMidiPacketConverter.usbMidiToRawMidi( - inputBuffer, bytesRead); - } - if (DEBUG) { - logByteArray("Input after conversion ", convertedArray, - 0, convertedArray.length); - } + if (DEBUG) { + logByteArray("Input " + cableNumber + + " after conversion ", convertedArray, 0, + convertedArray.length); + } - if ((outputReceivers == null) - || (outputReceivers[portFinal] == null)) { - Log.w(TAG, "outputReceivers is null"); - break; - } - outputReceivers[portFinal].send(convertedArray, 0, - convertedArray.length, timestamp); - - // Boost power if there seems to be a voice message. - // For legacy devices, boost when message is more than size 1. - // For UMP devices, boost for channel voice messages. - if ((mPowerBoostSetter != null && convertedArray.length > 1) - && (!mIsUniversalMidiDevice - || isChannelVoiceMessage(convertedArray))) { - mPowerBoostSetter.boostPower(); + if (convertedArray.length == 0) { + continue; + } + + if ((outputReceivers == null) + || (outputReceivers[portStartFinal + cableNumber] + == null)) { + Log.w(TAG, "outputReceivers is null"); + keepGoing = false; + break; + } + outputReceivers[portStartFinal + cableNumber].send( + convertedArray, 0, convertedArray.length, + timestamp); + + // Boost power if there seems to be a voice message. + // For legacy devices, boost if message length > 1. + // For UMP devices, boost for channel voice messages. + if ((mPowerBoostSetter != null + && convertedArray.length > 1) + && (!mIsUniversalMidiDevice + || isChannelVoiceMessage(convertedArray))) { + mPowerBoostSetter.boostPower(); + } } } } @@ -455,64 +516,93 @@ public final class UsbDirectMidiDevice implements Closeable { }; newThread.start(); mThreads.add(newThread); - portNumber++; + portStartNumber += cableCountFinal; } } // Create output thread for each output port of the physical device - portNumber = 0; + portStartNumber = 0; for (int connectionIndex = 0; connectionIndex < mOutputUsbEndpoints.size(); connectionIndex++) { for (int endpointIndex = 0; endpointIndex < mOutputUsbEndpoints.get(connectionIndex).size(); endpointIndex++) { + // Each USB endpoint maps to one or more MIDI ports. Each port has an event + // scheduler that is used to pull incoming raw MIDI bytes from Android apps. + // With a MidiEventMultiScheduler, data can be pulled if any of the schedulers + // have new incoming data. This data is then packaged as USB MIDI packets before + // getting sent through USB. One thread will be created per endpoint that pulls + // data from all relevant event schedulers. Raw MIDI from the event schedulers + // will be converted to the correct USB MIDI format before getting sent through + // USB. + final UsbDeviceConnection connectionFinal = mUsbDeviceConnections.get(connectionIndex); final UsbEndpoint endpointFinal = mOutputUsbEndpoints.get(connectionIndex).get(endpointIndex); - final int portFinal = portNumber; - final MidiEventScheduler eventSchedulerFinal = mEventSchedulers[portFinal]; - - Thread newThread = new Thread("UsbDirectMidiDevice output thread " + portFinal) { + final int portStartFinal = portStartNumber; + final int cableCountFinal = + mOutputUsbEndpointCableCounts.get(connectionIndex).get(endpointIndex); + final MidiEventMultiScheduler multiSchedulerFinal = + mMidiEventMultiSchedulers.get(connectionIndex).get(endpointIndex); + + Thread newThread = new Thread("UsbDirectMidiDevice output write thread " + + portStartFinal) { @Override public void run() { try { - while (true) { - if (Thread.currentThread().interrupted()) { - Log.w(TAG, "output thread interrupted"); + final ByteArrayOutputStream midi2ByteStream = + new ByteArrayOutputStream(); + final UsbMidiPacketConverter packetConverter = + new UsbMidiPacketConverter(); + packetConverter.createEncoders(cableCountFinal); + boolean isInterrupted = false; + while (!isInterrupted) { + boolean wasSuccessful = multiSchedulerFinal.waitNextEvent(); + if (!wasSuccessful) { + Log.d(TAG, "output thread closed"); break; } - MidiEvent event; - try { - event = (MidiEvent) eventSchedulerFinal.waitNextEvent(); - } catch (InterruptedException e) { - Log.w(TAG, "event scheduler interrupted"); - break; - } - if (event == null) { - Log.w(TAG, "event is null"); - break; + long now = System.nanoTime(); + for (int cableNumber = 0; cableNumber < cableCountFinal; + cableNumber++) { + MidiEventScheduler eventScheduler = + multiSchedulerFinal.getEventScheduler(cableNumber); + MidiEvent event = + (MidiEvent) eventScheduler.getNextEvent(now); + while (event != null) { + if (DEBUG) { + logByteArray("Output before conversion ", + event.data, 0, event.count); + } + if (mIsUniversalMidiDevice) { + // For USB, each 32 bit word of a UMP is + // sent with the least significant byte first. + byte[] convertedArray = swapEndiannessPerWord( + event.data, event.count); + midi2ByteStream.write(convertedArray, 0, + convertedArray.length); + } else { + packetConverter.encodeMidiPackets(event.data, + event.count, cableNumber); + } + eventScheduler.addEventToPool(event); + event = (MidiEvent) eventScheduler.getNextEvent(now); + } } - if (DEBUG) { - logByteArray("Output before conversion ", event.data, 0, - event.count); + if (Thread.currentThread().interrupted()) { + Log.d(TAG, "output thread interrupted"); + break; } - byte[] convertedArray; + byte[] convertedArray = new byte[0]; if (mIsUniversalMidiDevice) { - // For USB, each 32 bit word of a UMP is - // sent with the least significant byte first. - convertedArray = swapEndiannessPerWord(event.data, - event.count); + convertedArray = midi2ByteStream.toByteArray(); + midi2ByteStream.reset(); } else { - if (mUsbMidiPacketConverter == null) { - Log.w(TAG, "mUsbMidiPacketConverter is null"); - break; - } convertedArray = - mUsbMidiPacketConverter.rawMidiToUsbMidi( - event.data, event.count, portFinal); + packetConverter.pullEncodedMidiPackets(); } if (DEBUG) { @@ -520,7 +610,6 @@ public final class UsbDirectMidiDevice implements Closeable { convertedArray.length); } - boolean isInterrupted = false; // Split the packet into multiple if they are greater than the // endpoint's max packet size. for (int curPacketStart = 0; @@ -558,11 +647,9 @@ public final class UsbDirectMidiDevice implements Closeable { } } } - if (isInterrupted == true) { - break; - } - eventSchedulerFinal.addEventToPool(event); } + } catch (InterruptedException e) { + Log.w(TAG, "output thread: ", e); } catch (NullPointerException e) { Log.e(TAG, "output thread: ", e); } @@ -571,7 +658,7 @@ public final class UsbDirectMidiDevice implements Closeable { }; newThread.start(); mThreads.add(newThread); - portNumber++; + portStartNumber += cableCountFinal; } } @@ -667,11 +754,21 @@ public final class UsbDirectMidiDevice implements Closeable { } mThreads = null; - for (int i = 0; i < mEventSchedulers.length; i++) { + for (int i = 0; i < mMidiInputPortReceivers.length; i++) { mMidiInputPortReceivers[i].setReceiver(null); - mEventSchedulers[i].close(); } - mEventSchedulers = null; + + for (int connectionIndex = 0; connectionIndex < mMidiEventMultiSchedulers.size(); + connectionIndex++) { + for (int endpointIndex = 0; + endpointIndex < mMidiEventMultiSchedulers.get(connectionIndex).size(); + endpointIndex++) { + MidiEventMultiScheduler multiScheduler = + mMidiEventMultiSchedulers.get(connectionIndex).get(endpointIndex); + multiScheduler.close(); + } + } + mMidiEventMultiSchedulers = null; for (UsbDeviceConnection connection : mUsbDeviceConnections) { connection.close(); @@ -679,8 +776,8 @@ public final class UsbDirectMidiDevice implements Closeable { mUsbDeviceConnections = null; mInputUsbEndpoints = null; mOutputUsbEndpoints = null; - - mUsbMidiPacketConverter = null; + mInputUsbEndpointCableCounts = null; + mOutputUsbEndpointCableCounts = null; mIsOpen = false; } @@ -788,4 +885,19 @@ public final class UsbDirectMidiDevice implements Closeable { return messageType == MESSAGE_TYPE_MIDI_1_CHANNEL_VOICE || messageType == MESSAGE_TYPE_MIDI_2_CHANNEL_VOICE; } + + // Returns the number of jacks for MIDI 1.0 endpoints. + // For MIDI 2.0 endpoints, this concept does not exist and each endpoint should be treated as + // one port. + private int getNumJacks(UsbEndpointDescriptor usbEndpointDescriptor) { + UsbDescriptor classSpecificEndpointDescriptor = + usbEndpointDescriptor.getClassSpecificEndpointDescriptor(); + if (classSpecificEndpointDescriptor != null + && (classSpecificEndpointDescriptor instanceof UsbACMidi10Endpoint)) { + UsbACMidi10Endpoint midiEndpoint = + (UsbACMidi10Endpoint) classSpecificEndpointDescriptor; + return midiEndpoint.getNumJacks(); + } + return 1; + } } diff --git a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java index 56bb23681741..65d7a41cf1ec 100644 --- a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java +++ b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java @@ -16,12 +16,17 @@ package com.android.server.usb; +import android.util.Log; + import java.io.ByteArrayOutputStream; /** - * Converts between MIDI packets and USB MIDI 1.0 packets. + * Converts between raw MIDI packets and USB MIDI 1.0 packets. + * This is NOT thread-safe. Please handle locking outside this function for multiple threads. + * For data mapping to an invalid cable number, this converter will use the first cable. */ public class UsbMidiPacketConverter { + private static final String TAG = "UsbMidiPacketConverter"; // Refer to Table 4-1 in USB MIDI 1.0 spec. private static final int[] PAYLOAD_SIZE = new int[]{ @@ -74,54 +79,133 @@ public class UsbMidiPacketConverter { private static final byte SYSEX_START_EXCLUSIVE = (byte) 0xF0; private static final byte SYSEX_END_EXCLUSIVE = (byte) 0xF7; - private UsbMidiDecoder mUsbMidiDecoder = new UsbMidiDecoder(); private UsbMidiEncoder[] mUsbMidiEncoders; + private ByteArrayOutputStream mEncoderOutputStream = new ByteArrayOutputStream(); - public UsbMidiPacketConverter(int numEncoders) { - mUsbMidiEncoders = new UsbMidiEncoder[numEncoders]; - for (int i = 0; i < numEncoders; i++) { - mUsbMidiEncoders[i] = new UsbMidiEncoder(); - } - } + private UsbMidiDecoder mUsbMidiDecoder; /** - * Converts a USB MIDI array into a raw MIDI array. + * Creates encoders. * - * @param usbMidiBytes the USB MIDI bytes to convert - * @param size the size of usbMidiBytes - * @return byte array of raw MIDI packets + * createEncoders() must be called before raw MIDI can be converted to USB MIDI. + * + * @param size the number of encoders to create */ - public byte[] usbMidiToRawMidi(byte[] usbMidiBytes, int size) { - return mUsbMidiDecoder.decode(usbMidiBytes, size); + public void createEncoders(int size) { + mUsbMidiEncoders = new UsbMidiEncoder[size]; + for (int i = 0; i < size; i++) { + mUsbMidiEncoders[i] = new UsbMidiEncoder(i); + } } /** * Converts a raw MIDI array into a USB MIDI array. * + * Call pullEncodedMidiPackets to retrieve the byte array. + * * @param midiBytes the raw MIDI bytes to convert * @param size the size of usbMidiBytes * @param encoderId which encoder to use + */ + public void encodeMidiPackets(byte[] midiBytes, int size, int encoderId) { + // Use the first encoder if the encoderId is invalid. + if (encoderId >= mUsbMidiEncoders.length) { + Log.w(TAG, "encoderId " + encoderId + " invalid"); + encoderId = 0; + } + byte[] encodedPacket = mUsbMidiEncoders[encoderId].encode(midiBytes, size); + mEncoderOutputStream.write(encodedPacket, 0, encodedPacket.length); + } + + /** + * Returns the encoded MIDI packets from encodeMidiPackets + * * @return byte array of USB MIDI packets */ - public byte[] rawMidiToUsbMidi(byte[] midiBytes, int size, int encoderId) { - return mUsbMidiEncoders[encoderId].encode(midiBytes, size); + public byte[] pullEncodedMidiPackets() { + byte[] output = mEncoderOutputStream.toByteArray(); + mEncoderOutputStream.reset(); + return output; + } + + /** + * Creates decoders. + * + * createDecoders() must be called before USB MIDI can be converted to raw MIDI. + * + * @param size the number of decoders to create + */ + public void createDecoders(int size) { + mUsbMidiDecoder = new UsbMidiDecoder(size); + } + + /** + * Converts a USB MIDI array into a multiple MIDI arrays, one per cable. + * + * Call pullDecodedMidiPackets to retrieve the byte array. + * + * @param usbMidiBytes the USB MIDI bytes to convert + * @param size the size of usbMidiBytes + */ + public void decodeMidiPackets(byte[] usbMidiBytes, int size) { + mUsbMidiDecoder.decode(usbMidiBytes, size); + } + + /** + * Returns the decoded MIDI packets from decodeMidiPackets + * + * @param cableNumber the cable to pull data from + * @return byte array of raw MIDI packets + */ + public byte[] pullDecodedMidiPackets(int cableNumber) { + return mUsbMidiDecoder.pullBytes(cableNumber); } private class UsbMidiDecoder { + int mNumJacks; + ByteArrayOutputStream[] mDecodedByteArrays; + + UsbMidiDecoder(int numJacks) { + mNumJacks = numJacks; + mDecodedByteArrays = new ByteArrayOutputStream[numJacks]; + for (int i = 0; i < numJacks; i++) { + mDecodedByteArrays[i] = new ByteArrayOutputStream(); + } + } + // Decodes the data from USB MIDI to raw MIDI. // Each valid 4 byte input maps to a 1-3 byte output. // Reference the USB MIDI 1.0 spec for more info. - public byte[] decode(byte[] usbMidiBytes, int size) { + public void decode(byte[] usbMidiBytes, int size) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + if (size % 4 != 0) { + Log.w(TAG, "size " + size + " not multiple of 4"); + } for (int i = 0; i + 3 < size; i += 4) { + int cableNumber = (usbMidiBytes[i] >> 4) & 0x0f; int codeIndex = usbMidiBytes[i] & 0x0f; int numPayloadBytes = PAYLOAD_SIZE[codeIndex]; if (numPayloadBytes < 0) { continue; } - outputStream.write(usbMidiBytes, i + 1, numPayloadBytes); + // Use the first cable if the cable number is invalid. + if (cableNumber >= mNumJacks) { + Log.w(TAG, "cableNumber " + cableNumber + " invalid"); + cableNumber = 0; + } + mDecodedByteArrays[cableNumber].write(usbMidiBytes, i + 1, numPayloadBytes); } - return outputStream.toByteArray(); + } + + public byte[] pullBytes(int cableNumber) { + // Use the first cable if the cable number is invalid. + if (cableNumber >= mNumJacks) { + Log.w(TAG, "cableNumber " + cableNumber + " invalid"); + cableNumber = 0; + } + byte[] output = mDecodedByteArrays[cableNumber].toByteArray(); + mDecodedByteArrays[cableNumber].reset(); + return output; } } @@ -135,6 +219,13 @@ public class UsbMidiPacketConverter { private byte[] mEmptyBytes = new byte[3]; // Used to fill out extra data + private byte mShiftedCableNumber; + + UsbMidiEncoder(int cableNumber) { + // Jack Id is always the left nibble of every byte so shift this now. + mShiftedCableNumber = (byte) (cableNumber << 4); + } + // Encodes the data from raw MIDI to USB MIDI. // Each valid 1-3 byte input maps to a 4 byte output. // Reference the USB MIDI 1.0 spec for more info. @@ -153,7 +244,8 @@ public class UsbMidiPacketConverter { midiBytes[curLocation]; mNumStoredSystemExclusiveBytes++; if (mNumStoredSystemExclusiveBytes == 3) { - outputStream.write(CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES); + outputStream.write(CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES + | mShiftedCableNumber); outputStream.write(mStoredSystemExclusiveBytes, 0, 3); mNumStoredSystemExclusiveBytes = 0; } @@ -179,7 +271,7 @@ public class UsbMidiPacketConverter { byte codeIndexNumber = (byte) ((midiBytes[curLocation] >> 4) & 0x0f); int channelMessageSize = PAYLOAD_SIZE[codeIndexNumber]; if (curLocation + channelMessageSize <= size) { - outputStream.write(codeIndexNumber); + outputStream.write(codeIndexNumber | mShiftedCableNumber); outputStream.write(midiBytes, curLocation, channelMessageSize); // Fill in the rest of the bytes with 0. outputStream.write(mEmptyBytes, 0, 3 - channelMessageSize); @@ -197,8 +289,8 @@ public class UsbMidiPacketConverter { curLocation++; } else if (midiBytes[curLocation] == SYSEX_END_EXCLUSIVE) { // 1 byte is 0x05, 2 bytes is 0x06, and 3 bytes is 0x07 - outputStream.write(CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE - + mNumStoredSystemExclusiveBytes); + outputStream.write((CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE + + mNumStoredSystemExclusiveBytes) | mShiftedCableNumber); mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] = midiBytes[curLocation]; mNumStoredSystemExclusiveBytes++; @@ -218,7 +310,7 @@ public class UsbMidiPacketConverter { } else { int systemMessageSize = PAYLOAD_SIZE[codeIndexNumber]; if (curLocation + systemMessageSize <= size) { - outputStream.write(codeIndexNumber); + outputStream.write(codeIndexNumber | mShiftedCableNumber); outputStream.write(midiBytes, curLocation, systemMessageSize); // Fill in the rest of the bytes with 0. outputStream.write(mEmptyBytes, 0, 3 - systemMessageSize); @@ -236,7 +328,7 @@ public class UsbMidiPacketConverter { } private void writeSingleByte(ByteArrayOutputStream outputStream, byte byteToWrite) { - outputStream.write(CODE_INDEX_NUMBER_SINGLE_BYTE); + outputStream.write(CODE_INDEX_NUMBER_SINGLE_BYTE | mShiftedCableNumber); outputStream.write(byteToWrite); outputStream.write(0); outputStream.write(0); diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java index 1f448acac5e8..117a3d9f374e 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java @@ -118,7 +118,7 @@ public class UsbEndpointDescriptor extends UsbDescriptor { mClassSpecificEndpointDescriptor = descriptor; } - UsbDescriptor getClassSpecificEndpointDescriptor() { + public UsbDescriptor getClassSpecificEndpointDescriptor() { return mClassSpecificEndpointDescriptor; } diff --git a/services/voiceinteraction/TEST_MAPPING b/services/voiceinteraction/TEST_MAPPING index d6c69647d754..5fe1c8d2ecb0 100644 --- a/services/voiceinteraction/TEST_MAPPING +++ b/services/voiceinteraction/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit-large": [ + "presubmit": [ { "name": "CtsVoiceInteractionTestCases", "options": [ diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java index ce1157e1d80c..7d5750e49907 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java @@ -30,11 +30,14 @@ import android.media.soundtrigger.SoundModel; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerModule; import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; +import android.os.BatteryStatsInternal; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.util.Log; import com.android.internal.util.LatencyTracker; +import com.android.server.LocalServices; import java.io.PrintWriter; import java.text.SimpleDateFormat; @@ -291,6 +294,8 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession) throws RemoteException { try { + BatteryStatsHolder.INSTANCE.noteWakingSoundTrigger( + SystemClock.elapsedRealtime(), mOriginatorIdentity.uid); mCallbackDelegate.onRecognition(modelHandle, event, captureSession); logVoidReturn("onRecognition", modelHandle, event); } catch (Exception e) { @@ -304,6 +309,8 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt int captureSession) throws RemoteException { try { + BatteryStatsHolder.INSTANCE.noteWakingSoundTrigger( + SystemClock.elapsedRealtime(), mOriginatorIdentity.uid); startKeyphraseEventLatencyTracking(event); mCallbackDelegate.onPhraseRecognition(modelHandle, event, captureSession); logVoidReturn("onPhraseRecognition", modelHandle, event); @@ -387,6 +394,12 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt } } + private static class BatteryStatsHolder { + private static final BatteryStatsInternal INSTANCE = + LocalServices.getService(BatteryStatsInternal.class); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// // Actual logging logic below. private static final int NUM_EVENTS_TO_DUMP = 64; diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index de99ebf09e27..9170117f39e0 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -2073,6 +2073,14 @@ public class TelecomManager { * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to add a new incoming call. * <p> + * Specify the address associated with the incoming call using + * {@link #EXTRA_INCOMING_CALL_ADDRESS}. If an incoming call is from an anonymous source, omit + * this extra and ensure you specify a valid number presentation via + * {@link Connection#setAddress(Uri, int)} on the {@link Connection} instance you return in + * your + * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)} + * implementation. + * <p> * The incoming call you are adding is assumed to have a video state of * {@link VideoProfile#STATE_AUDIO_ONLY}, unless the extra value * {@link #EXTRA_INCOMING_VIDEO_STATE} is specified. diff --git a/telephony/java/android/telephony/DomainSelectionService.java b/telephony/java/android/telephony/DomainSelectionService.java index c352f2bc3f83..abcce5f61aee 100644 --- a/telephony/java/android/telephony/DomainSelectionService.java +++ b/telephony/java/android/telephony/DomainSelectionService.java @@ -537,9 +537,9 @@ public class DomainSelectionService extends Service { } @Override - public void onWlanSelected() { + public void onWlanSelected(boolean useEmergencyPdn) { try { - mCallback.onWlanSelected(); + mCallback.onWlanSelected(useEmergencyPdn); } catch (Exception e) { Rlog.e(TAG, "onWlanSelected e=" + e); } @@ -702,9 +702,10 @@ public class DomainSelectionService extends Service { } @Override - public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain) { + public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain, + boolean useEmergencyPdn) { try { - mCallback.onDomainSelected(domain); + mCallback.onDomainSelected(domain, useEmergencyPdn); } catch (Exception e) { Rlog.e(TAG, "onDomainSelected e=" + e); } diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index b9008c4ecd81..788d0e88170d 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -1526,7 +1526,7 @@ public class PhoneNumberUtils { * Formats the specified {@code phoneNumber} to the E.164 representation. * * @param phoneNumber the phone number to format. - * @param defaultCountryIso the ISO 3166-1 two letters country code. + * @param defaultCountryIso the ISO 3166-1 two letters country code in UPPER CASE. * @return the E.164 representation, or null if the given phone number is not valid. */ public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) { @@ -1537,7 +1537,7 @@ public class PhoneNumberUtils { * Formats the specified {@code phoneNumber} to the RFC3966 representation. * * @param phoneNumber the phone number to format. - * @param defaultCountryIso the ISO 3166-1 two letters country code. + * @param defaultCountryIso the ISO 3166-1 two letters country code in UPPER CASE. * @return the RFC3966 representation, or null if the given phone number is not valid. */ public static String formatNumberToRFC3966(String phoneNumber, String defaultCountryIso) { diff --git a/telephony/java/android/telephony/TransportSelectorCallback.java b/telephony/java/android/telephony/TransportSelectorCallback.java index d39679072d37..04752e418466 100644 --- a/telephony/java/android/telephony/TransportSelectorCallback.java +++ b/telephony/java/android/telephony/TransportSelectorCallback.java @@ -35,8 +35,10 @@ public interface TransportSelectorCallback { /** * Notify that WLAN transport has been selected. + * + * @param useEmergencyPdn Indicates whether Wi-Fi emergency services use emergency PDN or not. */ - void onWlanSelected(); + void onWlanSelected(boolean useEmergencyPdn); /** * Notify that WWAN transport has been selected. diff --git a/telephony/java/android/telephony/WwanSelectorCallback.java b/telephony/java/android/telephony/WwanSelectorCallback.java index b3682caa4eb3..f9c2620cfaf8 100644 --- a/telephony/java/android/telephony/WwanSelectorCallback.java +++ b/telephony/java/android/telephony/WwanSelectorCallback.java @@ -46,6 +46,7 @@ public interface WwanSelectorCallback { * this interface can be discarded. * * @param domain The selected domain. + * @param useEmergencyPdn Indicates whether emergency services use emergency PDN or not. */ - void onDomainSelected(@NetworkRegistrationInfo.Domain int domain); + void onDomainSelected(@NetworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn); } diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index c3522668a83c..64454cc50a5c 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -166,8 +166,8 @@ public class SatelliteManager { public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility"; /** - * Bundle key to get the respoonse from - * {@link #sendSatelliteDatagram(long, int, SatelliteDatagram, Executor, OutcomeReceiver)}. + * Bundle key to get the respoonse from {@link + * #sendSatelliteDatagram(long, int, SatelliteDatagram, boolean, Executor, OutcomeReceiver)}. * @hide */ public static final String KEY_SEND_SATELLITE_DATAGRAM = "send_satellite_datagram"; @@ -701,6 +701,10 @@ public class SatelliteManager { */ public static final int SATELLITE_MODEM_STATE_OFF = 4; /** + * Satellite modem is unavailable. + */ + public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5; + /** * Satellite modem state is unknown. This generic modem state should be used only when the * modem state cannot be mapped to other specific modem states. */ @@ -713,6 +717,7 @@ public class SatelliteManager { SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING, SATELLITE_MODEM_STATE_DATAGRAM_RETRYING, SATELLITE_MODEM_STATE_OFF, + SATELLITE_MODEM_STATE_UNAVAILABLE, SATELLITE_MODEM_STATE_UNKNOWN }) @Retention(RetentionPolicy.SOURCE) diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl index 5dc1a6529151..d93ee217c2fb 100644 --- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl +++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl @@ -31,7 +31,6 @@ oneway interface ISatellite { * Register the callback interface with satellite service. * * @param listener The callback interface to handle satellite service indications. - * @param errorCallback The callback to receive the error code result of the operation. * * Valid error codes returned: * SatelliteError:ERROR_NONE @@ -43,7 +42,7 @@ oneway interface ISatellite { * SatelliteError:REQUEST_NOT_SUPPORTED * SatelliteError:NO_RESOURCES */ - void setSatelliteListener(in ISatelliteListener listener, in IIntegerConsumer errorCallback); + void setSatelliteListener(in ISatelliteListener listener); /** * Request to enable or disable the satellite service listening mode. @@ -51,6 +50,8 @@ oneway interface ISatellite { * * @param enable True to enable satellite listening mode and false to disable. * @param isDemoMode Whether demo mode is enabled. + * @param timeout How long the satellite modem should wait for the next incoming page before + * disabling listening mode. * @param errorCallback The callback to receive the error code result of the operation. * * Valid error codes returned: @@ -63,7 +64,7 @@ oneway interface ISatellite { * SatelliteError:REQUEST_NOT_SUPPORTED * SatelliteError:NO_RESOURCES */ - void requestSatelliteListeningEnabled(in boolean enable, in boolean isDemoMode, + void requestSatelliteListeningEnabled(in boolean enable, in boolean isDemoMode, in int timeout, in IIntegerConsumer errorCallback); /** diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl index e24e892e5b55..d9668687e6e6 100644 --- a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl +++ b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl @@ -36,10 +36,10 @@ oneway interface ISatelliteListener { /** * Indicates that new datagrams have been received on the device. * - * @param datagrams Array of new datagrams received. - * @param pendingCount The number of datagrams that are pending. + * @param datagram New datagram that was received. + * @param pendingCount Number of additional datagrams yet to be received. */ - void onSatelliteDatagramsReceived(in SatelliteDatagram[] datagrams, in int pendingCount); + void onSatelliteDatagramReceived(in SatelliteDatagram datagram, in int pendingCount); /** * Indicates that the satellite has pending datagrams for the device to be pulled. diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java index df5143243812..711dcbe3f62b 100644 --- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java +++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java @@ -63,19 +63,19 @@ public class SatelliteImplBase extends SatelliteService { private final IBinder mBinder = new ISatellite.Stub() { @Override - public void setSatelliteListener(ISatelliteListener listener, - IIntegerConsumer errorCallback) throws RemoteException { + public void setSatelliteListener(ISatelliteListener listener) throws RemoteException { executeMethodAsync( - () -> SatelliteImplBase.this.setSatelliteListener(listener, errorCallback), + () -> SatelliteImplBase.this.setSatelliteListener(listener), "setSatelliteListener"); } @Override public void requestSatelliteListeningEnabled(boolean enable, boolean isDemoMode, - IIntegerConsumer errorCallback) throws RemoteException { + int timeout, IIntegerConsumer errorCallback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .requestSatelliteListeningEnabled(enable, isDemoMode, errorCallback), + .requestSatelliteListeningEnabled( + enable, isDemoMode, timeout, errorCallback), "requestSatelliteListeningEnabled"); } @@ -229,7 +229,6 @@ public class SatelliteImplBase extends SatelliteService { * Register the callback interface with satellite service. * * @param listener The callback interface to handle satellite service indications. - * @param errorCallback The callback to receive the error code result of the operation. * * Valid error codes returned: * SatelliteError:ERROR_NONE @@ -241,8 +240,7 @@ public class SatelliteImplBase extends SatelliteService { * SatelliteError:REQUEST_NOT_SUPPORTED * SatelliteError:NO_RESOURCES */ - public void setSatelliteListener(@NonNull ISatelliteListener listener, - @NonNull IIntegerConsumer errorCallback) { + public void setSatelliteListener(@NonNull ISatelliteListener listener) { // stub implementation } @@ -252,6 +250,8 @@ public class SatelliteImplBase extends SatelliteService { * * @param enable True to enable satellite listening mode and false to disable. * @param isDemoMode Whether demo mode is enabled. + * @param timeout How long the satellite modem should wait for the next incoming page before + * disabling listening mode. * @param errorCallback The callback to receive the error code result of the operation. * * Valid error codes returned: @@ -264,7 +264,7 @@ public class SatelliteImplBase extends SatelliteService { * SatelliteError:REQUEST_NOT_SUPPORTED * SatelliteError:NO_RESOURCES */ - public void requestSatelliteListeningEnabled(boolean enable, boolean isDemoMode, + public void requestSatelliteListeningEnabled(boolean enable, boolean isDemoMode, int timeout, @NonNull IIntegerConsumer errorCallback) { // stub implementation } diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl index 5ee7f9abdcf1..e4f94134caa1 100644 --- a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl +++ b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl @@ -42,6 +42,10 @@ enum SatelliteModemState { */ SATELLITE_MODEM_STATE_OFF = 4, /** + * Satellite modem is unavailable. + */ + SATELLITE_MODEM_STATE_UNAVAILABLE = 5, + /** * Satellite modem state is unknown. This generic modem state should be used only when the * modem state cannot be mapped to other specific modem states. */ diff --git a/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl b/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl index aca83f4edbd7..6777256d171e 100644 --- a/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl +++ b/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl @@ -22,7 +22,7 @@ import com.android.internal.telephony.IWwanSelectorCallback; interface ITransportSelectorCallback { oneway void onCreated(in IDomainSelector selector); - oneway void onWlanSelected(); + oneway void onWlanSelected(boolean useEmergencyPdn); IWwanSelectorCallback onWwanSelected(); oneway void onWwanSelectedAsync(in ITransportSelectorResultCallback cb); oneway void onSelectionTerminated(int cause); diff --git a/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl b/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl index 339fbee91812..94e7c871066e 100644 --- a/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl +++ b/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl @@ -21,6 +21,6 @@ import com.android.internal.telephony.IWwanSelectorResultCallback; oneway interface IWwanSelectorCallback { void onRequestEmergencyNetworkScan(in int[] preferredNetworks, int scanType, in IWwanSelectorResultCallback cb); - void onDomainSelected(int domain); + void onDomainSelected(int domain, boolean useEmergencyPdn); void onCancel(); } diff --git a/tests/EnforcePermission/OWNERS b/tests/EnforcePermission/OWNERS new file mode 100644 index 000000000000..39550a394f33 --- /dev/null +++ b/tests/EnforcePermission/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 315013 +tweek@google.com +brufino@google.com diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java index c7e5a5ea3311..e59071bd1d88 100644 --- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java +++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java @@ -29,6 +29,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.os.Bundle; import android.os.Debug.MemoryInfo; +import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; import android.test.InstrumentationTestCase; @@ -40,6 +41,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; /** * This test is intended to measure the amount of memory applications use when @@ -313,17 +315,19 @@ public class MemoryUsageTest extends InstrumentationTestCase { public void run() { try { - String mimeType = mLaunchIntent.getType(); - if (mimeType == null && mLaunchIntent.getData() != null + AtomicReference<String> mimeType = new AtomicReference<>(mLaunchIntent.getType()); + if (mimeType.get() == null && mLaunchIntent.getData() != null && "content".equals(mLaunchIntent.getData().getScheme())) { - mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(), - UserHandle.USER_CURRENT); + mAm.getMimeTypeFilterAsync(mLaunchIntent.getData(), UserHandle.USER_CURRENT, + new RemoteCallback(result -> { + mimeType.set(result.getPairValue()); + })); } mAtm.startActivityAndWait(null, getInstrumentation().getContext().getBasePackageName(), getInstrumentation().getContext().getAttributionTag(), mLaunchIntent, - mimeType, null, null, 0, mLaunchIntent.getFlags(), null, null, + mimeType.get(), null, null, 0, mLaunchIntent.getFlags(), null, null, UserHandle.USER_CURRENT_OR_SELF); } catch (RemoteException e) { Log.w(TAG, "Error launching app", e); diff --git a/tests/MidiTests/Android.bp b/tests/MidiTests/Android.bp new file mode 100644 index 000000000000..254770d21818 --- /dev/null +++ b/tests/MidiTests/Android.bp @@ -0,0 +1,40 @@ +// +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "MidiTests", + srcs: ["**/*.java"], + static_libs: [ + "androidx.test.rules", + "mockito-target-inline-minus-junit4", + "platform-test-annotations", + "services.midi", + "truth-prebuilt", + ], + jni_libs: ["libdexmakerjvmtiagent"], + certificate: "platform", + platform_apis: true, + test_suites: ["device-tests"], +} diff --git a/tests/MidiTests/AndroidManifest.xml b/tests/MidiTests/AndroidManifest.xml new file mode 100644 index 000000000000..0ee1b4493764 --- /dev/null +++ b/tests/MidiTests/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.midi" > + + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> + <uses-permission android:name="android.permission.MANAGE_USERS" /> + + <application android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.midi" + android:label="MidiTests"/> +</manifest> diff --git a/tests/MidiTests/AndroidTest.xml b/tests/MidiTests/AndroidTest.xml new file mode 100644 index 000000000000..9320f0aac090 --- /dev/null +++ b/tests/MidiTests/AndroidTest.xml @@ -0,0 +1,30 @@ +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs sample instrumentation test."> + <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="MidiTests.apk"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/> + <option name="test-suite-tag" value="apct"/> + <option name="test-tag" value="MidiTests"/> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.server.midi"/> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/tests/MidiTests/OWNERS b/tests/MidiTests/OWNERS new file mode 100644 index 000000000000..af273a6f50e0 --- /dev/null +++ b/tests/MidiTests/OWNERS @@ -0,0 +1 @@ +include /services/midi/OWNERS diff --git a/tests/MidiTests/TEST_MAPPING b/tests/MidiTests/TEST_MAPPING new file mode 100644 index 000000000000..60416a8ab3f9 --- /dev/null +++ b/tests/MidiTests/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "MidiTests" + } + ] +} diff --git a/tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java b/tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java new file mode 100644 index 000000000000..1659cc07f021 --- /dev/null +++ b/tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.midi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.midi.MidiEventMultiScheduler; +import com.android.internal.midi.MidiEventScheduler; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Random; + +/** + * Unit tests for com.android.internal.midi.MidiEventMultiScheduler. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class MidiEventMultiSchedulerTest { + private byte[] generateRandomByteStream(Random rnd, int size) { + byte[] output = new byte[size]; + rnd.nextBytes(output); + return output; + } + + private void compareByteArrays(byte[] expectedArray, byte[] outputArray) { + assertEquals(expectedArray.length, outputArray.length); + for (int i = 0; i < outputArray.length; i++) { + assertEquals(expectedArray[i], outputArray[i]); + } + } + + private long timeFromNow(long milliseconds) { + return System.nanoTime() + 1000000L * milliseconds; + } + + @Test + public void testMultiScheduler() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3); + assertEquals(3, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1); + MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2); + + scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0, (byte) 0xf7}, + 0, 2, timeFromNow(100))); + scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1, (byte) 0xf2}, + 0, 2, timeFromNow(200))); + scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf3, (byte) 0xf4}, + 0, 2, timeFromNow(300))); + scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf5, (byte) 0xf6}, + 0, 2, timeFromNow(400))); + assertTrue(multiScheduler.waitNextEvent()); + assertNotNull(scheduler0.getNextEvent(System.nanoTime())); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + assertTrue(multiScheduler.waitNextEvent()); + assertNull(scheduler0.getNextEvent(System.nanoTime())); + assertNotNull(scheduler1.getNextEvent(System.nanoTime())); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + assertTrue(multiScheduler.waitNextEvent()); + assertNull(scheduler0.getNextEvent(System.nanoTime())); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertNotNull(scheduler2.getNextEvent(System.nanoTime())); + assertTrue(multiScheduler.waitNextEvent()); + assertNotNull(scheduler0.getNextEvent(System.nanoTime())); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + } catch (InterruptedException ex) { + + } + } + + @Test + public void testSchedulerLargeData() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1); + assertEquals(1, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + + Random rnd = new Random(42); + + final int arraySize = 1000; + byte[] expectedArray = generateRandomByteStream(rnd, arraySize); + + scheduler0.add(scheduler0.createScheduledEvent(expectedArray, 0, arraySize, + timeFromNow(100))); + assertTrue(multiScheduler.waitNextEvent()); + MidiEventScheduler.MidiEvent event = + (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + compareByteArrays(expectedArray, event.data); + } catch (InterruptedException ex) { + + } + } + + @Test + public void testSchedulerClose() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1); + assertEquals(1, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + scheduler0.close(); + // After all schedulers are closed, waitNextEvent() should return false. + assertFalse(multiScheduler.waitNextEvent()); + } catch (InterruptedException ex) { + + } + } + + @Test + public void testSchedulerMultiClose() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3); + assertEquals(3, multiScheduler.getNumEventSchedulers()); + multiScheduler.close(); + // After all schedulers are closed, waitNextEvent() should return false. + assertFalse(multiScheduler.waitNextEvent()); + } catch (InterruptedException ex) { + + } + } + + @Test + public void testSchedulerNoPreemptiveClose() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3); + assertEquals(3, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1); + MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2); + scheduler0.close(); + scheduler1.close(); + scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf5, (byte) 0xf6}, + 0, 2, timeFromNow(100))); + assertTrue(multiScheduler.waitNextEvent()); + scheduler2.close(); + // After all schedulers are closed, waitNextEvent() should return false. + assertFalse(multiScheduler.waitNextEvent()); + } catch (InterruptedException ex) { + + } + } + + @Test + public void testSchedulerSpamEvents() { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1); + assertEquals(1, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + // Create a msg with size 1 + byte[] msg = new byte[1]; + for (int i = 0; i < 1000; i++) { + msg[0] = (byte) i; + scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0))); + MidiEventScheduler.MidiEvent event = + (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals(msg[0], event.data[0]); + } + assertNull(scheduler0.getNextEvent(System.nanoTime())); + } + + @Test + public void testSchedulerSpamEventsPullLater() { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1); + assertEquals(1, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + // Create a msg with size 1 + byte[] msg = new byte[1]; + for (int i = 0; i < 1000; i++) { + msg[0] = (byte) i; + scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0))); + } + + for (int i = 0; i < 1000; i++) { + MidiEventScheduler.MidiEvent event = + (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) i, event.data[0]); + } + assertNull(scheduler0.getNextEvent(System.nanoTime())); + } + + @Test + public void testSchedulerSpamEventsCallbackLater() { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1); + assertEquals(1, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + // Create a msg with size 1 + byte[] msg = new byte[1]; + for (int i = 0; i < 1000; i++) { + msg[0] = (byte) i; + scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0))); + } + + for (int i = 0; i < 1000; i++) { + try { + assertTrue(multiScheduler.waitNextEvent()); + } catch (InterruptedException ex) { + } + MidiEventScheduler.MidiEvent event = + (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) i, event.data[0]); + } + assertNull(scheduler0.getNextEvent(System.nanoTime())); + } + + @Test + public void testMultiSchedulerOutOfOrder() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3); + assertEquals(3, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1); + MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2); + + scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf3}, + 0, 1, + timeFromNow(400))); + scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf2}, + 0, 1, + timeFromNow(300))); + scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1}, + 0, 1, + timeFromNow(200))); + scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0}, + 0, 1, + timeFromNow(100))); + + assertTrue(multiScheduler.waitNextEvent()); + MidiEventScheduler.MidiEvent event = + (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf0, event.data[0]); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + assertTrue(multiScheduler.waitNextEvent()); + assertNull(scheduler0.getNextEvent(System.nanoTime())); + event = (MidiEventScheduler.MidiEvent) scheduler1.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf1, event.data[0]); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + assertTrue(multiScheduler.waitNextEvent()); + assertNull(scheduler0.getNextEvent(System.nanoTime())); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + event = (MidiEventScheduler.MidiEvent) scheduler2.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf2, event.data[0]); + assertTrue(multiScheduler.waitNextEvent()); + event = (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf3, event.data[0]); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + } catch (InterruptedException ex) { + + } + } + + @Test + public void testMultiSchedulerOutOfOrderNegativeTime() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3); + assertEquals(3, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1); + MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2); + + scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf3}, + 0, 1, + timeFromNow(-100))); + scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf2}, + 0, 1, + timeFromNow(-200))); + scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1}, + 0, 1, + timeFromNow(-300))); + scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0}, + 0, 1, + timeFromNow(-400))); + + assertTrue(multiScheduler.waitNextEvent()); + MidiEventScheduler.MidiEvent event = + (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf0, event.data[0]); + assertTrue(multiScheduler.waitNextEvent()); + event = (MidiEventScheduler.MidiEvent) scheduler1.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf1, event.data[0]); + assertTrue(multiScheduler.waitNextEvent()); + event = (MidiEventScheduler.MidiEvent) scheduler2.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf2, event.data[0]); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertTrue(multiScheduler.waitNextEvent()); + event = (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf3, event.data[0]); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + } catch (InterruptedException ex) { + + } + } +} diff --git a/tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java b/tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java new file mode 100644 index 000000000000..ad701e5117fc --- /dev/null +++ b/tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.usb; + +import static org.junit.Assert.assertEquals; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; +import java.util.Random; + +/** + * Unit tests for com.android.server.usb.UsbMidiPacketConverter. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class UsbMidiPacketConverterTest { + private byte[] generateRandomByteStream(Random rnd, int size) { + byte[] output = new byte[size]; + rnd.nextBytes(output); + return output; + } + + private void compareByteArrays(byte[] expectedArray, byte[] outputArray) { + assertEquals(expectedArray.length, outputArray.length); + for (int i = 0; i < outputArray.length; i++) { + assertEquals(expectedArray[i], outputArray[i]); + } + } + + @Test + public void testDecoderSinglePacket() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createDecoders(2); + byte[] input = new byte[] {0x19 /* Cable 1 Note-On */, (byte) 0x91, 0x33, 0x66}; + byte[] expectedOutputCable0 = new byte[] {}; + byte[] expectedOutputCable1 = new byte[] {(byte) 0x91, 0x33, 0x66}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + compareByteArrays(expectedOutputCable1, actualOutputCable1); + } + + @Test + public void testDecoderMultiplePackets() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createDecoders(4); + byte[] input = new byte[] { + 0x1B /* Cable 1 Control Change */, (byte) 0xB4, 0x55, 0x6E, + 0x35 /* Cable 3 Single byte SysEx */, (byte) 0xF8, 0x00, 0x00, + 0x02 /* Cable 0 Two byte System Common */, (byte) 0xF3, 0x12, 0x00}; + byte[] expectedOutputCable0 = new byte[] {(byte) 0xF3, 0x12}; + byte[] expectedOutputCable1 = new byte[] {(byte) 0xB4, 0x55, 0x6E}; + byte[] expectedOutputCable2 = new byte[] {}; + byte[] expectedOutputCable3 = new byte[] {(byte) 0xF8}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1); + byte[] actualOutputCable2 = usbMidiPacketConverter.pullDecodedMidiPackets(2); + byte[] actualOutputCable3 = usbMidiPacketConverter.pullDecodedMidiPackets(3); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + compareByteArrays(expectedOutputCable1, actualOutputCable1); + compareByteArrays(expectedOutputCable2, actualOutputCable2); + compareByteArrays(expectedOutputCable3, actualOutputCable3); + } + + @Test + public void testDecoderSysExEndFirstByte() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createDecoders(2); + byte[] input = new byte[] { + 0x14 /* Cable 1 SysEx Start */, (byte) 0xF0, 0x00, 0x01, + 0x15 /* Cable 1 Single byte SysEx End */, (byte) 0xF7, 0x00, 0x00}; + byte[] expectedOutputCable0 = new byte[] {}; + byte[] expectedOutputCable1 = new byte[] { + (byte) 0xF0, 0x00, 0x01, + (byte) 0xF7}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + compareByteArrays(expectedOutputCable1, actualOutputCable1); + } + + @Test + public void testDecoderSysExEndSecondByte() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createDecoders(1); + byte[] input = new byte[] { + 0x04 /* Cable 0 SysEx Start */, (byte) 0xF0, 0x00, 0x01, + 0x06 /* Cable 0 Two byte SysEx End */, 0x02, (byte) 0xF7, 0x00}; + byte[] expectedOutputCable0 = new byte[] { + (byte) 0xF0, 0x00, 0x01, + 0x02, (byte) 0xF7}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + } + + @Test + public void testDecoderSysExEndThirdByte() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + byte[] input = new byte[] { + 0x04 /* Cable 0 SysEx Start */, (byte) 0xF0, 0x00, 0x01, + 0x07 /* Cable 0 Three byte SysEx End */, 0x02, 0x03, (byte) 0xF7}; + usbMidiPacketConverter.createDecoders(1); + byte[] expectedOutputCable0 = new byte[] { + (byte) 0xF0, 0x00, 0x01, + 0x02, 0x03, (byte) 0xF7}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + } + + @Test + public void testDecoderSysExStartEnd() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + byte[] input = new byte[] { + 0x06 /* Cable 0 Two byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00}; + usbMidiPacketConverter.createDecoders(1); + byte[] expectedOutputCable0 = new byte[] { + (byte) 0xF0, (byte) 0xF7}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + } + + @Test + public void testDecoderSysExStartByteEnd() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + byte[] input = new byte[] { + 0x07 /* Cable 0 Three byte SysEx End */, (byte) 0xF0, 0x44, (byte) 0xF7}; + usbMidiPacketConverter.createDecoders(1); + byte[] expectedOutputCable0 = new byte[] { + (byte) 0xF0, 0x44, (byte) 0xF7}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + } + + @Test + public void testDecoderDefaultToFirstCable() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + byte[] input = new byte[] {0x49 /* Cable 4 Note-On */, (byte) 0x91, 0x22, 0x33}; + usbMidiPacketConverter.createDecoders(1); + byte[] expectedOutputCable0 = new byte[] { + (byte) 0x91, 0x22, 0x33}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + } + + @Test + public void testDecoderLargePacketDoesNotCrash() { + for (long seed = 1001; seed < 5000; seed += 777) { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createDecoders(3); + Random rnd = new Random(seed); + byte[] input = generateRandomByteStream(rnd, 1003 /* arbitrary large size */); + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + usbMidiPacketConverter.pullDecodedMidiPackets(0); + usbMidiPacketConverter.pullDecodedMidiPackets(1); + usbMidiPacketConverter.pullDecodedMidiPackets(2); + } + } + + @Test + public void testEncoderBasic() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + byte[] input = new byte[] {(byte) 0x91 /* Note-On */, 0x33, 0x66}; + byte[] expectedOutput = new byte[] { + 0x09 /* Cable 0 Note-On */, (byte) 0x91, 0x33, 0x66}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderMultiplePackets() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(3); + byte[] inputCable2 = new byte[] { + (byte) 0xB4 /* Control Change */, 0x55, 0x6E}; + byte[] inputCable1 = new byte[] { + (byte) 0xF8 /* Timing Clock (Single Byte) */, + (byte) 0xF3 /* Song Select (Two Bytes) */, 0x12}; + byte[] expectedOutput = new byte[] { + 0x2B /* Cable 2 Control Change */, (byte) 0xB4, 0x55, 0x6E, + 0x15 /* Cable 1 Timing Clock */, (byte) 0xF8, 0x00, 0x00, + 0x12 /* Cable 1 Two Byte System Common */, (byte) 0xF3, 0x12, 0x00}; + usbMidiPacketConverter.encodeMidiPackets(inputCable2, inputCable2.length, 2); + usbMidiPacketConverter.encodeMidiPackets(inputCable1, inputCable1.length, 1); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderWeavePackets() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(2); + byte[] inputCable1Msg1 = new byte[] { + (byte) 0x93 /* Note-On */, 0x23, 0x43}; + byte[] inputCable0Msg = new byte[] { + (byte) 0xB4 /* Control Change */, 0x65, 0x26}; + byte[] inputCable1Msg2 = new byte[] { + (byte) 0xA4 /* Poly-KeyPress */, 0x52, 0x76}; + byte[] expectedOutput = new byte[] { + 0x19 /* Cable 1 Note-On */, (byte) 0x93, 0x23, 0x43, + 0x0B /* Cable 0 Control Change */, (byte) 0xB4, 0x65, 0x26, + 0x1A /* Cable 1 Poly-KeyPress */, (byte) 0xA4, 0x52, 0x76}; + usbMidiPacketConverter.encodeMidiPackets(inputCable1Msg1, inputCable1Msg1.length, 1); + usbMidiPacketConverter.encodeMidiPackets(inputCable0Msg, inputCable0Msg.length, 0); + usbMidiPacketConverter.encodeMidiPackets(inputCable1Msg2, inputCable1Msg2.length, 1); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderSysExEndFirstByte() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + byte[] input = new byte[] { + (byte) 0xF0 /* SysEx Start */, 0x00, 0x01, + (byte) 0xF7 /* SysEx End */}; + byte[] expectedOutput = new byte[] { + 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01, + 0x05 /* Cable 0 One Byte SysEx End */, (byte) 0xF7, 0x00, 0x00}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderSysExEndSecondByte() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + byte[] input = new byte[] { + (byte) 0xF0 /* SysEx Start */, 0x00, 0x01, + 0x02, (byte) 0xF7 /* SysEx End */}; + byte[] expectedOutput = new byte[] { + 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01, + 0x06 /* Cable 0 Two Byte SysEx End */, 0x02, (byte) 0xF7, 0x00}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderSysExEndThirdByte() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + byte[] input = new byte[] { + (byte) 0xF0 /* SysEx Start */, 0x00, 0x01, + 0x02, 0x03, (byte) 0xF7 /* SysEx End */}; + byte[] expectedOutput = new byte[] { + 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01, + 0x07 /* Cable 0 Three Byte SysEx End */, 0x02, 0x03, (byte) 0xF7}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderSysExStartEnd() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + byte[] input = new byte[] { + (byte) 0xF0 /* SysEx Start */, (byte) 0xF7 /* SysEx End */}; + byte[] expectedOutput = new byte[] { + 0x06 /* Cable 0 Two Byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderSysExStartByteEnd() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + byte[] input = new byte[] { + (byte) 0xF0 /* SysEx Start */, 0x44, (byte) 0xF7 /* SysEx End */}; + byte[] expectedOutput = new byte[] { + 0x07 /* Cable 0 Three Byte SysEx End */, (byte) 0xF0, 0x44, (byte) 0xF7}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderMultiplePulls() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + + byte[] input = new byte[] { + (byte) 0xF0 /* SysEx Start */, 0x44, 0x55, + 0x66, 0x77}; // 0x66 and 0x77 will not be pulled the first time + byte[] expectedOutput = new byte[] { + 0x04 /* SysEx Start */, (byte) 0xF0, 0x44, 0x55}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + + input = new byte[] { + 0x11, // Combined with 0x66 and 0x77 above + 0x22, (byte) 0xF7 /* SysEx End */}; + expectedOutput = new byte[] { + 0x04 /* Cable 0 SysEx Continue */, 0x66, 0x77, 0x11, + 0x06 /* Cable 0 Two Byte SysEx End */, 0x22, (byte) 0xF7, 0x00}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + + input = new byte[] { + (byte) 0xF0 /* SysEx Start */, (byte) 0xF7 /* SysEx End */}; + expectedOutput = new byte[] { + 0x06 /* Cable 0 Two Byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderDefaultToFirstCable() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(2); + byte[] input = new byte[] {(byte) 0x91 /* Note-On */, 0x22, 0x33}; + byte[] expectedOutput = new byte[] { + 0x09 /* Cable 0 Note-On */, (byte) 0x91, 0x22, 0x33}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 4); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderLargePacketDoesNotCrash() { + for (long seed = 234; seed < 4000; seed += 666) { + Random rnd = new Random(seed); + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(4); + for (int cableNumber = 0; cableNumber < 4; cableNumber++) { + byte[] input = generateRandomByteStream(rnd, 1003 /* arbitrary large size */); + usbMidiPacketConverter.encodeMidiPackets(input, input.length, cableNumber); + } + usbMidiPacketConverter.pullEncodedMidiPackets(); + } + } + + @Test + public void testEncodeDecode() { + final int bufferSize = 30; + final int numCables = 16; + final int bytesToEncodePerEncoding = 10; + byte[][] rawMidi = new byte[numCables][bufferSize]; + for (long seed = 45; seed < 3000; seed += 300) { + Random rnd = new Random(seed); + for (int cableNumber = 0; cableNumber < numCables; cableNumber++) { + rawMidi[cableNumber] = generateRandomByteStream(rnd, bufferSize); + + // Change the last byte to SysEx End. + // This way the encoder is guaranteed to flush all packets. + rawMidi[cableNumber][bufferSize - 1] = (byte) 0xF7; + } + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(numCables); + // Encode packets and interweave them + for (int startByte = 0; startByte < bufferSize; + startByte += bytesToEncodePerEncoding) { + for (int cableNumber = 0; cableNumber < numCables; cableNumber++) { + byte[] bytesToEncode = Arrays.copyOfRange(rawMidi[cableNumber], startByte, + startByte + bytesToEncodePerEncoding); + usbMidiPacketConverter.encodeMidiPackets(bytesToEncode, bytesToEncode.length, + cableNumber); + } + } + byte[] usbMidi = usbMidiPacketConverter.pullEncodedMidiPackets(); + + usbMidiPacketConverter.createDecoders(numCables); + + // Now decode the MIDI packets to check if they are the same as the original + usbMidiPacketConverter.decodeMidiPackets(usbMidi, usbMidi.length); + for (int cableNumber = 0; cableNumber < numCables; cableNumber++) { + byte[] decodedRawMidi = usbMidiPacketConverter.pullDecodedMidiPackets(cableNumber); + compareByteArrays(rawMidi[cableNumber], decodedRawMidi); + } + } + } +} diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java index 161c83ce2fab..fe23caae7f03 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java @@ -20,15 +20,17 @@ import static android.net.wifi.WifiAnnotations.SecurityType; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import android.util.ArraySet; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; import java.util.Objects; +import java.util.Set; /** * A data class representing a known Wi-Fi network. @@ -37,21 +39,28 @@ import java.util.Objects; */ @SystemApi public final class KnownNetwork implements Parcelable { + + /** + * Network source is unknown. + */ + public static final int NETWORK_SOURCE_UNKNOWN = 0; + /** * Network is known by a nearby device with the same user account. */ - public static final int NETWORK_SOURCE_NEARBY_SELF = 0; + public static final int NETWORK_SOURCE_NEARBY_SELF = 1; /** * Network is known via cloud storage associated with this device's user account. */ - public static final int NETWORK_SOURCE_CLOUD_SELF = 1; + public static final int NETWORK_SOURCE_CLOUD_SELF = 2; /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({ + NETWORK_SOURCE_UNKNOWN, NETWORK_SOURCE_NEARBY_SELF, NETWORK_SOURCE_CLOUD_SELF }) @@ -59,7 +68,7 @@ public final class KnownNetwork implements Parcelable { @NetworkSource private final int mNetworkSource; private final String mSsid; - @SecurityType private final int[] mSecurityTypes; + @SecurityType private final ArraySet<Integer> mSecurityTypes; private final DeviceInfo mDeviceInfo; /** @@ -68,11 +77,9 @@ public final class KnownNetwork implements Parcelable { public static final class Builder { @NetworkSource private int mNetworkSource = -1; private String mSsid; - @SecurityType private int[] mSecurityTypes; + @SecurityType private final ArraySet<Integer> mSecurityTypes = new ArraySet<>(); private android.net.wifi.sharedconnectivity.app.DeviceInfo mDeviceInfo; - public Builder() {} - /** * Sets the indicated source of the known network. * @@ -98,25 +105,26 @@ public final class KnownNetwork implements Parcelable { } /** - * Sets the security types of the known network. + * Adds a security type of the known network. * - * @param securityTypes The array of security types supported by the known network. + * @param securityType A security type supported by the known network. * @return Returns the Builder object. */ @NonNull - public Builder setSecurityTypes(@NonNull @SecurityType int[] securityTypes) { - mSecurityTypes = securityTypes; + public Builder addSecurityType(@SecurityType int securityType) { + mSecurityTypes.add(securityType); return this; } /** * Sets the device information of the device providing connectivity. + * Must be set if network source is {@link KnownNetwork#NETWORK_SOURCE_NEARBY_SELF}. * * @param deviceInfo The device information object. * @return Returns the Builder object. */ @NonNull - public Builder setDeviceInfo(@NonNull DeviceInfo deviceInfo) { + public Builder setDeviceInfo(@Nullable DeviceInfo deviceInfo) { mDeviceInfo = deviceInfo; return this; } @@ -136,28 +144,34 @@ public final class KnownNetwork implements Parcelable { } } - private static void validate(int networkSource, String ssid, int [] securityTypes) { - if (networkSource != NETWORK_SOURCE_CLOUD_SELF && networkSource - != NETWORK_SOURCE_NEARBY_SELF) { + private static void validate(int networkSource, String ssid, Set<Integer> securityTypes, + DeviceInfo deviceInfo) { + if (networkSource != NETWORK_SOURCE_UNKNOWN + && networkSource != NETWORK_SOURCE_CLOUD_SELF + && networkSource != NETWORK_SOURCE_NEARBY_SELF) { throw new IllegalArgumentException("Illegal network source"); } if (TextUtils.isEmpty(ssid)) { throw new IllegalArgumentException("SSID must be set"); } - if (securityTypes == null || securityTypes.length == 0) { + if (securityTypes.isEmpty()) { throw new IllegalArgumentException("SecurityTypes must be set"); } + if (networkSource == NETWORK_SOURCE_NEARBY_SELF && deviceInfo == null) { + throw new IllegalArgumentException("Device info must be provided when network source" + + " is NETWORK_SOURCE_NEARBY_SELF"); + } } private KnownNetwork( @NetworkSource int networkSource, @NonNull String ssid, - @NonNull @SecurityType int[] securityTypes, - @NonNull DeviceInfo deviceInfo) { - validate(networkSource, ssid, securityTypes); + @NonNull @SecurityType ArraySet<Integer> securityTypes, + @Nullable DeviceInfo deviceInfo) { + validate(networkSource, ssid, securityTypes, deviceInfo); mNetworkSource = networkSource; mSsid = ssid; - mSecurityTypes = securityTypes; + mSecurityTypes = new ArraySet<>(securityTypes); mDeviceInfo = deviceInfo; } @@ -184,20 +198,21 @@ public final class KnownNetwork implements Parcelable { /** * Gets the security types of the known network. * - * @return Returns the array of security types supported by the known network. + * @return Returns a set with security types supported by the known network. */ @NonNull @SecurityType - public int[] getSecurityTypes() { + public Set<Integer> getSecurityTypes() { return mSecurityTypes; } /** * Gets the device information of the device providing connectivity. * - * @return Returns the information of the device providing the known network. + * @return Returns the information of the device providing the known network. Can be null if the + * network source is cloud or unknown. */ - @NonNull + @Nullable public DeviceInfo getDeviceInfo() { return mDeviceInfo; } @@ -208,14 +223,13 @@ public final class KnownNetwork implements Parcelable { KnownNetwork other = (KnownNetwork) obj; return mNetworkSource == other.getNetworkSource() && Objects.equals(mSsid, other.getSsid()) - && Arrays.equals(mSecurityTypes, other.getSecurityTypes()) + && Objects.equals(mSecurityTypes, other.getSecurityTypes()) && Objects.equals(mDeviceInfo, other.getDeviceInfo()); } @Override public int hashCode() { - return Objects.hash(mNetworkSource, mSsid, Arrays.hashCode(mSecurityTypes), - mDeviceInfo.hashCode()); + return Objects.hash(mNetworkSource, mSsid, mSecurityTypes, mDeviceInfo); } @Override @@ -227,7 +241,7 @@ public final class KnownNetwork implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mNetworkSource); dest.writeString(mSsid); - dest.writeIntArray(mSecurityTypes); + dest.writeArraySet(mSecurityTypes); mDeviceInfo.writeToParcel(dest, flags); } @@ -238,7 +252,8 @@ public final class KnownNetwork implements Parcelable { */ @NonNull public static KnownNetwork readFromParcel(@NonNull Parcel in) { - return new KnownNetwork(in.readInt(), in.readString(), in.createIntArray(), + return new KnownNetwork(in.readInt(), in.readString(), + (ArraySet<Integer>) in.readArraySet(null), DeviceInfo.readFromParcel(in)); } @@ -260,7 +275,7 @@ public final class KnownNetwork implements Parcelable { return new StringBuilder("KnownNetwork[") .append("NetworkSource=").append(mNetworkSource) .append(", ssid=").append(mSsid) - .append(", securityTypes=").append(Arrays.toString(mSecurityTypes)) + .append(", securityTypes=").append(mSecurityTypes.toString()) .append(", deviceInfo=").append(mDeviceInfo.toString()) .append("]").toString(); } diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java index a19510b3f9df..c09f85b39ee4 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java @@ -19,6 +19,7 @@ package android.net.wifi.sharedconnectivity.app; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -151,7 +152,7 @@ public class SharedConnectivityManager { * Creates a new instance of {@link SharedConnectivityManager}. * * Automatically binds to implementation of {@link SharedConnectivityService} specified in - * device overlay. + * the device overlay. * * @return An instance of {@link SharedConnectivityManager} or null if the shared connectivity * service is not found. @@ -266,9 +267,10 @@ public class SharedConnectivityManager { * * @param executor The Executor used to invoke the callback. * @param callback The callback of type {@link SharedConnectivityClientCallback} that is invoked - * when the service updates either the list of Tether Networks or Known - * Networks. + * when the service updates its data. */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void registerCallback(@NonNull @CallbackExecutor Executor executor, @NonNull SharedConnectivityClientCallback callback) { Objects.requireNonNull(executor, "executor cannot be null"); @@ -297,6 +299,8 @@ public class SharedConnectivityManager { * * @return Returns true if the callback was successfully unregistered, false otherwise. */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean unregisterCallback( @NonNull SharedConnectivityClientCallback callback) { Objects.requireNonNull(callback, "callback cannot be null"); @@ -325,7 +329,7 @@ public class SharedConnectivityManager { return true; } - /** + /** * Send command to the implementation of {@link SharedConnectivityService} requesting connection * to the specified Tether Network. * @@ -334,6 +338,8 @@ public class SharedConnectivityManager { * @return Returns true if the service received the command. Does not guarantee that the * connection was successful. */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean connectTetherNetwork(@NonNull TetherNetwork network) { Objects.requireNonNull(network, "Tether network cannot be null"); @@ -359,6 +365,8 @@ public class SharedConnectivityManager { * @return Returns true if the service received the command. Does not guarantee that the * disconnection was successful. */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean disconnectTetherNetwork(@NonNull TetherNetwork network) { if (mService == null) { return false; @@ -382,6 +390,8 @@ public class SharedConnectivityManager { * @return Returns true if the service received the command. Does not guarantee that the * connection was successful. */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean connectKnownNetwork(@NonNull KnownNetwork network) { Objects.requireNonNull(network, "Known network cannot be null"); @@ -405,6 +415,8 @@ public class SharedConnectivityManager { * @return Returns true if the service received the command. Does not guarantee that the * forget action was successful. */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean forgetKnownNetwork(@NonNull KnownNetwork network) { Objects.requireNonNull(network, "Known network cannot be null"); @@ -426,6 +438,8 @@ public class SharedConnectivityManager { * * @return Returns a {@link List} of {@link TetherNetwork} objects, empty list on failure. */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @NonNull public List<TetherNetwork> getTetherNetworks() { if (mService == null) { @@ -445,6 +459,8 @@ public class SharedConnectivityManager { * * @return Returns a {@link List} of {@link KnownNetwork} objects, empty list on failure. */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @NonNull public List<KnownNetwork> getKnownNetworks() { if (mService == null) { @@ -465,6 +481,8 @@ public class SharedConnectivityManager { * @return Returns a {@link SharedConnectivitySettingsState} object with the state, null on * failure. */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Nullable public SharedConnectivitySettingsState getSettingsState() { if (mService == null) { @@ -486,6 +504,8 @@ public class SharedConnectivityManager { * null on failure. If no connection is active the status will be * {@link TetherNetworkConnectionStatus#CONNECTION_STATUS_UNKNOWN}. */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Nullable public TetherNetworkConnectionStatus getTetherNetworkConnectionStatus() { if (mService == null) { @@ -507,6 +527,8 @@ public class SharedConnectivityManager { * null on failure. If no connection is active the status will be * {@link KnownNetworkConnectionStatus#CONNECTION_STATUS_UNKNOWN}. */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Nullable public KnownNetworkConnectionStatus getKnownNetworkConnectionStatus() { if (mService == null) { diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java index af4fd4a2cc76..7b591d3a45bd 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java @@ -25,12 +25,12 @@ import android.annotation.SystemApi; import android.net.wifi.sharedconnectivity.service.SharedConnectivityService; import android.os.Parcel; import android.os.Parcelable; - +import android.util.ArraySet; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; import java.util.Objects; +import java.util.Set; /** * A data class representing an Instant Tether network. @@ -79,7 +79,7 @@ public final class TetherNetwork implements Parcelable { private final String mNetworkName; @Nullable private final String mHotspotSsid; @Nullable private final String mHotspotBssid; - @Nullable @SecurityType private final int[] mHotspotSecurityTypes; + @Nullable @SecurityType private final ArraySet<Integer> mHotspotSecurityTypes; /** * Builder class for {@link TetherNetwork}. @@ -91,9 +91,8 @@ public final class TetherNetwork implements Parcelable { private String mNetworkName; @Nullable private String mHotspotSsid; @Nullable private String mHotspotBssid; - @Nullable @SecurityType private int[] mHotspotSecurityTypes; - - public Builder() {} + @Nullable @SecurityType private final ArraySet<Integer> mHotspotSecurityTypes = + new ArraySet<>(); /** * Set the remote device ID. @@ -168,15 +167,14 @@ public final class TetherNetwork implements Parcelable { } /** - * Sets the hotspot security types supported by the remote device, or null if hotspot is - * off. + * Adds a security type supported by the hotspot created by the remote device. * - * @param hotspotSecurityTypes The array of security types supported by the hotspot. + * @param hotspotSecurityType A security type supported by the hotspot. * @return Returns the Builder object. */ @NonNull - public Builder setHotspotSecurityTypes(@NonNull @SecurityType int[] hotspotSecurityTypes) { - mHotspotSecurityTypes = hotspotSecurityTypes; + public Builder addHotspotSecurityType(@SecurityType int hotspotSecurityType) { + mHotspotSecurityTypes.add(hotspotSecurityType); return this; } @@ -218,7 +216,7 @@ public final class TetherNetwork implements Parcelable { @NonNull String networkName, @Nullable String hotspotSsid, @Nullable String hotspotBssid, - @Nullable @SecurityType int[] hotspotSecurityTypes) { + @Nullable @SecurityType ArraySet<Integer> hotspotSecurityTypes) { validate(deviceId, networkType, networkName); @@ -228,7 +226,7 @@ public final class TetherNetwork implements Parcelable { mNetworkName = networkName; mHotspotSsid = hotspotSsid; mHotspotBssid = hotspotBssid; - mHotspotSecurityTypes = hotspotSecurityTypes; + mHotspotSecurityTypes = new ArraySet<>(hotspotSecurityTypes); } /** @@ -293,11 +291,11 @@ public final class TetherNetwork implements Parcelable { /** * Gets the hotspot security types supported by the remote device. * - * @return Returns the array of security types supported by the hotspot. + * @return Returns a set of the security types supported by the hotspot. */ - @Nullable + @NonNull @SecurityType - public int[] getHotspotSecurityTypes() { + public Set<Integer> getHotspotSecurityTypes() { return mHotspotSecurityTypes; } @@ -311,13 +309,13 @@ public final class TetherNetwork implements Parcelable { && Objects.equals(mNetworkName, other.getNetworkName()) && Objects.equals(mHotspotSsid, other.getHotspotSsid()) && Objects.equals(mHotspotBssid, other.getHotspotBssid()) - && Arrays.equals(mHotspotSecurityTypes, other.getHotspotSecurityTypes()); + && Objects.equals(mHotspotSecurityTypes, other.getHotspotSecurityTypes()); } @Override public int hashCode() { return Objects.hash(mDeviceId, mDeviceInfo, mNetworkName, mHotspotSsid, mHotspotBssid, - Arrays.hashCode(mHotspotSecurityTypes)); + mHotspotSecurityTypes); } @Override @@ -333,7 +331,7 @@ public final class TetherNetwork implements Parcelable { dest.writeString(mNetworkName); dest.writeString(mHotspotSsid); dest.writeString(mHotspotBssid); - dest.writeIntArray(mHotspotSecurityTypes); + dest.writeArraySet(mHotspotSecurityTypes); } /** @@ -345,7 +343,7 @@ public final class TetherNetwork implements Parcelable { public static TetherNetwork readFromParcel(@NonNull Parcel in) { return new TetherNetwork(in.readLong(), DeviceInfo.readFromParcel(in), in.readInt(), in.readString(), in.readString(), in.readString(), - in.createIntArray()); + (ArraySet<Integer>) in.readArraySet(null)); } @NonNull @@ -370,7 +368,7 @@ public final class TetherNetwork implements Parcelable { .append(", networkName=").append(mNetworkName) .append(", hotspotSsid=").append(mHotspotSsid) .append(", hotspotBssid=").append(mHotspotBssid) - .append(", hotspotSecurityTypes=").append(Arrays.toString(mHotspotSecurityTypes)) + .append(", hotspotSecurityTypes=").append(mHotspotSecurityTypes.toString()) .append("]").toString(); } } diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java index d2cea613d2ec..4c88c1b269a9 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java @@ -82,66 +82,148 @@ public abstract class SharedConnectivityService extends Service { if (DEBUG) Log.i(TAG, "onBind intent=" + intent); mHandler = new Handler(getMainLooper()); IBinder serviceStub = new ISharedConnectivityService.Stub() { + + /** + * Registers a callback for receiving updates to the list of Tether Networks, Known + * Networks, shared connectivity settings state, tether network connection status and + * known network connection status. + * + * @param callback The callback of type {@link ISharedConnectivityCallback} to be called + * when there is update to the data. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Override public void registerCallback(ISharedConnectivityCallback callback) { checkPermissions(); mHandler.post(() -> onRegisterCallback(callback)); } + /** + * Unregisters a previously registered callback. + * + * @param callback The callback to unregister. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Override public void unregisterCallback(ISharedConnectivityCallback callback) { checkPermissions(); mHandler.post(() -> onUnregisterCallback(callback)); } + /** + * Connects to a tether network. + * + * @param network The network to connect to. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Override public void connectTetherNetwork(TetherNetwork network) { checkPermissions(); mHandler.post(() -> onConnectTetherNetwork(network)); } + /** + * Disconnects from a previously connected tether network. + * + * @param network The network to disconnect from. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Override public void disconnectTetherNetwork(TetherNetwork network) { checkPermissions(); mHandler.post(() -> onDisconnectTetherNetwork(network)); } + /** + * Adds a known network to the available networks on the device and connects to it. + * + * @param network The network to connect to. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Override public void connectKnownNetwork(KnownNetwork network) { checkPermissions(); mHandler.post(() -> onConnectKnownNetwork(network)); } + /** + * Removes a known network from the available networks on the device which will also + * disconnect the device from the network if it is connected to it. + * + * @param network The network to forget. + */ @Override public void forgetKnownNetwork(KnownNetwork network) { checkPermissions(); mHandler.post(() -> onForgetKnownNetwork(network)); } + /** + * Gets the list of tether networks the user can select to connect to. + * + * @return Returns a {@link List} of {@link TetherNetwork} objects + */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Override public List<TetherNetwork> getTetherNetworks() { checkPermissions(); return mTetherNetworks; } + /** + * Gets the list of known networks the user can select to connect to. + * + * @return Returns a {@link List} of {@link KnownNetwork} objects. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Override public List<KnownNetwork> getKnownNetworks() { checkPermissions(); return mKnownNetworks; } + /** + * Gets the shared connectivity settings state. + * + * @return Returns a {@link SharedConnectivitySettingsState} object with the state. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Override public SharedConnectivitySettingsState getSettingsState() { checkPermissions(); return mSettingsState; } + /** + * Gets the connection status of the tether network the user selected to connect to. + * + * @return Returns a {@link TetherNetworkConnectionStatus} object with the connection + * status. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Override public TetherNetworkConnectionStatus getTetherNetworkConnectionStatus() { checkPermissions(); return mTetherNetworkConnectionStatus; } + /** + * Gets the connection status of the known network the user selected to connect to. + * + * @return Returns a {@link KnownNetworkConnectionStatus} object with the connection + * status. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) @Override public KnownNetworkConnectionStatus getKnownNetworkConnectionStatus() { checkPermissions(); diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java index f8f07008e34b..e6595eb2e2a3 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java @@ -19,8 +19,7 @@ package android.net.wifi.sharedconnectivity.app; import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_LAPTOP; import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_PHONE; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static com.google.common.truth.Truth.assertThat; import android.os.Parcel; @@ -29,7 +28,7 @@ import androidx.test.filters.SmallTest; import org.junit.Test; /** - * Unit tests for {@link android.app.sharedconnectivity.DeviceInfo}. + * Unit tests for {@link DeviceInfo}. */ @SmallTest public class DeviceInfoTest { @@ -63,8 +62,8 @@ public class DeviceInfoTest { parcelR.setDataPosition(0); DeviceInfo fromParcel = DeviceInfo.CREATOR.createFromParcel(parcelR); - assertEquals(info, fromParcel); - assertEquals(info.hashCode(), fromParcel.hashCode()); + assertThat(fromParcel).isEqualTo(info); + assertThat(fromParcel.hashCode()).isEqualTo(info.hashCode()); } /** @@ -74,24 +73,24 @@ public class DeviceInfoTest { public void testEqualsOperation() { DeviceInfo info1 = buildDeviceInfoBuilder().build(); DeviceInfo info2 = buildDeviceInfoBuilder().build(); - assertEquals(info1, info2); + assertThat(info1).isEqualTo(info2); DeviceInfo.Builder builder = buildDeviceInfoBuilder().setDeviceType(DEVICE_TYPE_1); - assertNotEquals(info1, builder.build()); + assertThat(builder.build()).isNotEqualTo(info1); builder = buildDeviceInfoBuilder().setDeviceName(DEVICE_NAME_1); - assertNotEquals(info1, builder.build()); + assertThat(builder.build()).isNotEqualTo(info1); builder = buildDeviceInfoBuilder().setModelName(DEVICE_MODEL_1); - assertNotEquals(info1, builder.build()); + assertThat(builder.build()).isNotEqualTo(info1); builder = buildDeviceInfoBuilder() .setBatteryPercentage(BATTERY_PERCENTAGE_1); - assertNotEquals(info1, builder.build()); + assertThat(builder.build()).isNotEqualTo(info1); builder = buildDeviceInfoBuilder() .setConnectionStrength(CONNECTION_STRENGTH_1); - assertNotEquals(info1, builder.build()); + assertThat(builder.build()).isNotEqualTo(info1); } /** @@ -100,12 +99,19 @@ public class DeviceInfoTest { @Test public void testGetMethods() { DeviceInfo info = buildDeviceInfoBuilder().build(); - assertEquals(info.getDeviceType(), DEVICE_TYPE); - assertEquals(info.getDeviceName(), DEVICE_NAME); - assertEquals(info.getModelName(), DEVICE_MODEL); - assertEquals(info.getBatteryPercentage(), BATTERY_PERCENTAGE); - assertEquals(info.getConnectionStrength(), CONNECTION_STRENGTH); - assertEquals(info.getConnectionStrength(), CONNECTION_STRENGTH); + assertThat(info.getDeviceType()).isEqualTo(DEVICE_TYPE); + assertThat(info.getDeviceName()).isEqualTo(DEVICE_NAME); + assertThat(info.getModelName()).isEqualTo(DEVICE_MODEL); + assertThat(info.getBatteryPercentage()).isEqualTo(BATTERY_PERCENTAGE); + assertThat(info.getConnectionStrength()).isEqualTo(CONNECTION_STRENGTH); + } + + @Test + public void testHashCode() { + DeviceInfo info1 = buildDeviceInfoBuilder().build(); + DeviceInfo info2 = buildDeviceInfoBuilder().build(); + + assertThat(info1.hashCode()).isEqualTo(info2.hashCode()); } private DeviceInfo.Builder buildDeviceInfoBuilder() { diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java index 37dca8def0bd..8a0f21e5eea6 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java @@ -22,8 +22,7 @@ import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURC import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVED; import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVE_FAILED; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static com.google.common.truth.Truth.assertThat; import android.os.Bundle; import android.os.Parcel; @@ -32,8 +31,10 @@ import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.Arrays; + /** - * Unit tests for {@link android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus}. + * Unit tests for {@link KnownNetworkConnectionStatus}. */ @SmallTest public class KnownNetworkConnectionStatusTest { @@ -45,6 +46,7 @@ public class KnownNetworkConnectionStatusTest { .setConnectionStrength(2).setBatteryPercentage(50).build(); private static final String SSID_1 = "TEST_SSID1"; private static final String BUNDLE_KEY = "INT-KEY"; + private static final int BUNDLE_VALUE = 1; /** * Verifies parcel serialization/deserialization. @@ -64,8 +66,8 @@ public class KnownNetworkConnectionStatusTest { KnownNetworkConnectionStatus fromParcel = KnownNetworkConnectionStatus.CREATOR.createFromParcel(parcelR); - assertEquals(status, fromParcel); - assertEquals(status.hashCode(), fromParcel.hashCode()); + assertThat(fromParcel).isEqualTo(status); + assertThat(fromParcel.hashCode()).isEqualTo(status.hashCode()); } /** @@ -75,15 +77,15 @@ public class KnownNetworkConnectionStatusTest { public void testEqualsOperation() { KnownNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build(); KnownNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build(); - assertEquals(status2, status2); + assertThat(status1).isEqualTo(status2); KnownNetworkConnectionStatus.Builder builder = buildConnectionStatusBuilder() .setStatus(CONNECTION_STATUS_SAVE_FAILED); - assertNotEquals(status1, builder.build()); + assertThat(builder.build()).isNotEqualTo(status1); builder = buildConnectionStatusBuilder() .setKnownNetwork(buildKnownNetworkBuilder().setSsid(SSID_1).build()); - assertNotEquals(status1, builder.build()); + assertThat(builder.build()).isNotEqualTo(status1); } /** @@ -92,9 +94,17 @@ public class KnownNetworkConnectionStatusTest { @Test public void testGetMethods() { KnownNetworkConnectionStatus status = buildConnectionStatusBuilder().build(); - assertEquals(status.getStatus(), CONNECTION_STATUS_SAVED); - assertEquals(status.getKnownNetwork(), buildKnownNetworkBuilder().build()); - assertEquals(status.getExtras().getInt(BUNDLE_KEY), buildBundle().getInt(BUNDLE_KEY)); + assertThat(status.getStatus()).isEqualTo(CONNECTION_STATUS_SAVED); + assertThat(status.getKnownNetwork()).isEqualTo(buildKnownNetworkBuilder().build()); + assertThat(status.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE); + } + + @Test + public void testHashCode() { + KnownNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build(); + KnownNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build(); + + assertThat(status1.hashCode()).isEqualTo(status2.hashCode()); } private KnownNetworkConnectionStatus.Builder buildConnectionStatusBuilder() { @@ -106,13 +116,15 @@ public class KnownNetworkConnectionStatusTest { private Bundle buildBundle() { Bundle bundle = new Bundle(); - bundle.putInt(BUNDLE_KEY, 1); + bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE); return bundle; } private KnownNetwork.Builder buildKnownNetworkBuilder() { - return new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE).setSsid(SSID) - .setSecurityTypes(SECURITY_TYPES).setDeviceInfo(DEVICE_INFO); + KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE) + .setSsid(SSID).setDeviceInfo(DEVICE_INFO); + Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType); + return builder; } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java index 266afcc9a1a6..872dd2e63227 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java @@ -23,18 +23,19 @@ import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TAB import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_CLOUD_SELF; import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static com.google.common.truth.Truth.assertThat; import android.os.Parcel; +import android.util.ArraySet; import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.Arrays; + /** - * Unit tests for {@link android.app.sharedconnectivity.KnownNetwork}. + * Unit tests for {@link KnownNetwork}. */ @SmallTest public class KnownNetworkTest { @@ -69,8 +70,8 @@ public class KnownNetworkTest { parcelR.setDataPosition(0); KnownNetwork fromParcel = KnownNetwork.CREATOR.createFromParcel(parcelR); - assertEquals(network, fromParcel); - assertEquals(network.hashCode(), fromParcel.hashCode()); + assertThat(fromParcel).isEqualTo(network); + assertThat(fromParcel.hashCode()).isEqualTo(network.hashCode()); } /** @@ -80,20 +81,21 @@ public class KnownNetworkTest { public void testEqualsOperation() { KnownNetwork network1 = buildKnownNetworkBuilder().build(); KnownNetwork network2 = buildKnownNetworkBuilder().build(); - assertEquals(network1, network2); + assertThat(network1).isEqualTo(network2); KnownNetwork.Builder builder = buildKnownNetworkBuilder() .setNetworkSource(NETWORK_SOURCE_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildKnownNetworkBuilder().setSsid(SSID_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); - builder = buildKnownNetworkBuilder().setSecurityTypes(SECURITY_TYPES_1); - assertNotEquals(network1, builder.build()); + builder = buildKnownNetworkBuilder(); + Arrays.stream(SECURITY_TYPES_1).forEach(builder::addSecurityType); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildKnownNetworkBuilder().setDeviceInfo(DEVICE_INFO_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); } /** @@ -102,14 +104,27 @@ public class KnownNetworkTest { @Test public void testGetMethods() { KnownNetwork network = buildKnownNetworkBuilder().build(); - assertEquals(network.getNetworkSource(), NETWORK_SOURCE); - assertEquals(network.getSsid(), SSID); - assertArrayEquals(network.getSecurityTypes(), SECURITY_TYPES); - assertEquals(network.getDeviceInfo(), DEVICE_INFO); + ArraySet<Integer> securityTypes = new ArraySet<>(); + Arrays.stream(SECURITY_TYPES).forEach(securityTypes::add); + + assertThat(network.getNetworkSource()).isEqualTo(NETWORK_SOURCE); + assertThat(network.getSsid()).isEqualTo(SSID); + assertThat(network.getSecurityTypes()).containsExactlyElementsIn(securityTypes); + assertThat(network.getDeviceInfo()).isEqualTo(DEVICE_INFO); + } + + @Test + public void testHashCode() { + KnownNetwork network1 = buildKnownNetworkBuilder().build(); + KnownNetwork network2 = buildKnownNetworkBuilder().build(); + + assertThat(network1.hashCode()).isEqualTo(network2.hashCode()); } private KnownNetwork.Builder buildKnownNetworkBuilder() { - return new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE).setSsid(SSID) - .setSecurityTypes(SECURITY_TYPES).setDeviceInfo(DEVICE_INFO); + KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE) + .setSsid(SSID).setDeviceInfo(DEVICE_INFO); + Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType); + return builder; } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java index cdb438f8b96b..7c0a8b65813c 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java @@ -22,11 +22,8 @@ import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TAB import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF; import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doThrow; @@ -49,6 +46,7 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; @@ -110,7 +108,7 @@ public class SharedConnectivityManagerTest { public void resourcesNotDefined() { when(mResources.getString(anyInt())).thenThrow(new Resources.NotFoundException()); - assertNull(SharedConnectivityManager.create(mContext)); + assertThat(SharedConnectivityManager.create(mContext)).isNull(); } /** @@ -183,7 +181,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertFalse(manager.unregisterCallback(mClientCallback)); + assertThat(manager.unregisterCallback(mClientCallback)).isFalse(); } @Test @@ -191,7 +189,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(mService); - assertFalse(manager.unregisterCallback(mClientCallback)); + assertThat(manager.unregisterCallback(mClientCallback)).isFalse(); } @Test @@ -201,7 +199,7 @@ public class SharedConnectivityManagerTest { manager.registerCallback(mExecutor, mClientCallback); - assertTrue(manager.unregisterCallback(mClientCallback)); + assertThat(manager.unregisterCallback(mClientCallback)).isTrue(); verify(mService).unregisterCallback(any()); } @@ -213,7 +211,7 @@ public class SharedConnectivityManagerTest { manager.registerCallback(mExecutor, mClientCallback); manager.unregisterCallback(mClientCallback); - assertFalse(manager.unregisterCallback(mClientCallback)); + assertThat(manager.unregisterCallback(mClientCallback)).isFalse(); } @Test @@ -224,7 +222,7 @@ public class SharedConnectivityManagerTest { manager.registerCallback(mExecutor, mClientCallback); manager.unregisterCallback(mClientCallback); - assertFalse(manager.unregisterCallback(mClientCallback)); + assertThat(manager.unregisterCallback(mClientCallback)).isFalse(); } @Test @@ -234,7 +232,7 @@ public class SharedConnectivityManagerTest { doThrow(new RemoteException()).when(mService).unregisterCallback(any()); - assertFalse(manager.unregisterCallback(mClientCallback)); + assertThat(manager.unregisterCallback(mClientCallback)).isFalse(); } /** @@ -294,7 +292,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertFalse(manager.connectTetherNetwork(network)); + assertThat(manager.connectTetherNetwork(network)).isFalse(); } @Test @@ -315,7 +313,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).connectTetherNetwork(network); - assertFalse(manager.connectTetherNetwork(network)); + assertThat(manager.connectTetherNetwork(network)).isFalse(); } /** @@ -327,7 +325,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertFalse(manager.disconnectTetherNetwork(network)); + assertThat(manager.disconnectTetherNetwork(network)).isFalse(); } @Test @@ -348,7 +346,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).disconnectTetherNetwork(any()); - assertFalse(manager.disconnectTetherNetwork(network)); + assertThat(manager.disconnectTetherNetwork(network)).isFalse(); } /** @@ -360,7 +358,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertFalse(manager.connectKnownNetwork(network)); + assertThat(manager.connectKnownNetwork(network)).isFalse(); } @Test @@ -381,7 +379,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).connectKnownNetwork(network); - assertFalse(manager.connectKnownNetwork(network)); + assertThat(manager.connectKnownNetwork(network)).isFalse(); } /** @@ -393,7 +391,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertFalse(manager.forgetKnownNetwork(network)); + assertThat(manager.forgetKnownNetwork(network)).isFalse(); } @Test @@ -414,7 +412,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).forgetKnownNetwork(network); - assertFalse(manager.forgetKnownNetwork(network)); + assertThat(manager.forgetKnownNetwork(network)).isFalse(); } /** @@ -425,7 +423,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertArrayEquals(List.of().toArray(), manager.getTetherNetworks().toArray()); + assertThat(manager.getKnownNetworks()).isEmpty(); } @Test @@ -434,18 +432,17 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).getTetherNetworks(); - assertArrayEquals(List.of().toArray(), manager.getTetherNetworks().toArray()); + assertThat(manager.getKnownNetworks()).isEmpty(); } @Test public void getTetherNetworks_shouldReturnNetworksList() throws RemoteException { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); List<TetherNetwork> networks = List.of(buildTetherNetwork()); - List<TetherNetwork> expected = List.of(buildTetherNetwork()); manager.setService(mService); when(mService.getTetherNetworks()).thenReturn(networks); - assertArrayEquals(expected.toArray(), manager.getTetherNetworks().toArray()); + assertThat(manager.getTetherNetworks()).containsExactly(buildTetherNetwork()); } @Test @@ -454,7 +451,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertArrayEquals(List.of().toArray(), manager.getKnownNetworks().toArray()); + assertThat(manager.getKnownNetworks()).isEmpty(); } @Test @@ -463,18 +460,17 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).getKnownNetworks(); - assertArrayEquals(List.of().toArray(), manager.getKnownNetworks().toArray()); + assertThat(manager.getKnownNetworks()).isEmpty(); } @Test public void getKnownNetworks_shouldReturnNetworksList() throws RemoteException { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); List<KnownNetwork> networks = List.of(buildKnownNetwork()); - List<KnownNetwork> expected = List.of(buildKnownNetwork()); manager.setService(mService); when(mService.getKnownNetworks()).thenReturn(networks); - assertArrayEquals(expected.toArray(), manager.getKnownNetworks().toArray()); + assertThat(manager.getKnownNetworks()).containsExactly(buildKnownNetwork()); } @Test @@ -482,7 +478,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertNull(manager.getSettingsState()); + assertThat(manager.getSettingsState()).isNull(); } @Test @@ -491,7 +487,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).getSettingsState(); - assertNull(manager.getSettingsState()); + assertThat(manager.getSettingsState()).isNull(); } @Test @@ -502,7 +498,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); when(mService.getSettingsState()).thenReturn(state); - assertEquals(state, manager.getSettingsState()); + assertThat(manager.getSettingsState()).isEqualTo(state); } @Test @@ -511,7 +507,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertNull(manager.getTetherNetworkConnectionStatus()); + assertThat(manager.getTetherNetworkConnectionStatus()).isNull(); } @Test @@ -521,7 +517,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).getTetherNetworkConnectionStatus(); - assertNull(manager.getTetherNetworkConnectionStatus()); + assertThat(manager.getTetherNetworkConnectionStatus()).isNull(); } @Test @@ -534,7 +530,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); when(mService.getTetherNetworkConnectionStatus()).thenReturn(status); - assertEquals(status, manager.getTetherNetworkConnectionStatus()); + assertThat(manager.getTetherNetworkConnectionStatus()).isEqualTo(status); } @Test @@ -543,7 +539,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertNull(manager.getKnownNetworkConnectionStatus()); + assertThat(manager.getKnownNetworkConnectionStatus()).isNull(); } @Test @@ -553,7 +549,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).getKnownNetworkConnectionStatus(); - assertNull(manager.getKnownNetworkConnectionStatus()); + assertThat(manager.getKnownNetworkConnectionStatus()).isNull(); } @Test @@ -566,7 +562,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); when(mService.getKnownNetworkConnectionStatus()).thenReturn(status); - assertEquals(status, manager.getKnownNetworkConnectionStatus()); + assertThat(manager.getKnownNetworkConnectionStatus()).isEqualTo(status); } private void setResources(@Mock Context context) { @@ -576,18 +572,20 @@ public class SharedConnectivityManagerTest { } private TetherNetwork buildTetherNetwork() { - return new TetherNetwork.Builder() + TetherNetwork.Builder builder = new TetherNetwork.Builder() .setDeviceId(DEVICE_ID) .setDeviceInfo(DEVICE_INFO) .setNetworkType(NETWORK_TYPE) .setNetworkName(NETWORK_NAME) - .setHotspotSsid(HOTSPOT_SSID) - .setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES) - .build(); + .setHotspotSsid(HOTSPOT_SSID); + Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType); + return builder.build(); } private KnownNetwork buildKnownNetwork() { - return new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE).setSsid(SSID) - .setSecurityTypes(SECURITY_TYPES).build(); + KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE) + .setSsid(SSID).setDeviceInfo(DEVICE_INFO); + Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType); + return builder.build(); } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java index 3137c7268ae0..752b74905c97 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java @@ -16,8 +16,7 @@ package android.net.wifi.sharedconnectivity.app; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static com.google.common.truth.Truth.assertThat; import android.os.Parcel; @@ -26,7 +25,7 @@ import androidx.test.filters.SmallTest; import org.junit.Test; /** - * Unit tests for {@link android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState}. + * Unit tests for {@link SharedConnectivitySettingsState}. */ @SmallTest public class SharedConnectivitySettingsStateTest { @@ -51,8 +50,8 @@ public class SharedConnectivitySettingsStateTest { SharedConnectivitySettingsState fromParcel = SharedConnectivitySettingsState.CREATOR.createFromParcel(parcelR); - assertEquals(state, fromParcel); - assertEquals(state.hashCode(), fromParcel.hashCode()); + assertThat(fromParcel).isEqualTo(state); + assertThat(fromParcel.hashCode()).isEqualTo(state.hashCode()); } /** @@ -62,11 +61,11 @@ public class SharedConnectivitySettingsStateTest { public void testEqualsOperation() { SharedConnectivitySettingsState state1 = buildSettingsStateBuilder().build(); SharedConnectivitySettingsState state2 = buildSettingsStateBuilder().build(); - assertEquals(state1, state2); + assertThat(state1).isEqualTo(state2); SharedConnectivitySettingsState.Builder builder = buildSettingsStateBuilder() .setInstantTetherEnabled(INSTANT_TETHER_STATE_1); - assertNotEquals(state1, builder.build()); + assertThat(builder.build()).isNotEqualTo(state1); } /** @@ -75,7 +74,15 @@ public class SharedConnectivitySettingsStateTest { @Test public void testGetMethods() { SharedConnectivitySettingsState state = buildSettingsStateBuilder().build(); - assertEquals(state.isInstantTetherEnabled(), INSTANT_TETHER_STATE); + assertThat(state.isInstantTetherEnabled()).isEqualTo(INSTANT_TETHER_STATE); + } + + @Test + public void testHashCode() { + SharedConnectivitySettingsState state1 = buildSettingsStateBuilder().build(); + SharedConnectivitySettingsState state2 = buildSettingsStateBuilder().build(); + + assertThat(state1.hashCode()).isEqualTo(state2.hashCode()); } private SharedConnectivitySettingsState.Builder buildSettingsStateBuilder() { diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java index 1d9c2e6df38a..0844364e7a63 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java @@ -23,8 +23,7 @@ import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE import static android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT; import static android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.CONNECTION_STATUS_TETHERING_TIMEOUT; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static com.google.common.truth.Truth.assertThat; import android.os.Bundle; import android.os.Parcel; @@ -33,8 +32,10 @@ import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.Arrays; + /** - * Unit tests for {@link android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus}. + * Unit tests for {@link TetherNetworkConnectionStatus}. */ @SmallTest public class TetherNetworkConnectionStatusTest { @@ -49,6 +50,7 @@ public class TetherNetworkConnectionStatusTest { private static final int[] HOTSPOT_SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP}; private static final long DEVICE_ID_1 = 111L; private static final String BUNDLE_KEY = "INT-KEY"; + private static final int BUNDLE_VALUE = 1; /** * Verifies parcel serialization/deserialization. @@ -68,8 +70,8 @@ public class TetherNetworkConnectionStatusTest { TetherNetworkConnectionStatus fromParcel = TetherNetworkConnectionStatus.CREATOR.createFromParcel(parcelR); - assertEquals(status, fromParcel); - assertEquals(status.hashCode(), fromParcel.hashCode()); + assertThat(fromParcel).isEqualTo(status); + assertThat(fromParcel.hashCode()).isEqualTo(status.hashCode()); } /** @@ -79,15 +81,15 @@ public class TetherNetworkConnectionStatusTest { public void testEqualsOperation() { TetherNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build(); TetherNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build(); - assertEquals(status2, status2); + assertThat(status1).isEqualTo(status2); TetherNetworkConnectionStatus.Builder builder = buildConnectionStatusBuilder() .setStatus(CONNECTION_STATUS_TETHERING_TIMEOUT); - assertNotEquals(status1, builder.build()); + assertThat(builder.build()).isNotEqualTo(status1); builder = buildConnectionStatusBuilder() .setTetherNetwork(buildTetherNetworkBuilder().setDeviceId(DEVICE_ID_1).build()); - assertNotEquals(status1, builder.build()); + assertThat(builder.build()).isNotEqualTo(status1); } /** @@ -96,13 +98,20 @@ public class TetherNetworkConnectionStatusTest { @Test public void testGetMethods() { TetherNetworkConnectionStatus status = buildConnectionStatusBuilder().build(); - assertEquals(status.getStatus(), CONNECTION_STATUS_ENABLING_HOTSPOT); - assertEquals(status.getTetherNetwork(), buildTetherNetworkBuilder().build()); - assertEquals(status.getExtras().getInt(BUNDLE_KEY), buildBundle().getInt(BUNDLE_KEY)); + assertThat(status.getStatus()).isEqualTo(CONNECTION_STATUS_ENABLING_HOTSPOT); + assertThat(status.getTetherNetwork()).isEqualTo(buildTetherNetworkBuilder().build()); + assertThat(status.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE); } - private TetherNetworkConnectionStatus.Builder buildConnectionStatusBuilder() { + @Test + public void testHashCode() { + TetherNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build(); + TetherNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build(); + + assertThat(status1.hashCode()).isEqualTo(status2.hashCode()); + } + private TetherNetworkConnectionStatus.Builder buildConnectionStatusBuilder() { return new TetherNetworkConnectionStatus.Builder() .setStatus(CONNECTION_STATUS_ENABLING_HOTSPOT) .setTetherNetwork(buildTetherNetworkBuilder().build()) @@ -111,18 +120,19 @@ public class TetherNetworkConnectionStatusTest { private Bundle buildBundle() { Bundle bundle = new Bundle(); - bundle.putInt(BUNDLE_KEY, 1); + bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE); return bundle; } private TetherNetwork.Builder buildTetherNetworkBuilder() { - return new TetherNetwork.Builder() + TetherNetwork.Builder builder = new TetherNetwork.Builder() .setDeviceId(DEVICE_ID) .setDeviceInfo(DEVICE_INFO) .setNetworkType(NETWORK_TYPE) .setNetworkName(NETWORK_NAME) .setHotspotSsid(HOTSPOT_SSID) - .setHotspotBssid(HOTSPOT_BSSID) - .setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES); + .setHotspotBssid(HOTSPOT_BSSID); + Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType); + return builder; } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java index b01aec4ad1c1..a50d76782c4a 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java @@ -24,18 +24,19 @@ import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TAB import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR; import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_WIFI; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static com.google.common.truth.Truth.assertThat; import android.os.Parcel; +import android.util.ArraySet; import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.Arrays; + /** - * Unit tests for {@link android.net.wifi.sharedconnectivity.app.TetherNetwork}. + * Unit tests for {@link TetherNetwork}. */ @SmallTest public class TetherNetworkTest { @@ -76,8 +77,8 @@ public class TetherNetworkTest { parcelR.setDataPosition(0); TetherNetwork fromParcel = TetherNetwork.CREATOR.createFromParcel(parcelR); - assertEquals(network, fromParcel); - assertEquals(network.hashCode(), fromParcel.hashCode()); + assertThat(fromParcel).isEqualTo(network); + assertThat(fromParcel.hashCode()).isEqualTo(network.hashCode()); } /** @@ -87,28 +88,31 @@ public class TetherNetworkTest { public void testEqualsOperation() { TetherNetwork network1 = buildTetherNetworkBuilder().build(); TetherNetwork network2 = buildTetherNetworkBuilder().build(); - assertEquals(network1, network2); + assertThat(network1).isEqualTo(network2); TetherNetwork.Builder builder = buildTetherNetworkBuilder().setDeviceId(DEVICE_ID_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildTetherNetworkBuilder().setDeviceInfo(DEVICE_INFO_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildTetherNetworkBuilder().setNetworkType(NETWORK_TYPE_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildTetherNetworkBuilder().setNetworkName(NETWORK_NAME_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildTetherNetworkBuilder().setHotspotSsid(HOTSPOT_SSID_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildTetherNetworkBuilder().setHotspotBssid(HOTSPOT_BSSID_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); + + builder = buildTetherNetworkBuilder(); + TetherNetwork.Builder builder1 = buildTetherNetworkBuilder(); + Arrays.stream(HOTSPOT_SECURITY_TYPES_1).forEach(builder1::addHotspotSecurityType); - builder = buildTetherNetworkBuilder().setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES_1); - assertNotEquals(network1, builder.build()); + assertThat(builder1.build()).isNotEqualTo(builder.build()); } /** @@ -117,23 +121,35 @@ public class TetherNetworkTest { @Test public void testGetMethods() { TetherNetwork network = buildTetherNetworkBuilder().build(); - assertEquals(network.getDeviceId(), DEVICE_ID); - assertEquals(network.getDeviceInfo(), DEVICE_INFO); - assertEquals(network.getNetworkType(), NETWORK_TYPE); - assertEquals(network.getNetworkName(), NETWORK_NAME); - assertEquals(network.getHotspotSsid(), HOTSPOT_SSID); - assertEquals(network.getHotspotBssid(), HOTSPOT_BSSID); - assertArrayEquals(network.getHotspotSecurityTypes(), HOTSPOT_SECURITY_TYPES); + ArraySet<Integer> securityTypes = new ArraySet<>(); + Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(securityTypes::add); + + assertThat(network.getDeviceId()).isEqualTo(DEVICE_ID); + assertThat(network.getDeviceInfo()).isEqualTo(DEVICE_INFO); + assertThat(network.getNetworkType()).isEqualTo(NETWORK_TYPE); + assertThat(network.getNetworkName()).isEqualTo(NETWORK_NAME); + assertThat(network.getHotspotSsid()).isEqualTo(HOTSPOT_SSID); + assertThat(network.getHotspotBssid()).isEqualTo(HOTSPOT_BSSID); + assertThat(network.getHotspotSecurityTypes()).containsExactlyElementsIn(securityTypes); + } + + @Test + public void testHashCode() { + TetherNetwork network1 = buildTetherNetworkBuilder().build(); + TetherNetwork network2 = buildTetherNetworkBuilder().build(); + + assertThat(network1.hashCode()).isEqualTo(network2.hashCode()); } private TetherNetwork.Builder buildTetherNetworkBuilder() { - return new TetherNetwork.Builder() + TetherNetwork.Builder builder = new TetherNetwork.Builder() .setDeviceId(DEVICE_ID) .setDeviceInfo(DEVICE_INFO) .setNetworkType(NETWORK_TYPE) .setNetworkName(NETWORK_NAME) .setHotspotSsid(HOTSPOT_SSID) - .setHotspotBssid(HOTSPOT_BSSID) - .setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES); + .setHotspotBssid(HOTSPOT_BSSID); + Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType); + return builder; } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java index a04526a61bcb..81efa79f6df8 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java @@ -24,9 +24,8 @@ import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStat import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR; import static android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.when; import android.content.Context; @@ -52,11 +51,10 @@ import org.mockito.MockitoAnnotations; import java.util.List; /** - * Unit tests for {@link android.net.wifi.sharedconnectivity.service.SharedConnectivityService}. + * Unit tests for {@link SharedConnectivityService}. */ @SmallTest public class SharedConnectivityServiceTest { - private static final int[] SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP}; private static final DeviceInfo DEVICE_INFO = new DeviceInfo.Builder() .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName("TEST_MODEL") .setConnectionStrength(2).setBatteryPercentage(50).build(); @@ -64,12 +62,13 @@ public class SharedConnectivityServiceTest { new TetherNetwork.Builder().setDeviceId(1).setDeviceInfo(DEVICE_INFO) .setNetworkType(NETWORK_TYPE_CELLULAR).setNetworkName("TEST_NETWORK") .setHotspotSsid("TEST_SSID").setHotspotBssid("TEST_BSSID") - .setHotspotSecurityTypes(SECURITY_TYPES).build(); + .addHotspotSecurityType(SECURITY_TYPE_WEP) + .addHotspotSecurityType(SECURITY_TYPE_EAP).build(); private static final List<TetherNetwork> TETHER_NETWORKS = List.of(TETHER_NETWORK); private static final KnownNetwork KNOWN_NETWORK = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE_NEARBY_SELF) - .setSsid("TEST_SSID").setSecurityTypes(SECURITY_TYPES) - .setDeviceInfo(DEVICE_INFO).build(); + .setSsid("TEST_SSID").addSecurityType(SECURITY_TYPE_WEP) + .addSecurityType(SECURITY_TYPE_EAP).setDeviceInfo(DEVICE_INFO).build(); private static final List<KnownNetwork> KNOWN_NETWORKS = List.of(KNOWN_NETWORK); private static final SharedConnectivitySettingsState SETTINGS_STATE = new SharedConnectivitySettingsState.Builder().setInstantTetherEnabled(true) @@ -111,7 +110,8 @@ public class SharedConnectivityServiceTest { @Test public void onBind_isNotNull() { SharedConnectivityService service = createService(); - assertNotNull(service.onBind(new Intent())); + + assertThat(service.onBind(new Intent())).isNotNull(); } @Test @@ -121,7 +121,9 @@ public class SharedConnectivityServiceTest { (ISharedConnectivityService.Stub) service.onBind(new Intent()); service.setTetherNetworks(TETHER_NETWORKS); - assertArrayEquals(TETHER_NETWORKS.toArray(), binder.getTetherNetworks().toArray()); + + assertThat(binder.getTetherNetworks()) + .containsExactlyElementsIn(List.copyOf(TETHER_NETWORKS)); } @Test @@ -131,7 +133,9 @@ public class SharedConnectivityServiceTest { (ISharedConnectivityService.Stub) service.onBind(new Intent()); service.setKnownNetworks(KNOWN_NETWORKS); - assertArrayEquals(KNOWN_NETWORKS.toArray(), binder.getKnownNetworks().toArray()); + + assertThat(binder.getKnownNetworks()) + .containsExactlyElementsIn(List.copyOf(KNOWN_NETWORKS)); } @Test @@ -141,7 +145,8 @@ public class SharedConnectivityServiceTest { (ISharedConnectivityService.Stub) service.onBind(new Intent()); service.setSettingsState(SETTINGS_STATE); - assertEquals(SETTINGS_STATE, binder.getSettingsState()); + + assertThat(binder.getSettingsState()).isEqualTo(SETTINGS_STATE); } @Test @@ -151,7 +156,9 @@ public class SharedConnectivityServiceTest { (ISharedConnectivityService.Stub) service.onBind(new Intent()); service.updateTetherNetworkConnectionStatus(TETHER_NETWORK_CONNECTION_STATUS); - assertEquals(TETHER_NETWORK_CONNECTION_STATUS, binder.getTetherNetworkConnectionStatus()); + + assertThat(binder.getTetherNetworkConnectionStatus()) + .isEqualTo(TETHER_NETWORK_CONNECTION_STATUS); } @Test @@ -161,7 +168,9 @@ public class SharedConnectivityServiceTest { (ISharedConnectivityService.Stub) service.onBind(new Intent()); service.updateKnownNetworkConnectionStatus(KNOWN_NETWORK_CONNECTION_STATUS); - assertEquals(KNOWN_NETWORK_CONNECTION_STATUS, binder.getKnownNetworkConnectionStatus()); + + assertThat(binder.getKnownNetworkConnectionStatus()) + .isEqualTo(KNOWN_NETWORK_CONNECTION_STATUS); } private SharedConnectivityService createService() { |