diff options
810 files changed, 16956 insertions, 9827 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 7913ad1d09a5..08a09e1b1a73 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -15,6 +15,7 @@ aconfig_srcjars = [ ":android.app.usage.flags-aconfig-java{.generated_srcjars}", ":android.content.pm.flags-aconfig-java{.generated_srcjars}", + ":android.hardware.radio.flags-aconfig-java{.generated_srcjars}", ":android.nfc.flags-aconfig-java{.generated_srcjars}", ":android.os.flags-aconfig-java{.generated_srcjars}", ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}", @@ -35,6 +36,8 @@ aconfig_srcjars = [ ":android.permission.flags-aconfig-java{.generated_srcjars}", ":hwui_flags_java_lib{.generated_srcjars}", ":display_flags_lib{.generated_srcjars}", + ":android.multiuser.flags-aconfig-java{.generated_srcjars}", + ":android.app.flags-aconfig-java{.generated_srcjars}", ] filegroup { @@ -252,7 +255,7 @@ java_aconfig_library { aconfig_declarations { name: "android.content.pm.flags-aconfig", package: "android.content.pm", - srcs: ["core/java/android/content/pm/*.aconfig"], + srcs: ["core/java/android/content/pm/flags.aconfig"], } java_aconfig_library { @@ -313,3 +316,42 @@ java_aconfig_library { aconfig_declarations: "display_flags", defaults: ["framework-minus-apex-aconfig-java-defaults"], } + +// Multi user +aconfig_declarations { + name: "android.multiuser.flags-aconfig", + package: "android.multiuser", + srcs: ["core/java/android/content/pm/multiuser.aconfig"], +} + +java_aconfig_library { + name: "android.multiuser.flags-aconfig-java", + aconfig_declarations: "android.multiuser.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + +// Activity Manager +aconfig_declarations { + name: "android.app.flags-aconfig", + package: "android.app", + srcs: ["core/java/android/app/activity_manager.aconfig"], +} + +java_aconfig_library { + name: "android.app.flags-aconfig-java", + aconfig_declarations: "android.app.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + +// Broadcast Radio +aconfig_declarations { + name: "android.hardware.radio.flags-aconfig", + package: "android.hardware.radio", + srcs: ["core/java/android/hardware/radio/*.aconfig"], +} + +java_aconfig_library { + name: "android.hardware.radio.flags-aconfig-java", + aconfig_declarations: "android.hardware.radio.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/apct-tests/perftests/core/src/android/accessibility/AccessibilityPerfTest.java b/apct-tests/perftests/core/src/android/accessibility/AccessibilityPerfTest.java index 7927aa90695e..885000fdcf3e 100644 --- a/apct-tests/perftests/core/src/android/accessibility/AccessibilityPerfTest.java +++ b/apct-tests/perftests/core/src/android/accessibility/AccessibilityPerfTest.java @@ -22,7 +22,6 @@ import android.app.Activity; import android.app.Instrumentation; import android.app.UiAutomation; import android.perftests.utils.PerfTestActivity; -import android.platform.test.annotations.LargeTest; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; @@ -32,6 +31,7 @@ import android.widget.TextView; import androidx.benchmark.BenchmarkState; import androidx.benchmark.junit4.BenchmarkRule; +import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; diff --git a/apct-tests/perftests/core/src/android/text/TextViewCursorAnchorInfoPerfTest.java b/apct-tests/perftests/core/src/android/text/TextViewCursorAnchorInfoPerfTest.java index 898111f2fd8a..436ee16504bb 100644 --- a/apct-tests/perftests/core/src/android/text/TextViewCursorAnchorInfoPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/TextViewCursorAnchorInfoPerfTest.java @@ -22,13 +22,13 @@ import android.content.Context; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; import android.perftests.utils.PerfTestActivity; -import android.platform.test.annotations.LargeTest; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.CursorAnchorInfo; import android.widget.TextView; +import androidx.test.filters.LargeTest; import androidx.test.rule.ActivityTestRule; import org.junit.Before; diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java index fd9d99181fbf..c53cc1820b6f 100644 --- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java +++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java @@ -17,12 +17,15 @@ package android.surfaceflinger; import static android.server.wm.CtsWindowInfoUtils.waitForWindowOnTop; +import static android.provider.Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS; import android.app.Instrumentation; +import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.os.Bundle; +import android.provider.Settings; import android.util.Log; import android.view.SurfaceControl; import android.view.SurfaceHolder; @@ -33,9 +36,11 @@ import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.compatibility.common.util.SystemUtil; import com.android.helpers.SimpleperfHelper; import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; @@ -71,7 +76,7 @@ public class SurfaceFlingerPerfTest { private int mTransformHint; private SimpleperfHelper mSimpleperfHelper = new SimpleperfHelper(); - + private static String sImmersiveModeConfirmationValue; /** Start simpleperf sampling. */ public void startSimpleperf(String subcommand, String arguments) { if (!mSimpleperfHelper.startCollecting(subcommand, arguments)) { @@ -88,6 +93,17 @@ public class SurfaceFlingerPerfTest { @BeforeClass public static void suiteSetup() { + SystemUtil.runWithShellPermissionIdentity(() -> { + // hide immersive mode confirmation dialog + final ContentResolver resolver = + InstrumentationRegistry.getInstrumentation().getContext().getContentResolver(); + sImmersiveModeConfirmationValue = + Settings.Secure.getString(resolver, IMMERSIVE_MODE_CONFIRMATIONS); + Settings.Secure.putString( + resolver, + IMMERSIVE_MODE_CONFIRMATIONS, + "confirmed"); + }); final Bundle arguments = InstrumentationRegistry.getArguments(); sProfilingIterations = Integer.parseInt( arguments.getString(ARGUMENT_PROFILING_ITERATIONS, DEFAULT_PROFILING_ITERATIONS)); @@ -98,6 +114,18 @@ public class SurfaceFlingerPerfTest { .executeShellCommand("service call SurfaceFlinger 1041 i32 -1"); } + @AfterClass + public static void suiteTeardown() { + SystemUtil.runWithShellPermissionIdentity(() -> { + // Restore the immersive mode confirmation state. + Settings.Secure.putString( + InstrumentationRegistry.getInstrumentation().getContext().getContentResolver(), + IMMERSIVE_MODE_CONFIRMATIONS, + sImmersiveModeConfirmationValue); + }); + } + + @Before public void setup() { mActivityRule.getScenario().onActivity(activity -> mActivity = activity); diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING index d9c463294a69..8504b1f0bdb1 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING +++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING @@ -4,7 +4,7 @@ "name": "CtsJobSchedulerTestCases", "options": [ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, - {"exclude-annotation": "android.platform.test.annotations.LargeTest"}, + {"exclude-annotation": "androidx.test.filters.LargeTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.LargeTest"} ] @@ -14,7 +14,7 @@ "options": [ {"include-filter": "com.android.server.job"}, {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, - {"exclude-annotation": "android.platform.test.annotations.LargeTest"}, + {"exclude-annotation": "androidx.test.filters.LargeTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"} ] }, @@ -23,7 +23,7 @@ "options": [ {"include-filter": "com.android.server.job"}, {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, - {"exclude-annotation": "android.platform.test.annotations.LargeTest"}, + {"exclude-annotation": "androidx.test.filters.LargeTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"} ] } diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp index 8989b10675d3..56a69b46b12c 100644 --- a/api/StubLibraries.bp +++ b/api/StubLibraries.bp @@ -67,6 +67,7 @@ droidstubs { tag: ".removed-api.txt", }, ], + api_surface: "public", } priv_apps = " --show-annotation android.annotation.SystemApi\\(" + @@ -120,6 +121,7 @@ droidstubs { tag: ".removed-api.txt", }, ], + api_surface: "system", } droidstubs { @@ -166,6 +168,7 @@ droidstubs { tag: ".removed-api.txt", }, ], + api_surface: "test", } droidstubs { @@ -205,6 +208,7 @@ droidstubs { tag: ".removed-api.txt", }, ], + api_surface: "module-lib", } ///////////////////////////////////////////////////////////////////// @@ -381,8 +385,8 @@ java_defaults { java_api_library { name: "android-non-updatable.stubs.from-text", api_surface: "public", - api_files: [ - ":non-updatable-current.txt", + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", ], defaults: ["android-non-updatable_from_text_defaults"], full_api_surface_stub: "android_stubs_current.from-text", @@ -391,9 +395,9 @@ java_api_library { java_api_library { name: "android-non-updatable.stubs.system.from-text", api_surface: "system", - api_files: [ - ":non-updatable-current.txt", - ":non-updatable-system-current.txt", + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", ], defaults: ["android-non-updatable_from_text_defaults"], full_api_surface_stub: "android_system_stubs_current.from-text", @@ -402,10 +406,10 @@ java_api_library { java_api_library { name: "android-non-updatable.stubs.test.from-text", api_surface: "test", - api_files: [ - ":non-updatable-current.txt", - ":non-updatable-system-current.txt", - ":non-updatable-test-current.txt", + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", + "test-api-stubs-docs-non-updatable.api.contribution", ], defaults: ["android-non-updatable_from_text_defaults"], full_api_surface_stub: "android_test_stubs_current.from-text", @@ -414,10 +418,10 @@ java_api_library { java_api_library { name: "android-non-updatable.stubs.module_lib.from-text", api_surface: "module_lib", - api_files: [ - ":non-updatable-current.txt", - ":non-updatable-system-current.txt", - ":non-updatable-module-lib-current.txt", + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", + "module-lib-api-stubs-docs-non-updatable.api.contribution", ], defaults: ["android-non-updatable_from_text_defaults"], full_api_surface_stub: "android_module_lib_stubs_current_full.from-text", @@ -615,7 +619,6 @@ java_defaults { name: "android_test_stubs_current_contributions", api_surface: "test", api_contributions: [ - "test-api-stubs-docs-non-updatable.api.contribution", "framework-virtualization.stubs.source.test.api.contribution", "framework-location.stubs.source.test.api.contribution", ], @@ -690,6 +693,7 @@ java_api_library { api_contributions: [ "api-stubs-docs-non-updatable.api.contribution", "system-api-stubs-docs-non-updatable.api.contribution", + "test-api-stubs-docs-non-updatable.api.contribution", ], visibility: ["//visibility:public"], } diff --git a/core/api/current.txt b/core/api/current.txt index 1eb9e97db42b..d8e3abfcc33d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4616,9 +4616,9 @@ package android.app { public class ActivityManager { method public int addAppTask(@NonNull android.app.Activity, @NonNull android.content.Intent, @Nullable android.app.ActivityManager.TaskDescription, @NonNull android.graphics.Bitmap); - method public void addStartInfoTimestamp(@IntRange(from=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START, to=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int, long); + method @FlaggedApi("android.app.app_start_info") public void addStartInfoTimestamp(@IntRange(from=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START, to=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int, long); method public void appNotResponding(@NonNull String); - method public void clearApplicationStartInfoCompletionListener(); + method @FlaggedApi("android.app.app_start_info") public void clearApplicationStartInfoCompletionListener(); method public boolean clearApplicationUserData(); method public void clearWatchHeapLimit(); method @RequiresPermission(android.Manifest.permission.DUMP) public void dumpPackageState(java.io.FileDescriptor, String); @@ -4626,7 +4626,7 @@ package android.app { method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks(); method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo(); method @NonNull public java.util.List<android.app.ApplicationExitInfo> getHistoricalProcessExitReasons(@Nullable String, @IntRange(from=0) int, @IntRange(from=0) int); - method @NonNull public java.util.List<android.app.ApplicationStartInfo> getHistoricalProcessStartReasons(@IntRange(from=0) int); + method @FlaggedApi("android.app.app_start_info") @NonNull public java.util.List<android.app.ApplicationStartInfo> getHistoricalProcessStartReasons(@IntRange(from=0) int); method public int getLargeMemoryClass(); method public int getLauncherLargeIconDensity(); method public int getLauncherLargeIconSize(); @@ -4653,7 +4653,7 @@ package android.app { method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int); method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int, android.os.Bundle); method @Deprecated public void restartPackage(String); - method public void setApplicationStartInfoCompletionListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.ApplicationStartInfo>); + method @FlaggedApi("android.app.app_start_info") public void setApplicationStartInfoCompletionListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.ApplicationStartInfo>); method public void setProcessStateSummary(@Nullable byte[]); method public static void setVrThread(int); method public void setWatchHeapLimit(long); @@ -5234,7 +5234,7 @@ package android.app { field public static final int REASON_USER_STOPPED = 11; // 0xb } - public final class ApplicationStartInfo implements android.os.Parcelable { + @FlaggedApi("android.app.app_start_info") public final class ApplicationStartInfo implements android.os.Parcelable { method public int describeContents(); method public int getDefiningUid(); method @Nullable public android.content.Intent getIntent(); @@ -17563,7 +17563,7 @@ package android.graphics.fonts { ctor public FontFamily.Builder(@NonNull android.graphics.fonts.Font); method @NonNull public android.graphics.fonts.FontFamily.Builder addFont(@NonNull android.graphics.fonts.Font); method @NonNull public android.graphics.fonts.FontFamily build(); - method @Nullable public android.graphics.fonts.FontFamily buildVariableFamily(); + method @FlaggedApi("com.android.text.flags.deprecate_fonts_xml") @Nullable public android.graphics.fonts.FontFamily buildVariableFamily(); } public final class FontStyle { @@ -17656,7 +17656,7 @@ package android.graphics.text { method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public int getHyphenation(); method public int getLineBreakStyle(); method public int getLineBreakWordStyle(); - method @NonNull public android.graphics.text.LineBreakConfig merge(@NonNull android.graphics.text.LineBreakConfig); + method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.graphics.text.LineBreakConfig merge(@NonNull android.graphics.text.LineBreakConfig); field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_DISABLED = 0; // 0x0 field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_ENABLED = 1; // 0x1 field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_UNSPECIFIED = -1; // 0xffffffff @@ -17664,16 +17664,16 @@ package android.graphics.text { field public static final int LINE_BREAK_STYLE_NONE = 0; // 0x0 field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2 field public static final int LINE_BREAK_STYLE_STRICT = 3; // 0x3 - field public static final int LINE_BREAK_STYLE_UNSPECIFIED = -1; // 0xffffffff + field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int LINE_BREAK_STYLE_UNSPECIFIED = -1; // 0xffffffff field public static final int LINE_BREAK_WORD_STYLE_NONE = 0; // 0x0 field public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; // 0x1 - field public static final int LINE_BREAK_WORD_STYLE_UNSPECIFIED = -1; // 0xffffffff + field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int LINE_BREAK_WORD_STYLE_UNSPECIFIED = -1; // 0xffffffff } public static final class LineBreakConfig.Builder { ctor public LineBreakConfig.Builder(); method @NonNull public android.graphics.text.LineBreakConfig build(); - method @NonNull public android.graphics.text.LineBreakConfig.Builder merge(@NonNull android.graphics.text.LineBreakConfig); + method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.graphics.text.LineBreakConfig.Builder merge(@NonNull android.graphics.text.LineBreakConfig); method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.graphics.text.LineBreakConfig.Builder setHyphenation(int); method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakStyle(int); method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakWordStyle(int); @@ -17698,7 +17698,7 @@ package android.graphics.text { method @NonNull public android.graphics.text.LineBreaker.Builder setHyphenationFrequency(int); method @NonNull public android.graphics.text.LineBreaker.Builder setIndents(@Nullable int[]); method @NonNull public android.graphics.text.LineBreaker.Builder setJustificationMode(int); - method @NonNull public android.graphics.text.LineBreaker.Builder setUseBoundsForWidth(boolean); + method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.graphics.text.LineBreaker.Builder setUseBoundsForWidth(boolean); } public static class LineBreaker.ParagraphConstraints { @@ -17750,18 +17750,18 @@ package android.graphics.text { method public float getAdvance(); method public float getAscent(); method public float getDescent(); - method public boolean getFakeBold(@IntRange(from=0) int); - method public boolean getFakeItalic(@IntRange(from=0) int); + method @FlaggedApi("com.android.text.flags.deprecate_fonts_xml") public boolean getFakeBold(@IntRange(from=0) int); + method @FlaggedApi("com.android.text.flags.deprecate_fonts_xml") public boolean getFakeItalic(@IntRange(from=0) int); method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int); method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int); method public float getGlyphX(@IntRange(from=0) int); method public float getGlyphY(@IntRange(from=0) int); - method public float getItalicOverride(@IntRange(from=0) int); + method @FlaggedApi("com.android.text.flags.deprecate_fonts_xml") public float getItalicOverride(@IntRange(from=0) int); method public float getOffsetX(); method public float getOffsetY(); - method public float getWeightOverride(@IntRange(from=0) int); + method @FlaggedApi("com.android.text.flags.deprecate_fonts_xml") public float getWeightOverride(@IntRange(from=0) int); method @IntRange(from=0) public int glyphCount(); - field public static final float NO_OVERRIDE = 1.4E-45f; + field @FlaggedApi("com.android.text.flags.deprecate_fonts_xml") public static final float NO_OVERRIDE = 1.4E-45f; } public class TextRunShaper { @@ -18646,6 +18646,10 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> FLASH_INFO_AVAILABLE; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> FLASH_INFO_STRENGTH_DEFAULT_LEVEL; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> FLASH_INFO_STRENGTH_MAXIMUM_LEVEL; + field @FlaggedApi("com.android.internal.camera.flags.camera_manual_flash_strength_control") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL; + field @FlaggedApi("com.android.internal.camera.flags.camera_manual_flash_strength_control") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> FLASH_SINGLE_STRENGTH_MAX_LEVEL; + field @FlaggedApi("com.android.internal.camera.flags.camera_manual_flash_strength_control") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> FLASH_TORCH_STRENGTH_DEFAULT_LEVEL; + field @FlaggedApi("com.android.internal.camera.flags.camera_manual_flash_strength_control") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> FLASH_TORCH_STRENGTH_MAX_LEVEL; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.DeviceStateSensorOrientationMap> INFO_DEVICE_STATE_SENSOR_ORIENTATION_MAP; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> INFO_SUPPORTED_HARDWARE_LEVEL; @@ -19215,6 +19219,7 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> EDGE_MODE; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> EXTENSION_STRENGTH; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> FLASH_MODE; + field @FlaggedApi("com.android.internal.camera.flags.camera_manual_flash_strength_control") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> FLASH_STRENGTH_LEVEL; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> HOT_PIXEL_MODE; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<android.location.Location> JPEG_GPS_LOCATION; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> JPEG_ORIENTATION; @@ -19310,6 +19315,7 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EXTENSION_STRENGTH; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> FLASH_MODE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> FLASH_STATE; + field @FlaggedApi("com.android.internal.camera.flags.camera_manual_flash_strength_control") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> FLASH_STRENGTH_LEVEL; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> HOT_PIXEL_MODE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.location.Location> JPEG_GPS_LOCATION; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> JPEG_ORIENTATION; @@ -47891,8 +47897,8 @@ package android.text.style { } @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public class LineBreakConfigSpan { - ctor public LineBreakConfigSpan(@NonNull android.graphics.text.LineBreakConfig); - method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig(); + ctor @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public LineBreakConfigSpan(@NonNull android.graphics.text.LineBreakConfig); + method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig(); } @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final class LineBreakConfigSpan.NoHyphenationSpan extends android.text.style.LineBreakConfigSpan { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 660859d6e66e..220482da9d64 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -540,7 +540,7 @@ package android.app { method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int); method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String); method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser(); - method @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int); + method @FlaggedApi(Flags.FLAG_APP_START_INFO) @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String); method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales(); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int); @@ -3232,6 +3232,7 @@ package android.companion.virtual { method public int getDefaultActivityPolicy(); method public int getDefaultNavigationPolicy(); method public int getDevicePolicy(int); + method @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @Nullable public android.content.ComponentName getHomeComponent(); method public int getLockState(); method @Nullable public String getName(); method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts(); @@ -3263,6 +3264,7 @@ package android.companion.virtual { method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int); + method @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName); method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>); @@ -4527,6 +4529,7 @@ package android.hardware.display { method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration); method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfigurationForDisplay(@NonNull android.hardware.display.BrightnessConfiguration, @NonNull String); method @Deprecated @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_SATURATION) public void setSaturationLevel(float); + field public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 128; // 0x80 field public static final int VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED = 65536; // 0x10000 field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400 } @@ -9840,7 +9843,6 @@ package android.os { field public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1; // 0x1 field public static final int BUGREPORT_MODE_FULL = 0; // 0x0 field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1 - field public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7 field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2 field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4 field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3 diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 4f4569134c19..3bf2ccaf9923 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -24,6 +24,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import android.Manifest; import android.annotation.ColorInt; import android.annotation.DrawableRes; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -3982,6 +3983,7 @@ public class ActivityManager { * the order from most recent to least recent. */ @NonNull + @FlaggedApi(Flags.FLAG_APP_START_INFO) public List<ApplicationStartInfo> getHistoricalProcessStartReasons( @IntRange(from = 0) int maxNum) { try { @@ -4012,6 +4014,7 @@ public class ActivityManager { */ @NonNull @SystemApi + @FlaggedApi(Flags.FLAG_APP_START_INFO) @RequiresPermission(Manifest.permission.DUMP) public List<ApplicationStartInfo> getExternalHistoricalProcessStartReasons( @NonNull String packageName, @IntRange(from = 0) int maxNum) { @@ -4044,6 +4047,7 @@ public class ActivityManager { * * @throws IllegalArgumentException if executor or listener are null. */ + @FlaggedApi(Flags.FLAG_APP_START_INFO) public void setApplicationStartInfoCompletionListener(@NonNull final Executor executor, @NonNull final Consumer<ApplicationStartInfo> listener) { Preconditions.checkNotNull(executor, "executor cannot be null"); @@ -4065,6 +4069,7 @@ public class ActivityManager { /** * Removes the callback set by {@link #setApplicationStartInfoCompletionListener} if there is one. */ + @FlaggedApi(Flags.FLAG_APP_START_INFO) public void clearApplicationStartInfoCompletionListener() { try { getService().clearApplicationStartInfoCompleteListener(mContext.getUserId()); @@ -4089,6 +4094,7 @@ public class ActivityManager { * Will thow {@link java.lang.IllegalArgumentException} if not in range. * @param timestampNs Clock monotonic time in nanoseconds of event to be recorded. */ + @FlaggedApi(Flags.FLAG_APP_START_INFO) public void addStartInfoTimestamp(@IntRange( from = ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START, to = ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int key, diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java index 2767b43a119c..6a50e74ca3a4 100644 --- a/core/java/android/app/Application.java +++ b/core/java/android/app/Application.java @@ -616,9 +616,8 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { Log.v(TAG, "getAutofillClient(): null on super, trying to find activity thread"); } // Okay, ppl use the application context when they should not. This breaks - // autofill among other things. We pick the focused activity since autofill - // interacts only with the currently focused activity and we need the fill - // client only if a call comes from the focused activity. Sigh... + // autofill among other things. Below is a mitigation to find the top resumed + // activity. final ActivityThread activityThread = ActivityThread.currentActivityThread(); if (activityThread == null) { return null; @@ -634,16 +633,27 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { if (activity == null) { continue; } + if (record.isTopResumedActivity) { + if (android.view.autofill.Helper.sVerbose) { + Log.v(TAG, "getAutofillClient(): found top resumed activity for " + this + + ": " + activity); + } + return activity.getAutofillClient(); + } + // As a back up option, we pick the focused activity since autofill interacts only + // with the currently focused activity and we need the fill client only if a call + // comes from the focused activity. if (activity.getWindow().getDecorView().hasFocus()) { if (android.view.autofill.Helper.sVerbose) { - Log.v(TAG, "getAutofillClient(): found activity for " + this + ": " + activity); + Log.v(TAG, "getAutofillClient(): found focused activity for " + this + + ": " + activity); } return activity.getAutofillClient(); } } if (android.view.autofill.Helper.sVerbose) { Log.v(TAG, "getAutofillClient(): none of the " + activityCount + " activities on " - + this + " have focus"); + + this + " are top resumed nor have focus"); } return null; } diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java index f5fb6edfcc04..a6a57cd5745a 100644 --- a/core/java/android/app/ApplicationStartInfo.java +++ b/core/java/android/app/ApplicationStartInfo.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -37,6 +38,7 @@ import java.util.Set; /** * Provide information related to a processes startup. */ +@FlaggedApi(Flags.FLAG_APP_START_INFO) public final class ApplicationStartInfo implements Parcelable { /** diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 7f38b27c12c8..ec5effd0963d 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -98,6 +98,7 @@ interface INotificationManager ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted); NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid); NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(String pkg, int uid, String groupId, boolean includeDeleted); + ParceledListSlice getRecentBlockedNotificationChannelGroupsForPackage(String pkg, int uid); void updateNotificationChannelGroupForPackage(String pkg, int uid, in NotificationChannelGroup group); void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel); void unlockNotificationChannel(String pkg, int uid, String channelId); diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index 93107cea406e..315a0556042a 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -138,7 +138,7 @@ "include-annotation": "android.platform.test.annotations.Presubmit" }, { - "exclude-annotation": "android.platform.test.annotations.LargeTest" + "exclude-annotation": "androidx.test.filters.LargeTest" }, { "exclude-annotation": "androidx.test.filters.FlakyTest" diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index 6f4abfdc05c1..6a03c17159d3 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -207,9 +207,10 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { final long identity = Binder.clearCallingIdentity(); try { if (rotation == UiAutomation.ROTATION_UNFREEZE) { - mWindowManager.thawRotation(); + mWindowManager.thawRotation(/* caller= */ "UiAutomationConnection#setRotation"); } else { - mWindowManager.freezeRotation(rotation); + mWindowManager.freezeRotation(rotation, + /* caller= */ "UiAutomationConnection#setRotation"); } return true; } catch (RemoteException re) { @@ -615,11 +616,13 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { if (mInitialFrozenRotation != INITIAL_FROZEN_ROTATION_UNSPECIFIED) { // Calling out with a lock held is fine since if the system // process is gone the client calling in will be killed. - mWindowManager.freezeRotation(mInitialFrozenRotation); + mWindowManager.freezeRotation(mInitialFrozenRotation, + /* caller= */ "UiAutomationConnection#restoreRotationStateLocked"); } else { // Calling out with a lock held is fine since if the system // process is gone the client calling in will be killed. - mWindowManager.thawRotation(); + mWindowManager.thawRotation( + /* caller= */ "UiAutomationConnection#restoreRotationStateLocked"); } } catch (RemoteException re) { /* ignore */ diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig new file mode 100644 index 000000000000..2076e85828a6 --- /dev/null +++ b/core/java/android/app/activity_manager.aconfig @@ -0,0 +1,8 @@ +package: "android.app" + +flag { + namespace: "system_performance" + name: "app_start_info" + description: "Control collecting of ApplicationStartInfo records and APIs." + bug: "247814855" +}
\ No newline at end of file diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 213e5cb4ad64..7704486b606c 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -10335,11 +10335,14 @@ public class DevicePolicyManager { * @return the current credential manager policy if null then this policy has not been * configured. */ + @UserHandleAware( + enabledSinceTargetSdkVersion = UPSIDE_DOWN_CAKE, + requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS) public @Nullable PackagePolicy getCredentialManagerPolicy() { throwIfParentInstance("getCredentialManagerPolicy"); if (mService != null) { try { - return mService.getCredentialManagerPolicy(); + return mService.getCredentialManagerPolicy(myUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index c49b820b9e37..58f9d57763fa 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -346,7 +346,7 @@ interface IDevicePolicyManager { boolean hasManagedProfileCallerIdAccess(int userId, String packageName); void setCredentialManagerPolicy(in PackagePolicy policy); - PackagePolicy getCredentialManagerPolicy(); + PackagePolicy getCredentialManagerPolicy(int userId); void setManagedProfileContactsAccessPolicy(in PackagePolicy policy); PackagePolicy getManagedProfileContactsAccessPolicy(); diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index ed0f872bf9bc..15bd1dcc3980 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -22,8 +22,10 @@ import android.os.PooledStringWriter; import android.os.RemoteException; import android.os.SystemClock; import android.service.autofill.FillRequest; +import android.text.InputType; import android.text.Spanned; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.Log; import android.util.Pair; import android.view.View; @@ -2452,7 +2454,7 @@ public class AssistStructure implements Parcelable { + node.getTextStyle()); Log.i(TAG, prefix + " Text color fg: #" + Integer.toHexString(node.getTextColor()) + ", bg: #" + Integer.toHexString(node.getTextBackgroundColor())); - Log.i(TAG, prefix + " Input type: " + node.getInputType()); + Log.i(TAG, prefix + " Input type: " + getInputTypeString(node.getInputType())); Log.i(TAG, prefix + " Resource id: " + node.getTextIdEntry()); } String webDomain = node.getWebDomain(); @@ -2664,4 +2666,33 @@ public class AssistStructure implements Parcelable { return new AssistStructure[size]; } }; + + private static final ArrayMap<Integer, String> INPUT_TYPE_VARIATIONS = new ArrayMap<>(); + static { + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, "EmailSubject"); + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS, "PostalAddress"); + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_PERSON_NAME, "PersonName"); + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_PASSWORD, "Password"); + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, "VisiblePassword"); + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_URI, "URI"); + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS, "WebEmailAddress"); + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD, "WebPassword"); + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE, "LongMessage"); + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE, "ShortMessage"); + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_FLAG_MULTI_LINE, "MultiLine"); + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE, "ImeMultiLine"); + INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_FILTER, "Filter"); + } + + private static String getInputTypeString(int inputType) { + StringBuilder sb = new StringBuilder(); + sb.append(inputType); + sb.append("(class=").append(inputType & InputType.TYPE_MASK_CLASS).append(')'); + for (int variation : INPUT_TYPE_VARIATIONS.keySet()) { + if ((variation & inputType) == variation) { + sb.append('|').append(INPUT_TYPE_VARIATIONS.get(variation)); + } + } + return sb.toString(); + } } diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index b4c740ec5599..0fa78c88c863 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -229,6 +229,7 @@ public final class VirtualDeviceParams implements Parcelable { @Nullable private final String mName; // Mapping of @PolicyType to @DevicePolicy @NonNull private final SparseIntArray mDevicePolicies; + @Nullable private final ComponentName mHomeComponent; @NonNull private final List<VirtualSensorConfig> mVirtualSensorConfigs; @Nullable private final IVirtualSensorCallback mVirtualSensorCallback; private final int mAudioPlaybackSessionId; @@ -243,6 +244,7 @@ public final class VirtualDeviceParams implements Parcelable { @NonNull Set<ComponentName> activityPolicyExemptions, @Nullable String name, @NonNull SparseIntArray devicePolicies, + @Nullable ComponentName homeComponent, @NonNull List<VirtualSensorConfig> virtualSensorConfigs, @Nullable IVirtualSensorCallback virtualSensorCallback, int audioPlaybackSessionId, @@ -258,6 +260,7 @@ public final class VirtualDeviceParams implements Parcelable { new ArraySet<>(Objects.requireNonNull(activityPolicyExemptions)); mName = name; mDevicePolicies = Objects.requireNonNull(devicePolicies); + mHomeComponent = homeComponent; mVirtualSensorConfigs = Objects.requireNonNull(virtualSensorConfigs); mVirtualSensorCallback = virtualSensorCallback; mAudioPlaybackSessionId = audioPlaybackSessionId; @@ -280,6 +283,7 @@ public final class VirtualDeviceParams implements Parcelable { IVirtualSensorCallback.Stub.asInterface(parcel.readStrongBinder()); mAudioPlaybackSessionId = parcel.readInt(); mAudioRecordingSessionId = parcel.readInt(); + mHomeComponent = parcel.readTypedObject(ComponentName.CREATOR); } /** @@ -291,6 +295,19 @@ public final class VirtualDeviceParams implements Parcelable { } /** + * Returns the custom component used as home on all displays owned by this virtual device that + * support home activities. + * + * @see Builder#setHomeComponent + */ + // TODO(b/297168328): Link to the relevant API for creating displays with home support. + @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) + @Nullable + public ComponentName getHomeComponent() { + return mHomeComponent; + } + + /** * Returns the user handles with matching managed accounts on the remote device to which * this virtual device is streaming. * @@ -468,6 +485,7 @@ public final class VirtualDeviceParams implements Parcelable { mVirtualSensorCallback != null ? mVirtualSensorCallback.asBinder() : null); dest.writeInt(mAudioPlaybackSessionId); dest.writeInt(mAudioRecordingSessionId); + dest.writeTypedObject(mHomeComponent, flags); } @Override @@ -508,7 +526,7 @@ public final class VirtualDeviceParams implements Parcelable { int hashCode = Objects.hash( mLockState, mUsersWithMatchingAccounts, mCrossTaskNavigationExemptions, mDefaultNavigationPolicy, mActivityPolicyExemptions, mDefaultActivityPolicy, mName, - mDevicePolicies, mAudioPlaybackSessionId, mAudioRecordingSessionId); + mDevicePolicies, mHomeComponent, mAudioPlaybackSessionId, mAudioRecordingSessionId); for (int i = 0; i < mDevicePolicies.size(); i++) { hashCode = 31 * hashCode + mDevicePolicies.keyAt(i); hashCode = 31 * hashCode + mDevicePolicies.valueAt(i); @@ -528,6 +546,7 @@ public final class VirtualDeviceParams implements Parcelable { + " mActivityPolicyExemptions=" + mActivityPolicyExemptions + " mName=" + mName + " mDevicePolicies=" + mDevicePolicies + + " mHomeComponent=" + mHomeComponent + " mAudioPlaybackSessionId=" + mAudioPlaybackSessionId + " mAudioRecordingSessionId=" + mAudioRecordingSessionId + ")"; @@ -588,6 +607,7 @@ public final class VirtualDeviceParams implements Parcelable { @Nullable private VirtualSensorCallback mVirtualSensorCallback; @Nullable private Executor mVirtualSensorDirectChannelCallbackExecutor; @Nullable private VirtualSensorDirectChannelCallback mVirtualSensorDirectChannelCallback; + @Nullable private ComponentName mHomeComponent; private static class VirtualSensorCallbackDelegate extends IVirtualSensorCallback.Stub { @NonNull @@ -665,6 +685,23 @@ public final class VirtualDeviceParams implements Parcelable { } /** + * Specifies a component to be used as home on all displays owned by this virtual device + * that support home activities. + * * + * <p>Note: Only relevant for virtual displays that support home activities.</p> + * + * @param homeComponent The component name to be used as home. If unset, then the system- + * default secondary home activity will be used. + */ + // TODO(b/297168328): Link to the relevant API for creating displays with home support. + @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) + @NonNull + public Builder setHomeComponent(@Nullable ComponentName homeComponent) { + mHomeComponent = homeComponent; + return this; + } + + /** * Sets the user handles with matching managed accounts on the remote device to which * this virtual device is streaming. The caller is responsible for verifying the presence * and legitimacy of a matching managed account on the remote device. @@ -1031,6 +1068,7 @@ public final class VirtualDeviceParams implements Parcelable { mActivityPolicyExemptions, mName, mDevicePolicies, + mHomeComponent, mVirtualSensorConfigs, virtualSensorCallbackDelegate, mAudioPlaybackSessionId, diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig index ee36f187b729..3e96c96d8d17 100644 --- a/core/java/android/companion/virtual/flags.aconfig +++ b/core/java/android/companion/virtual/flags.aconfig @@ -15,6 +15,13 @@ flag { } flag { + name: "vdm_custom_home" + namespace: "virtual_devices" + description: "Enable custom home API" + bug: "297168328" +} + +flag { name: "vdm_public_apis" namespace: "virtual_devices" description: "Enable public VDM API for device capabilities" diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING index 01a9373dea1f..addede4cc60c 100644 --- a/core/java/android/content/TEST_MAPPING +++ b/core/java/android/content/TEST_MAPPING @@ -7,7 +7,7 @@ "include-annotation": "android.platform.test.annotations.Presubmit" }, { - "exclude-annotation": "android.platform.test.annotations.LargeTest" + "exclude-annotation": "androidx.test.filters.LargeTest" }, { "exclude-annotation": "androidx.test.filters.FlakyTest" diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS index 0a7d079b8939..53dd3bffe06c 100644 --- a/core/java/android/content/pm/OWNERS +++ b/core/java/android/content/pm/OWNERS @@ -9,3 +9,4 @@ per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS per-file UserInfo* = file:/MULTIUSER_OWNERS per-file *UserProperties* = file:/MULTIUSER_OWNERS +per-file *multiuser* = file:/MULTIUSER_OWNERS diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig new file mode 100644 index 000000000000..07ff7be00114 --- /dev/null +++ b/core/java/android/content/pm/multiuser.aconfig @@ -0,0 +1,8 @@ +package: "android.multiuser" + +flag { + name: "save_global_and_guest_restrictions_on_system_user_xml" + namespace: "multiuser" + description: "Save guest and device policy global restrictions on the SYSTEM user's XML file." + bug: "301067944" +} diff --git a/core/java/android/database/sqlite/package.html b/core/java/android/database/sqlite/package.html index 2c366466f9df..f2df5368494e 100644 --- a/core/java/android/database/sqlite/package.html +++ b/core/java/android/database/sqlite/package.html @@ -15,7 +15,7 @@ instead use the generic {@link android.database} classes. <a href="{@docRoot}studio/command-line/sqlite3.html">sqlite3</a> command-line database tool. On your development machine, run the tool from the <code>platform-tools/</code> folder of your SDK. On the emulator, run the tool -with adb shell, for example, <code>adb -e shell sqlite3</code>. +with adb shell, for example, <code>adb shell sqlite3</code>. <p>The version of SQLite depends on the version of Android. See the following table: <table style="width:auto;"> @@ -42,15 +42,19 @@ with adb shell, for example, <code>adb -e shell sqlite3</code>. <ul> <li>If available, use the sqlite3 tool, for example: - <code>adb -e shell sqlite3 --version</code>.</li> + <code>adb shell sqlite3 --version</code>.</li> <li>Create and query an in-memory database as shown in the following code sample: <pre> String query = "select sqlite_version() AS sqlite_version"; SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(":memory:", null); Cursor cursor = db.rawQuery(query, null); String sqliteVersion = ""; - if (cursor.moveToNext()) { - sqliteVersion = cursor.getString(0); + try { + if (cursor.moveToNext()) { + sqliteVersion = cursor.getString(0); + } + } finally { + cursor.close(); }</pre> </li> </ul> diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 943f0c4bba0a..89f889f988e2 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1407,6 +1407,74 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Integer>("android.flash.info.strengthDefaultLevel", int.class); /** + * <p>Maximum flash brightness level for manual flash control in SINGLE mode.</p> + * <p>Maximum flash brightness level in camera capture mode and + * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to SINGLE. + * Value will be > 1 if the manual flash strength control feature is supported, + * otherwise the value will be equal to 1. + * Note that this level is just a number of supported levels (the granularity of control). + * There is no actual physical power units tied to this level.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#FLASH_MODE + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_CAMERA_MANUAL_FLASH_STRENGTH_CONTROL) + public static final Key<Integer> FLASH_SINGLE_STRENGTH_MAX_LEVEL = + new Key<Integer>("android.flash.singleStrengthMaxLevel", int.class); + + /** + * <p>Default flash brightness level for manual flash control in SINGLE mode.</p> + * <p>If flash unit is available this will be greater than or equal to 1 and less + * or equal to <code>android.flash.info.singleStrengthMaxLevel</code>. + * Note for devices that do not support the manual flash strength control + * feature, this level will always be equal to 1.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_CAMERA_MANUAL_FLASH_STRENGTH_CONTROL) + public static final Key<Integer> FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL = + new Key<Integer>("android.flash.singleStrengthDefaultLevel", int.class); + + /** + * <p>Maximum flash brightness level for manual flash control in TORCH mode</p> + * <p>Maximum flash brightness level in camera capture mode and + * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to TORCH. + * Value will be > 1 if the manual flash strength control feature is supported, + * otherwise the value will be equal to 1.</p> + * <p>Note that this level is just a number of supported levels(the granularity of control). + * There is no actual physical power units tied to this level. + * There is no relation between android.flash.info.torchStrengthMaxLevel and + * android.flash.info.singleStrengthMaxLevel i.e. the ratio of + * android.flash.info.torchStrengthMaxLevel:android.flash.info.singleStrengthMaxLevel + * is not guaranteed to be the ratio of actual brightness.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#FLASH_MODE + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_CAMERA_MANUAL_FLASH_STRENGTH_CONTROL) + public static final Key<Integer> FLASH_TORCH_STRENGTH_MAX_LEVEL = + new Key<Integer>("android.flash.torchStrengthMaxLevel", int.class); + + /** + * <p>Default flash brightness level for manual flash control in TORCH mode</p> + * <p>If flash unit is available this will be greater than or equal to 1 and less + * or equal to android.flash.info.torchStrengthMaxLevel. + * Note for the devices that do not support the manual flash strength control feature, + * this level will always be equal to 1.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_CAMERA_MANUAL_FLASH_STRENGTH_CONTROL) + public static final Key<Integer> FLASH_TORCH_STRENGTH_DEFAULT_LEVEL = + new Key<Integer>("android.flash.torchStrengthDefaultLevel", int.class); + + /** * <p>List of hot pixel correction modes for {@link CaptureRequest#HOT_PIXEL_MODE android.hotPixel.mode} that are supported by this * camera device.</p> * <p>FULL mode camera devices will always support FAST.</p> diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index dd3238414030..9421359d8fc7 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -2654,6 +2654,46 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.flash.mode", int.class); /** + * <p>Flash strength level to be used when manual flash control is active.</p> + * <p>Flash strength level to use in capture mode i.e. when the applications control + * flash with either SINGLE or TORCH mode.</p> + * <p>Use android.flash.info.singleStrengthMaxLevel and + * android.flash.info.torchStrengthMaxLevel to check whether the device supports + * flash strength control or not. + * If the values of android.flash.info.singleStrengthMaxLevel and + * android.flash.info.torchStrengthMaxLevel are greater than 1, + * then the device supports manual flash strength control.</p> + * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> TORCH the value must be >= 1 + * and <= android.flash.info.torchStrengthMaxLevel. + * If the application doesn't set the key and + * android.flash.info.torchStrengthMaxLevel > 1, + * then the flash will be fired at the default level set by HAL in + * android.flash.info.torchStrengthDefaultLevel. + * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> SINGLE, then the value must be >= 1 + * and <= android.flash.info.singleStrengthMaxLevel. + * If the application does not set this key and + * android.flash.info.singleStrengthMaxLevel > 1, + * then the flash will be fired at the default level set by HAL + * in android.flash.info.singleStrengthDefaultLevel. + * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH, + * ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.</p> + * <p><b>Range of valid values:</b><br> + * <code>[1-android.flash.info.torchStrengthMaxLevel]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is + * set to TORCH; + * <code>[1-android.flash.info.singleStrengthMaxLevel]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is + * set to SINGLE</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#FLASH_MODE + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_CAMERA_MANUAL_FLASH_STRENGTH_CONTROL) + public static final Key<Integer> FLASH_STRENGTH_LEVEL = + new Key<Integer>("android.flash.strengthLevel", int.class); + + /** * <p>Operational mode for hot pixel correction.</p> * <p>Hotpixel correction interpolates out, or otherwise removes, pixels * that do not accurately measure the incoming light (i.e. pixels that diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 9a800150ef27..4606e5b23900 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2940,6 +2940,46 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.flash.state", int.class); /** + * <p>Flash strength level to be used when manual flash control is active.</p> + * <p>Flash strength level to use in capture mode i.e. when the applications control + * flash with either SINGLE or TORCH mode.</p> + * <p>Use android.flash.info.singleStrengthMaxLevel and + * android.flash.info.torchStrengthMaxLevel to check whether the device supports + * flash strength control or not. + * If the values of android.flash.info.singleStrengthMaxLevel and + * android.flash.info.torchStrengthMaxLevel are greater than 1, + * then the device supports manual flash strength control.</p> + * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> TORCH the value must be >= 1 + * and <= android.flash.info.torchStrengthMaxLevel. + * If the application doesn't set the key and + * android.flash.info.torchStrengthMaxLevel > 1, + * then the flash will be fired at the default level set by HAL in + * android.flash.info.torchStrengthDefaultLevel. + * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> SINGLE, then the value must be >= 1 + * and <= android.flash.info.singleStrengthMaxLevel. + * If the application does not set this key and + * android.flash.info.singleStrengthMaxLevel > 1, + * then the flash will be fired at the default level set by HAL + * in android.flash.info.singleStrengthDefaultLevel. + * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH, + * ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.</p> + * <p><b>Range of valid values:</b><br> + * <code>[1-android.flash.info.torchStrengthMaxLevel]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is + * set to TORCH; + * <code>[1-android.flash.info.singleStrengthMaxLevel]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is + * set to SINGLE</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#FLASH_MODE + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_CAMERA_MANUAL_FLASH_STRENGTH_CONTROL) + public static final Key<Integer> FLASH_STRENGTH_LEVEL = + new Key<Integer>("android.flash.strengthLevel", int.class); + + /** * <p>Operational mode for hot pixel correction.</p> * <p>Hotpixel correction interpolates out, or otherwise removes, pixels * that do not accurately measure the incoming light (i.e. pixels that diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index f0c87a138a98..990ebc5fdbcd 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -27,6 +27,7 @@ import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; @@ -364,11 +365,20 @@ public final class DisplayManager { /** * Virtual display flag: Indicates that the orientation of this display device is coupled to - * the rotation of its associated logical display. + * the orientation of its associated logical display. + * <p> + * The flag should not be set when the physical display is mounted in a fixed orientation + * such as on a desk. Without this flag, display manager will apply a coordinate transformation + * such as a scale and translation to letterbox or pillarbox format under the assumption that + * the physical orientation of the display is invariant. With this flag set, the content will + * rotate to fill in the space of the display, as it does on the internal device display. + * </p> * * @see #createVirtualDisplay * @hide */ + @SuppressLint("UnflaggedApi") + @SystemApi public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7; /** diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index b1aa7deeb4ef..4700720736b5 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -32,6 +32,7 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.RefreshRateRange; import android.view.SurfaceControl.Transaction; import android.window.DisplayWindowPolicyController; +import android.window.ScreenCapture; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -111,6 +112,25 @@ public abstract class DisplayManagerInternal { public abstract void unregisterDisplayGroupListener(DisplayGroupListener listener); /** + * Screenshot for internal system-only use such as rotation, etc. This method includes + * secure layers and the result should never be exposed to non-system applications. + * This method does not apply any rotation and provides the output in natural orientation. + * + * @param displayId The display id to take the screenshot of. + * @return The buffer or null if we have failed. + */ + public abstract ScreenCapture.ScreenshotHardwareBuffer systemScreenshot(int displayId); + + /** + * General screenshot functionality that excludes secure layers and applies appropriate + * rotation that the device is currently in. + * + * @param displayId The display id to take the screenshot of. + * @return The buffer or null if we have failed. + */ + public abstract ScreenCapture.ScreenshotHardwareBuffer userScreenshot(int displayId); + + /** * Returns information about the specified logical display. * * @param displayId The logical display id. diff --git a/core/java/android/hardware/radio/flags.aconfig b/core/java/android/hardware/radio/flags.aconfig new file mode 100644 index 000000000000..dbc1a4b21cfb --- /dev/null +++ b/core/java/android/hardware/radio/flags.aconfig @@ -0,0 +1,8 @@ +package: "android.hardware.radio" + +flag { + name: "hd_radio_improved" + namespace: "car_framework" + description: "Feature flag for improved HD radio support with less vendor extensions" + bug: "280300929" +} diff --git a/core/java/android/net/network-policy-restrictions.md b/core/java/android/net/network-policy-restrictions.md index 04c658c39ad3..20f3d747cf0c 100644 --- a/core/java/android/net/network-policy-restrictions.md +++ b/core/java/android/net/network-policy-restrictions.md @@ -29,8 +29,8 @@ More specifically: | **DS** | *AL* | ok | blk | ok | ok | | **ON** | *!AL* | blk | blk | blk | blk | | | *DL* | blk | blk | blk | blk | -| **DS** | *AL* | blk | blk | ok | ok | -| **OFF** | *!AL* | blk | blk | ok | ok | +| **DS** | *AL* | ok | blk | ok | ok | +| **OFF** | *!AL* | ok | blk | ok | ok | | | *DL* | blk | blk | blk | blk | diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index ada55325aded..f817fb8dcaed 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -32,7 +32,9 @@ import java.io.FileDescriptor; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -613,15 +615,35 @@ public final class BinderProxy implements IBinder { */ public native boolean transactNative(int code, Parcel data, Parcel reply, int flags) throws RemoteException; + + /* This list is to hold strong reference to the death recipients that are waiting for the death + * of binder that this proxy references. Previously, the death recipients were strongy + * referenced from JNI, but that can cause memory leak (b/298374304) when the application has a + * strong reference from the death recipient to the proxy object. The JNI reference is now weak. + * And this strong reference is to keep death recipients at least until the proxy is GC'ed. */ + private List<DeathRecipient> mDeathRecipients = Collections.synchronizedList(new ArrayList<>()); + /** * See {@link IBinder#linkToDeath(DeathRecipient, int)} */ - public native void linkToDeath(DeathRecipient recipient, int flags) - throws RemoteException; + public void linkToDeath(DeathRecipient recipient, int flags) + throws RemoteException { + linkToDeathNative(recipient, flags); + mDeathRecipients.add(recipient); + } + /** * See {@link IBinder#unlinkToDeath} */ - public native boolean unlinkToDeath(DeathRecipient recipient, int flags); + public boolean unlinkToDeath(DeathRecipient recipient, int flags) { + mDeathRecipients.remove(recipient); + return unlinkToDeathNative(recipient, flags); + } + + private native void linkToDeathNative(DeathRecipient recipient, int flags) + throws RemoteException; + + private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags); /** * Perform a dump on the remote object diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java index f10467f0760e..47ad72fe8d0e 100644 --- a/core/java/android/os/BugreportParams.java +++ b/core/java/android/os/BugreportParams.java @@ -124,6 +124,8 @@ public final class BugreportParams { /** * Options for a lightweight bugreport intended to be taken for onboarding-related flows. + * + * @hide */ public static final int BUGREPORT_MODE_ONBOARDING = IDumpstate.BUGREPORT_MODE_ONBOARDING; diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING index 60622f18fe3b..ad3abd9b531c 100644 --- a/core/java/android/os/TEST_MAPPING +++ b/core/java/android/os/TEST_MAPPING @@ -7,7 +7,7 @@ ], "name": "FrameworksVibratorCoreTests", "options": [ - {"exclude-annotation": "android.platform.test.annotations.LargeTest"}, + {"exclude-annotation": "androidx.test.filters.LargeTest"}, {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"}, {"exclude-annotation": "org.junit.Ignore"} @@ -20,7 +20,7 @@ ], "name": "FrameworksVibratorServicesTests", "options": [ - {"exclude-annotation": "android.platform.test.annotations.LargeTest"}, + {"exclude-annotation": "androidx.test.filters.LargeTest"}, {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"}, {"exclude-annotation": "org.junit.Ignore"} @@ -33,7 +33,7 @@ ], "name": "CtsVibratorTestCases", "options": [ - {"exclude-annotation": "android.platform.test.annotations.LargeTest"}, + {"exclude-annotation": "androidx.test.filters.LargeTest"}, {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"}, {"exclude-annotation": "org.junit.Ignore"} diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index febe6f719baf..a95e66d9eb04 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -16,7 +16,7 @@ flag { flag { name: "allow_private_profile" - namespace: "private_profile" + namespace: "profile_experiences" description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion." bug: "299069460" } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c19c20c8429d..e829ca288925 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -687,7 +687,8 @@ public final class Settings { "com.android.settings.MONITORING_CERT_INFO"; /** - * Activity Action: Show settings to allow configuration of privacy options. + * Activity Action: Show settings to allow configuration of privacy options, i.e. permission + * manager, privacy dashboard, privacy controls and more. * <p> * In some cases, a matching Activity may not exist, so ensure you * safeguard against this. @@ -701,6 +702,21 @@ public final class Settings { "android.settings.PRIVACY_SETTINGS"; /** + * Activity Action: Show privacy controls sub-page, i.e. privacy (camera/mic) toggles and more. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_PRIVACY_CONTROLS = + "android.settings.PRIVACY_CONTROLS"; + + /** * Activity Action: Show settings to allow configuration of VPN. * <p> * In some cases, a matching Activity may not exist, so ensure you diff --git a/core/java/android/security/FileIntegrityManager.java b/core/java/android/security/FileIntegrityManager.java index d6f3bf334456..dae3202b2043 100644 --- a/core/java/android/security/FileIntegrityManager.java +++ b/core/java/android/security/FileIntegrityManager.java @@ -49,9 +49,18 @@ public final class FileIntegrityManager { } /** - * Returns true if APK Verity is supported on the device. When supported, an APK can be - * installed with a fs-verity signature (if verified with trusted App Source Certificate) for - * continuous on-access verification. + * Returns whether fs-verity is supported on the device. fs-verity provides on-access + * verification, although the app APIs are only made available to apps in a later SDK version. + * Only when this method returns true, the other fs-verity APIs in the same class can succeed. + * + * <p>The app may not need this method and just call the other APIs (i.e. {@link + * #setupFsVerity(File)} and {@link #getFsVerityDigest(File)}) normally and handle any failure. + * If some app feature really depends on fs-verity (e.g. protecting integrity of a large file + * download), an early check of support status may avoid any cost if it is to fail late. + * + * <p>Note: for historical reasons this is named {@code isApkVeritySupported()} instead of + * {@code isFsVeritySupported()}. It has also been available since API level 30, predating the + * other fs-verity APIs. */ public boolean isApkVeritySupported() { try { @@ -67,28 +76,38 @@ public final class FileIntegrityManager { * Enables fs-verity to the owned file under the calling app's private directory. It always uses * the common configuration, i.e. SHA-256 digest algorithm, 4K block size, and without salt. * - * The operation can only succeed when the file is not opened as writable by any process. + * <p>For enabling fs-verity to succeed, the device must support fs-verity, the file must be + * writable by the app and not already have fs-verity enabled, and the file must not currently + * be open for writing by any process. To check whether the device supports fs-verity, use + * {@link #isApkVeritySupported()}. * - * It takes O(file size) time to build the underlying data structure for continuous + * <p>It takes O(file size) time to build the underlying data structure for continuous * verification. The operation is atomic, i.e. it's either enabled or not, even in case of * power failure during or after the call. * - * Note for the API users: When the file's authenticity is crucial, the app typical needs to + * <p>Note for the API users: When the file's authenticity is crucial, the app typical needs to * perform a signature check by itself before using the file. The signature is often delivered * as a separate file and stored next to the targeting file in the filesystem. The public key of * the signer (normally the same app developer) can be put in the APK, and the app can use the * public key to verify the signature to the file's actual fs-verity digest (from {@link - * #getFsVerityDigest}) before using the file. The exact format is not prescribed by the + * #getFsVerityDigest(File)}) before using the file. The exact format is not prescribed by the * framework. App developers may choose to use common practices like JCA for the signing and * verification, or their own preferred approach. * - * @param file The file to enable fs-verity. It should be an absolute path. + * @param file The file to enable fs-verity. It must represent an absolute path. + * @throws IllegalArgumentException If the provided file is not an absolute path. + * @throws IOException If the operation failed. * * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a> */ @FlaggedApi(Flags.FLAG_FSVERITY_API) public void setupFsVerity(@NonNull File file) throws IOException { if (!file.isAbsolute()) { + // fs-verity is to be enabled by installd, which enforces the validation to the + // (untrusted) file path passed from here. To make this less error prone, installd + // accepts only absolute path. When a relative path is provided, we fail with an + // explicit exception to help developers understand the requirement to use an absolute + // path. throw new IllegalArgumentException("Expect an absolute path"); } IFsveritySetupAuthToken authToken; @@ -112,11 +131,12 @@ public final class FileIntegrityManager { } /** - * Returns the fs-verity digest for the owned file under the calling app's - * private directory, or null when the file does not have fs-verity enabled. + * Returns the fs-verity digest for the owned file under the calling app's private directory, or + * null when the file does not have fs-verity enabled (including when fs-verity is not supported + * on older devices). * * @param file The file to measure the fs-verity digest. - * @return The fs-verity digeset in byte[], null if none. + * @return The fs-verity digest in byte[], null if none. * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a> */ @FlaggedApi(Flags.FLAG_FSVERITY_API) diff --git a/core/java/android/service/notification/TEST_MAPPING b/core/java/android/service/notification/TEST_MAPPING index 59b2bc1e4f73..7b8d52f51cee 100644 --- a/core/java/android/service/notification/TEST_MAPPING +++ b/core/java/android/service/notification/TEST_MAPPING @@ -13,9 +13,6 @@ "exclude-annotation": "org.junit.Ignore" }, { - "exclude-annotation": "android.platform.test.annotations.LargeTest" - }, - { "exclude-annotation": "androidx.test.filters.LargeTest" } ] @@ -33,9 +30,6 @@ "exclude-annotation": "org.junit.Ignore" }, { - "exclude-annotation": "android.platform.test.annotations.LargeTest" - }, - { "exclude-annotation": "androidx.test.filters.LargeTest" } ] diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 447c3bcdc186..4e3deb61761e 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -2495,8 +2495,14 @@ public class TextUtils { // Even if the argument name is `ellipsizeDip`, the unit of this argument is pixels. final int charCount = (int) ((ellipsizeDip + 0.5f) / assumedCharWidthInPx); - return TextUtils.trimToSize(gettingCleaned.toString(), charCount) - + getEllipsisString(TruncateAt.END); + + final String text = gettingCleaned.toString(); + if (TextUtils.isEmpty(text) || text.length() <= charCount) { + return text; + } else { + return TextUtils.trimToSize(text, charCount) + + getEllipsisString(TruncateAt.END); + } } else { // Truncate final TextPaint paint = new TextPaint(); diff --git a/core/java/android/text/style/LineBreakConfigSpan.java b/core/java/android/text/style/LineBreakConfigSpan.java index b8033a9d6fe1..25c1db4d9804 100644 --- a/core/java/android/text/style/LineBreakConfigSpan.java +++ b/core/java/android/text/style/LineBreakConfigSpan.java @@ -35,6 +35,7 @@ public class LineBreakConfigSpan { * Construct a new {@link LineBreakConfigSpan} * @param lineBreakConfig a line break config */ + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public LineBreakConfigSpan(@NonNull LineBreakConfig lineBreakConfig) { mLineBreakConfig = lineBreakConfig; } @@ -43,6 +44,7 @@ public class LineBreakConfigSpan { * Gets an associated line break config. * @return associated line break config. */ + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public @NonNull LineBreakConfig getLineBreakConfig() { return mLineBreakConfig; } diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java index a46136a53e95..31d759ea92e6 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -264,6 +264,16 @@ public abstract class DisplayEventReceiver { } /** + * Called when a display hotplug event with connection error is received. + * + * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()} + * timebase. + * @param connectionError the hotplug connection error code. + */ + public void onHotplugConnectionError(long timestampNanos, int connectionError) { + } + + /** * Called when a display mode changed event is received. * * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()} @@ -345,6 +355,11 @@ public abstract class DisplayEventReceiver { onHotplug(timestampNanos, physicalDisplayId, connected); } + @SuppressWarnings("unused") + private void dispatchHotplugConnectionError(long timestampNanos, int connectionError) { + onHotplugConnectionError(timestampNanos, connectionError); + } + // Called from native code. @SuppressWarnings("unused") private void dispatchModeChanged(long timestampNanos, long physicalDisplayId, int modeId, diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 512f4f2b5d22..981911ec8880 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.Display.Mode.INVALID_MODE_ID; import static android.view.DisplayInfoProto.APP_HEIGHT; import static android.view.DisplayInfoProto.APP_WIDTH; import static android.view.DisplayInfoProto.CUTOUT; @@ -200,6 +201,11 @@ public final class DisplayInfo implements Parcelable { public int defaultModeId; /** + * The user preferred display mode. + */ + public int userPreferredModeId = INVALID_MODE_ID; + + /** * The supported modes of this display. */ public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY; @@ -420,6 +426,7 @@ public final class DisplayInfo implements Parcelable { && modeId == other.modeId && renderFrameRate == other.renderFrameRate && defaultModeId == other.defaultModeId + && userPreferredModeId == other.userPreferredModeId && Arrays.equals(supportedModes, other.supportedModes) && colorMode == other.colorMode && Arrays.equals(supportedColorModes, other.supportedColorModes) @@ -478,6 +485,7 @@ public final class DisplayInfo implements Parcelable { modeId = other.modeId; renderFrameRate = other.renderFrameRate; defaultModeId = other.defaultModeId; + userPreferredModeId = other.userPreferredModeId; supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length); colorMode = other.colorMode; supportedColorModes = Arrays.copyOf( @@ -530,6 +538,7 @@ public final class DisplayInfo implements Parcelable { modeId = source.readInt(); renderFrameRate = source.readFloat(); defaultModeId = source.readInt(); + userPreferredModeId = source.readInt(); int nModes = source.readInt(); supportedModes = new Display.Mode[nModes]; for (int i = 0; i < nModes; i++) { @@ -596,6 +605,7 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(modeId); dest.writeFloat(renderFrameRate); dest.writeInt(defaultModeId); + dest.writeInt(userPreferredModeId); dest.writeInt(supportedModes.length); for (int i = 0; i < supportedModes.length; i++) { supportedModes[i].writeToParcel(dest, flags); @@ -832,9 +842,12 @@ public final class DisplayInfo implements Parcelable { sb.append(presentationDeadlineNanos); sb.append(", mode "); sb.append(modeId); + sb.append(", renderFrameRate "); sb.append(renderFrameRate); sb.append(", defaultMode "); sb.append(defaultModeId); + sb.append(", userPreferredModeId "); + sb.append(userPreferredModeId); sb.append(", modes "); sb.append(Arrays.toString(supportedModes)); sb.append(", hdrCapabilities "); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index d3b7a5be47ba..cccac95b9caa 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -316,14 +316,14 @@ interface IWindowManager * android.view.Display#DEFAULT_DISPLAY} and given rotation. */ @UnsupportedAppUsage - void freezeRotation(int rotation); + void freezeRotation(int rotation, String caller); /** * Equivalent to calling {@link #thawDisplayRotation(int)} with {@link * android.view.Display#DEFAULT_DISPLAY}. */ @UnsupportedAppUsage - void thawRotation(); + void thawRotation(String caller); /** * Equivelant to call {@link #isDisplayRotationFrozen(int)} with {@link @@ -341,7 +341,7 @@ interface IWindowManager * {@link android.view.Surface#ROTATION_270} or -1 to freeze it to current rotation. * @hide */ - void freezeDisplayRotation(int displayId, int rotation); + void freezeDisplayRotation(int displayId, int rotation, String caller); /** * Release the orientation lock imposed by freezeRotation() on the display. @@ -349,7 +349,7 @@ interface IWindowManager * @param displayId the ID of display which rotation should be thawed. * @hide */ - void thawDisplayRotation(int displayId); + void thawDisplayRotation(int displayId, String caller); /** * Gets whether the rotation is frozen on the display. diff --git a/core/java/android/view/TEST_MAPPING b/core/java/android/view/TEST_MAPPING index 1e39716988a9..db3590814e08 100644 --- a/core/java/android/view/TEST_MAPPING +++ b/core/java/android/view/TEST_MAPPING @@ -10,7 +10,7 @@ "include-annotation": "android.platform.test.annotations.Presubmit" }, { - "exclude-annotation": "android.platform.test.annotations.LargeTest" + "exclude-annotation": "androidx.test.filters.LargeTest" }, { "exclude-annotation": "androidx.test.filters.FlakyTest" diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index ff6165b8a3ba..f4213510a1c1 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1151,14 +1151,10 @@ public final class ViewRootImpl implements ViewParent, } private boolean isInTouchMode() { - IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService(); - if (windowManager != null) { - try { - return windowManager.isInTouchMode(getDisplayId()); - } catch (RemoteException e) { - } + if (mAttachInfo == null) { + return mContext.getResources().getBoolean(R.bool.config_defaultInTouchMode); } - return false; + return mAttachInfo.mInTouchMode; } /** @@ -3862,9 +3858,7 @@ public final class ViewRootImpl implements ViewParent, mPendingTransitions.clear(); } - if (mActiveSurfaceSyncGroup != null) { - mActiveSurfaceSyncGroup.markSyncReady(); - } + handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction); } else if (cancelAndRedraw) { mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener ? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason() @@ -3878,8 +3872,8 @@ public final class ViewRootImpl implements ViewParent, } mPendingTransitions.clear(); } - if (!performDraw(mActiveSurfaceSyncGroup) && mActiveSurfaceSyncGroup != null) { - mActiveSurfaceSyncGroup.markSyncReady(); + if (!performDraw(mActiveSurfaceSyncGroup)) { + handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction); } } @@ -3894,6 +3888,7 @@ public final class ViewRootImpl implements ViewParent, mReportNextDraw = false; mLastReportNextDrawReason = null; mActiveSurfaceSyncGroup = null; + mHasPendingTransactions = false; mSyncBuffer = false; if (isInWMSRequestedSync()) { mWmsRequestSyncGroup.markSyncReady(); @@ -4692,7 +4687,8 @@ public final class ViewRootImpl implements ViewParent, return false; } - final boolean fullRedrawNeeded = mFullRedrawNeeded || surfaceSyncGroup != null; + final boolean fullRedrawNeeded = + mFullRedrawNeeded || surfaceSyncGroup != null || mHasPendingTransactions; mFullRedrawNeeded = false; mIsDrawing = true; @@ -4722,8 +4718,15 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mPendingAnimatingRenderNodes.clear(); } - if (mReportNextDraw) { + final Transaction pendingTransaction; + if (!usingAsyncReport && mHasPendingTransactions) { + pendingTransaction = new Transaction(); + pendingTransaction.merge(mPendingTransaction); + } else { + pendingTransaction = null; + } + if (mReportNextDraw) { // if we're using multi-thread renderer, wait for the window frame draws if (mWindowDrawCountDown != null) { try { @@ -4745,9 +4748,7 @@ public final class ViewRootImpl implements ViewParent, if (mSurfaceHolder != null && mSurface.isValid()) { usingAsyncReport = true; SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> { - if (surfaceSyncGroup != null) { - surfaceSyncGroup.markSyncReady(); - } + handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction); }); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); @@ -4760,15 +4761,27 @@ public final class ViewRootImpl implements ViewParent, } } - if (surfaceSyncGroup != null && !usingAsyncReport) { - surfaceSyncGroup.markSyncReady(); + if (!usingAsyncReport) { + handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction); } + if (mPerformContentCapture) { performContentCaptureInitialReport(); } return true; } + private void handleSyncRequestWhenNoAsyncDraw(SurfaceSyncGroup surfaceSyncGroup, + @Nullable Transaction pendingTransaction) { + if (surfaceSyncGroup != null) { + if (pendingTransaction != null) { + surfaceSyncGroup.addTransaction(pendingTransaction); + } + surfaceSyncGroup.markSyncReady(); + } else if (pendingTransaction != null) { + pendingTransaction.apply(); + } + } /** * Checks (and caches) if content capture is enabled for this context. */ @@ -4854,8 +4867,8 @@ public final class ViewRootImpl implements ViewParent, } } - private boolean draw(boolean fullRedrawNeeded, - @Nullable SurfaceSyncGroup activeSyncGroup, boolean syncBuffer) { + private boolean draw(boolean fullRedrawNeeded, @Nullable SurfaceSyncGroup activeSyncGroup, + boolean syncBuffer) { Surface surface = mSurface; if (!surface.isValid()) { return false; @@ -4999,12 +5012,11 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mThreadedRenderer.forceDrawNextFrame(); } } else if (mHasPendingTransactions) { - // Register a calback if there's no sync involved but there were calls to + // Register a callback if there's no sync involved but there were calls to // applyTransactionOnDraw. If there is a sync involved, the sync callback will // handle merging the pending transaction. registerCallbackForPendingTransactions(); } - mHasPendingTransactions = false; mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this); } else { @@ -8981,13 +8993,7 @@ public final class ViewRootImpl implements ViewParent, mAdded = false; AnimationHandler.removeRequestor(this); } - if (mActiveSurfaceSyncGroup != null) { - mActiveSurfaceSyncGroup.markSyncReady(); - mActiveSurfaceSyncGroup = null; - } - if (mHasPendingTransactions) { - mPendingTransaction.apply(); - } + handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction); WindowManagerGlobal.getInstance().doRemoveView(this); } @@ -11506,9 +11512,7 @@ public final class ViewRootImpl implements ViewParent, Log.d(mTag, "registerCallbacksForSync syncBuffer=" + syncBuffer); } - Transaction t = new Transaction(); - t.merge(mPendingTransaction); - + surfaceSyncGroup.addTransaction(mPendingTransaction); mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() { @Override public void onFrameDraw(long frame) { @@ -11522,7 +11526,6 @@ public final class ViewRootImpl implements ViewParent, + frame + "."); } - mergeWithNextTransaction(t, frame); // If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or // SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up // any blast sync or commit callback, and the code should directly call diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java index 2747233a4bf1..8d71a8e998bd 100644 --- a/core/java/android/window/DisplayWindowPolicyController.java +++ b/core/java/android/window/DisplayWindowPolicyController.java @@ -109,6 +109,12 @@ public abstract class DisplayWindowPolicyController { } /** + * @return the custom home component specified for the relevant display, if any. + */ + @Nullable + public abstract ComponentName getCustomHomeComponent(); + + /** * Returns {@code true} if all of the given activities can be launched on this virtual display * in the configuration defined by the rest of the arguments. * diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java index 9b10a7ff5d12..932608a3b57b 100644 --- a/core/java/android/window/TransitionRequestInfo.java +++ b/core/java/android/window/TransitionRequestInfo.java @@ -16,6 +16,8 @@ package android.window; +import static android.view.WindowManager.transitTypeToString; + import android.annotation.Nullable; import android.app.ActivityManager; import android.app.WindowConfiguration; @@ -88,6 +90,11 @@ public final class TransitionRequestInfo implements Parcelable { this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags); } + /** @hide */ + String typeToString() { + return transitTypeToString(mType); + } + /** Requested change to a display. */ @DataClass(genToString = true, genSetters = true, genBuilder = false, genConstructor = false) public static class DisplayChange implements Parcelable { @@ -263,7 +270,7 @@ public final class TransitionRequestInfo implements Parcelable { }; @DataClass.Generated( - time = 1693425051905L, + time = 1695667226050L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java", inputSignatures = "private final int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate int mStartRotation\nprivate int mEndRotation\nprivate boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)") @@ -298,11 +305,11 @@ public final class TransitionRequestInfo implements Parcelable { * @param type * The type of the transition being requested. * @param triggerTask - * If non-null, If non-null, the task containing the activity whose lifecycle change (start or + * If non-null, the task containing the activity whose lifecycle change (start or * finish) has caused this transition to occur. * @param pipTask - * If non-null, If non-null, the task containing the activity whose lifecycle change (start or - * finish) has caused this transition to occur. + * If non-null, the task containing the pip activity that participates in this + * transition. * @param remoteTransition * If non-null, a remote-transition associated with the source of this transition. * @param displayChange @@ -431,7 +438,7 @@ public final class TransitionRequestInfo implements Parcelable { // String fieldNameToString() { ... } return "TransitionRequestInfo { " + - "type = " + mType + ", " + + "type = " + typeToString() + ", " + "triggerTask = " + mTriggerTask + ", " + "pipTask = " + mPipTask + ", " + "remoteTransition = " + mRemoteTransition + ", " + @@ -506,10 +513,10 @@ public final class TransitionRequestInfo implements Parcelable { }; @DataClass.Generated( - time = 1693425051928L, + time = 1695667226088L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java", - inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)") + inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\n java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)") @Deprecated private void __metadata() {} diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index 0d1871d379fa..663067ce7ae6 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -203,14 +203,22 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { } return false; } - + + /** + * Direct reflection of {@link Intent#ACTION_PACKAGE_CHANGED + * Intent.ACTION_PACKAGE_CHANGED} being received, this callback + * has extras passed in. + */ + public void onPackageChangedWithExtras(String packageName, Bundle extras) { + } + public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { return false; } public void onHandleUserStop(Intent intent, int userHandle) { } - + public void onUidRemoved(int uid) { } @@ -238,21 +246,34 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { } /** + * Called when a package disappears with extras passed in. + */ + public void onPackageDisappearedWithExtras(String packageName, Bundle extras) { + } + + /** * Called when a package appears for any reason. */ public void onPackageAppeared(String packageName, int reason) { } + + /** + * Called when a package appears with extras passed in. + */ + public void onPackageAppearedWithExtras(String packageName, Bundle extras) { + } + /** * Called when an existing package is updated or its disabled state changes. */ public void onPackageModified(@NonNull String packageName) { } - + public boolean didSomePackagesChange() { return mSomePackagesChanged; } - + public int isPackageAppearing(String packageName) { if (mAppearingPackages != null) { for (int i=mAppearingPackages.length-1; i>=0; i--) { @@ -381,6 +402,7 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { mChangeType = PACKAGE_PERMANENT_CHANGE; onPackageAdded(pkg, uid); } + onPackageAppearedWithExtras(pkg, intent.getExtras()); onPackageAppeared(pkg, mChangeType); } } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { @@ -403,6 +425,7 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { onPackageRemovedAllUsers(pkg, uid); } } + onPackageDisappearedWithExtras(pkg, intent.getExtras()); onPackageDisappeared(pkg, mChangeType); } } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { @@ -417,6 +440,7 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { if (onPackageChanged(pkg, uid, mModifiedComponents)) { mSomePackagesChanged = true; } + onPackageChangedWithExtras(pkg, intent.getExtras()); onPackageModified(pkg); } } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) { diff --git a/core/java/com/android/internal/policy/AttributeCache.java b/core/java/com/android/internal/policy/AttributeCache.java index 1bdad25d25bb..970f5111c2ea 100644 --- a/core/java/com/android/internal/policy/AttributeCache.java +++ b/core/java/com/android/internal/policy/AttributeCache.java @@ -16,12 +16,18 @@ package com.android.internal.policy; +import android.annotation.RequiresPermission; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; +import android.net.Uri; +import android.os.Handler; import android.os.UserHandle; import android.util.ArrayMap; import android.util.LruCache; @@ -46,6 +52,8 @@ public final class AttributeCache { @GuardedBy("this") private final Configuration mConfiguration = new Configuration(); + private PackageMonitor mPackageMonitor; + public final static class Package { public final Context context; private final SparseArray<ArrayMap<int[], Entry>> mMap = new SparseArray<>(); @@ -77,6 +85,34 @@ public final class AttributeCache { } } + /** + * Start monitor package change, so the resources can be loaded correctly. + */ + void monitorPackageRemove(Handler handler) { + if (mPackageMonitor == null) { + mPackageMonitor = new PackageMonitor(mContext, handler); + } + } + + static class PackageMonitor extends BroadcastReceiver { + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + PackageMonitor(Context context, Handler handler) { + final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme(IntentFilter.SCHEME_PACKAGE); + context.registerReceiverAsUser(this, UserHandle.ALL, filter, + null /* broadcastPermission */, handler); + } + + @Override + public void onReceive(Context context, Intent intent) { + final Uri packageUri = intent.getData(); + if (packageUri != null) { + final String packageName = packageUri.getEncodedSchemeSpecificPart(); + AttributeCache.instance().removePackage(packageName); + } + } + } + public static AttributeCache instance() { return sInstance; } diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java index 8f4df806296e..40a437f300f3 100644 --- a/core/java/com/android/internal/policy/TransitionAnimation.java +++ b/core/java/com/android/internal/policy/TransitionAnimation.java @@ -48,6 +48,7 @@ import android.graphics.drawable.Drawable; import android.hardware.HardwareBuffer; import android.media.Image; import android.media.ImageReader; +import android.os.Handler; import android.os.SystemProperties; import android.util.Slog; import android.view.InflateException; @@ -1399,4 +1400,14 @@ public class TransitionAnimation { // Approximation of WCAG 2.0 relative luminance. return ((r * 8) + (g * 22) + (b * 2)) >> 5; } + + /** + * For non-system server process, it must call this method to initialize the AttributeCache and + * start monitor package change, so the resources can be loaded correctly. + */ + public static void initAttributeCache(Context context, Handler handler) { + AttributeCache.init(context); + AttributeCache.instance().monitorPackageRemove(handler); + } + } diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java index 058c6ec4d13c..6e45796df053 100644 --- a/core/java/com/android/internal/view/RotationPolicy.java +++ b/core/java/com/android/internal/view/RotationPolicy.java @@ -105,23 +105,23 @@ public final class RotationPolicy { /** * Enables or disables rotation lock from the system UI toggle. */ - public static void setRotationLock(Context context, final boolean enabled) { + public static void setRotationLock(Context context, final boolean enabled, String caller) { final int rotation = areAllRotationsAllowed(context) || useCurrentRotationOnRotationLockChange(context) ? CURRENT_ROTATION : NATURAL_ROTATION; - setRotationLockAtAngle(context, enabled, rotation); + setRotationLockAtAngle(context, enabled, rotation, caller); } /** * Enables or disables rotation lock at a specific rotation from system UI. */ public static void setRotationLockAtAngle(Context context, final boolean enabled, - final int rotation) { + final int rotation, String caller) { Settings.System.putIntForUser(context.getContentResolver(), Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0, UserHandle.USER_CURRENT); - setRotationLock(enabled, rotation); + setRotationLock(enabled, rotation, caller); } /** @@ -129,12 +129,13 @@ public final class RotationPolicy { * * If rotation is locked for accessibility, the system UI toggle is hidden to avoid confusion. */ - public static void setRotationLockForAccessibility(Context context, final boolean enabled) { + public static void setRotationLockForAccessibility(Context context, final boolean enabled, + String caller) { Settings.System.putIntForUser(context.getContentResolver(), Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0, UserHandle.USER_CURRENT); - setRotationLock(enabled, NATURAL_ROTATION); + setRotationLock(enabled, NATURAL_ROTATION, caller); } private static boolean areAllRotationsAllowed(Context context) { @@ -146,16 +147,17 @@ public final class RotationPolicy { R.bool.config_useCurrentRotationOnRotationLockChange); } - private static void setRotationLock(final boolean enabled, final int rotation) { + private static void setRotationLock(final boolean enabled, final int rotation, + final String caller) { AsyncTask.execute(new Runnable() { @Override public void run() { try { IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); if (enabled) { - wm.freezeRotation(rotation); + wm.freezeRotation(rotation, caller); } else { - wm.thawRotation(); + wm.thawRotation(caller); } } catch (RemoteException exc) { Log.w(TAG, "Unable to save auto-rotate setting"); diff --git a/core/jni/Android.bp b/core/jni/Android.bp index ad196c014dec..9ed41553a4b4 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -15,7 +15,19 @@ license { ], } -cc_library_shared { +soong_config_module_type { + name: "cc_library_shared_for_libandroid_runtime", + module_type: "cc_library_shared", + config_namespace: "ANDROID", + bool_variables: [ + "release_binder_death_recipient_weak_from_jni", + ], + properties: [ + "cflags", + ], +} + +cc_library_shared_for_libandroid_runtime { name: "libandroid_runtime", host_supported: true, cflags: [ @@ -46,6 +58,12 @@ cc_library_shared { }, }, + soong_config_variables: { + release_binder_death_recipient_weak_from_jni: { + cflags: ["-DBINDER_DEATH_RECIPIENT_WEAK_FROM_JNI"], + }, + }, + cpp_std: "gnu++20", srcs: [ diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 041f9c7edeef..bfd80a9e4f74 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -17,19 +17,8 @@ #define LOG_TAG "JavaBinder" //#define LOG_NDEBUG 0 -#include "android_os_Parcel.h" #include "android_util_Binder.h" -#include <atomic> -#include <fcntl.h> -#include <inttypes.h> -#include <mutex> -#include <stdio.h> -#include <string> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - #include <android-base/stringprintf.h> #include <binder/BpBinder.h> #include <binder/IInterface.h> @@ -40,7 +29,16 @@ #include <binder/Stability.h> #include <binderthreadstate/CallerUtils.h> #include <cutils/atomic.h> +#include <fcntl.h> +#include <inttypes.h> #include <log/log.h> +#include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/ScopedUtfChars.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> #include <utils/KeyedVector.h> #include <utils/List.h> #include <utils/Log.h> @@ -48,10 +46,11 @@ #include <utils/SystemClock.h> #include <utils/threads.h> -#include <nativehelper/JNIHelp.h> -#include <nativehelper/ScopedLocalRef.h> -#include <nativehelper/ScopedUtfChars.h> +#include <atomic> +#include <mutex> +#include <string> +#include "android_os_Parcel.h" #include "core_jni_helpers.h" //#undef ALOGV @@ -553,14 +552,48 @@ public: }; // ---------------------------------------------------------------------------- +#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI +#if __BIONIC__ +#include <android/api-level.h> +static bool target_sdk_is_at_least_vic() { + return android_get_application_target_sdk_version() >= __ANDROID_API_V__; +} +#else +static constexpr bool target_sdk_is_at_least_vic() { + // If not built for Android (i.e. glibc host), follow the latest behavior as there's no compat + // requirement there. + return true; +} +#endif // __BIONIC__ +#endif // BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI class JavaDeathRecipient : public IBinder::DeathRecipient { public: JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list) - : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)), - mObjectWeak(NULL), mList(list) - { + : mVM(jnienv_to_javavm(env)), mObject(NULL), mObjectWeak(NULL), mList(list) { + // b/298374304: For apps targeting Android V or beyond, we no longer hold the global JNI ref + // to the death recipient objects. This is to prevent the memory leak which can happen when + // the death recipient object internally has a strong reference to the proxy object. Under + // the old behavior, you were unable to kill the binder service by dropping all references + // to the proxy object - because it is still strong referenced from JNI (here). The only way + // to cut the strong reference was to call unlinkDeath(), but it was easy to forget. + // + // Now, the strong reference to the death recipient is held in the Java-side proxy object. + // See BinderProxy.mDeathRecipients. From JNI, only the weak reference is kept. An + // implication of this is that you may not receive binderDied() if you drop all references + // to the proxy object before the service dies. This should be okay for most cases because + // you normally are not interested in the death of a binder service which you don't have any + // reference to. If however you want to get binderDied() regardless of the proxy object's + // lifecycle, keep a strong reference to the death recipient object by yourself. +#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI + if (target_sdk_is_at_least_vic()) { + mObjectWeak = env->NewWeakGlobalRef(object); + } else +#endif + { + mObject = env->NewGlobalRef(object); + } // These objects manage their own lifetimes so are responsible for final bookkeeping. // The list holds a strong reference to this object. LOGDEATH("Adding JDR %p to DRL %p", this, list.get()); @@ -573,26 +606,49 @@ public: void binderDied(const wp<IBinder>& who) { LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this); - if (mObject != NULL) { - JNIEnv* env = javavm_to_jnienv(mVM); - ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote())); - env->CallStaticVoidMethod(gBinderProxyOffsets.mClass, - gBinderProxyOffsets.mSendDeathNotice, mObject, - jBinderProxy.get()); - if (env->ExceptionCheck()) { - jthrowable excep = env->ExceptionOccurred(); - binder_report_exception(env, excep, - "*** Uncaught exception returned from death notification!"); - } + if (mObject == NULL && mObjectWeak == NULL) { + return; + } + JNIEnv* env = javavm_to_jnienv(mVM); + ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote())); + + // Hold a local reference to the recipient. This may fail if the recipient is weakly + // referenced, in which case we can't deliver the death notice. + ScopedLocalRef<jobject> jRecipient(env, + env->NewLocalRef(mObject != NULL ? mObject + : mObjectWeak)); + if (jRecipient.get() == NULL) { + ALOGW("Binder died, but death recipient is already garbage collected. If your target " + "sdk level is at or above 35, this can happen when you dropped all references to " + "the binder service before it died. If you want to get a death notice for a " + "binder service which you have no reference to, keep a strong reference to the " + "death recipient by yourself."); + return; + } - // Serialize with our containing DeathRecipientList so that we can't - // delete the global ref on mObject while the list is being iterated. + if (mFired) { + ALOGW("Received multiple death notices for the same binder object. Binder driver bug?"); + return; + } + mFired = true; + + env->CallStaticVoidMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mSendDeathNotice, + jRecipient.get(), jBinderProxy.get()); + if (env->ExceptionCheck()) { + jthrowable excep = env->ExceptionOccurred(); + binder_report_exception(env, excep, + "*** Uncaught exception returned from death notification!"); + } + + // Demote from strong ref (if exists) to weak after binderDied() has been delivered, to + // allow the DeathRecipient and BinderProxy to be GC'd if no longer needed. Do this in sync + // with our containing DeathRecipientList so that we can't delete the global ref on mObject + // while the list is being iterated. + if (mObject != NULL) { sp<DeathRecipientList> list = mList.promote(); if (list != NULL) { AutoMutex _l(list->lock()); - // Demote from strong ref to weak after binderDied() has been delivered, - // to allow the DeathRecipient and BinderProxy to be GC'd if no longer needed. mObjectWeak = env->NewWeakGlobalRef(mObject); env->DeleteGlobalRef(mObject); mObject = NULL; @@ -659,9 +715,19 @@ protected: private: JavaVM* const mVM; - jobject mObject; // Initial strong ref to Java-side DeathRecipient. Cleared on binderDied(). - jweak mObjectWeak; // Weak ref to the same Java-side DeathRecipient after binderDied(). + + // If target sdk version < 35, the Java-side DeathRecipient is strongly referenced from mObject + // upon linkToDeath() and then after binderDied() is called, the strong reference is demoted to + // a weak reference (mObjectWeak). + // If target sdk version >= 35, the strong reference is never made here (i.e. mObject == NULL + // always). Instead, the strong reference to the Java-side DeathRecipient is made in + // BinderProxy.mDeathRecipients. In the native world, only the weak reference is kept. + jobject mObject; + jweak mObjectWeak; wp<DeathRecipientList> mList; + + // Whether binderDied was called or not. + bool mFired = false; }; // ---------------------------------------------------------------------------- @@ -1435,17 +1501,19 @@ static jobject android_os_BinderProxy_getExtension(JNIEnv* env, jobject obj) { // ---------------------------------------------------------------------------- +// clang-format off static const JNINativeMethod gBinderProxyMethods[] = { /* name, signature, funcPtr */ {"pingBinder", "()Z", (void*)android_os_BinderProxy_pingBinder}, {"isBinderAlive", "()Z", (void*)android_os_BinderProxy_isBinderAlive}, {"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor}, {"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact}, - {"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath}, - {"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath}, + {"linkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath}, + {"unlinkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath}, {"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer}, {"getExtension", "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension}, }; +// clang-format on const char* const kBinderProxyPathName = "android/os/BinderProxy"; diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index 69fc515444b2..41c65aec3144 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -38,6 +38,7 @@ static struct { jmethodID dispatchVsync; jmethodID dispatchHotplug; + jmethodID dispatchHotplugConnectionError; jmethodID dispatchModeChanged; jmethodID dispatchFrameRateOverrides; @@ -89,6 +90,7 @@ private: void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, VsyncEventData vsyncEventData) override; void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; + void dispatchHotplugConnectionError(nsecs_t timestamp, int errorCode) override; void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, nsecs_t renderPeriod) override; void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, @@ -230,6 +232,22 @@ void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, PhysicalDisp mMessageQueue->raiseAndClearException(env, "dispatchHotplug"); } +void NativeDisplayEventReceiver::dispatchHotplugConnectionError(nsecs_t timestamp, + int connectionError) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal)); + if (receiverObj.get()) { + ALOGV("receiver %p ~ Invoking hotplug dispatchHotplugConnectionError handler.", this); + env->CallVoidMethod(receiverObj.get(), + gDisplayEventReceiverClassInfo.dispatchHotplugConnectionError, + timestamp, connectionError); + ALOGV("receiver %p ~ Returned from hotplug dispatchHotplugConnectionError handler.", this); + } + + mMessageQueue->raiseAndClearException(env, "dispatchHotplugConnectionError"); +} + void NativeDisplayEventReceiver::dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, nsecs_t renderPeriod) { JNIEnv* env = AndroidRuntime::getJNIEnv(); @@ -354,8 +372,12 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V"); - gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env, - gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V"); + gDisplayEventReceiverClassInfo.dispatchHotplug = + GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", + "(JJZ)V"); + gDisplayEventReceiverClassInfo.dispatchHotplugConnectionError = + GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, + "dispatchHotplugConnectionError", "(JI)V"); gDisplayEventReceiverClassInfo.dispatchModeChanged = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeChanged", "(JJIJ)V"); diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index b1caec253949..cd951cbdde1c 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Hierdie bevoorregte of stelselprogram kan enige tyd met \'n stelselkamera foto\'s neem en video\'s opneem. Vereis dat die program ook die android.permission.CAMERA-toestemming het"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Laat \'n program of diens toe om terugbeloproepe te ontvang oor kameratoestelle wat oopgemaak of toegemaak word."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Hierdie program kan terugbeloproepe ontvang wanneer enige kameratoestel oopgemaak (deur watter program) of toegemaak word."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Laat ’n program of diens toe om toegang tot die kamera te verkry as ’n stelselgebruiker sonder koppelvlak."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Hierdie app het toegang tot die kamera as ’n stelselgebruiker sonder koppelvlak."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"beheer vibrasie"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Laat die program toe om die vibrator te beheer."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Stel die program in staat om toegang tot die vibreerderstand te kry."</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 8ffdba3e3f92..42ba598502d5 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"ይህ ልዩ ፈቃድ ያለው የሥርዓት መተግበሪያ በማንኛውም ጊዜ የሥርዓት ካሜራን በመጠቀም ሥዕሎችን ማንሣት እና ቪዲዮ መቅረጽ ይችላል። የandroid.permission.CAMERA ፈቃዱ በመተግበሪያውም ጭምር እንዲያዝ ያስፈልገዋል።"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"አንድ መተግበሪያ ወይም አገልግሎት እየተከፈቱ ወይም እየተዘጉ ስላሉ የካሜራ መሣሪያዎች መልሶ ጥሪዎችን እንዲቀበል ይፍቀዱ።"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"ማንኛውም የካሜራ መሣሪያ እየተከፈተ (በምን መተግበሪያ) ወይም እየተዘጋ ባለበት ጊዜ ይህ መተግበሪያ መልሶ ጥሪዎችን መቀበል ይችላል።"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"መተግበሪያ ወይም አገልግሎት ካሜራን እንደ የበይነገፅ-አልባ ሥርዓት ተጠቃሚ እንዲደርስ ይፍቀዱ።"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"ይህ መተግበሪያ ካሜራን እንደ የበይነገፅ-አልባ ሥርዓት ተጠቃሚ መድረስ ይችላል።"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"ነዛሪ ተቆጣጠር"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"ነዛሪውን ለመቆጣጠር ለመተግበሪያው ይፈቅዳሉ።"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"መተግበሪያው የንዝረት ሁኔታውን እንዲደርስ ያስችለዋል።"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index ff1ecdca11da..dd9710764af8 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -505,6 +505,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"إنّ تطبيق النظام هذا، أو التطبيق المزوّد بأذونات مميّزة، يمكنه التقاط صور وتسجيل فيديوهات باستخدام كاميرا النظام في أي وقت. ويجب أن يحصل التطبيق أيضًا على الإذن android.permission.CAMERA."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"يسمح الإذن لتطبيق أو خدمة بتلقّي استدعاءات عما إذا كانت أجهزة الكاميرات مفتوحة أو مغلقة."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"يمكن أن يتلقّى هذا التطبيق استدعاءات عندما تكون هناك كاميرا مفتوحة (بواسطة هذا التطبيق) أو مغلقة."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"التحكم في الاهتزاز"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"للسماح للتطبيق بالتحكم في الهزّاز."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"يسمح هذا الإذن للتطبيق بالوصول إلى حالة الهزّاز."</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 0ca9ea235be8..ba3e7567f103 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"এই বিশেষাধিকাৰ প্ৰাপ্ত অথবা ছিষ্টেম এপ্টোৱে এটা ছিষ্টেম কেমেৰা ব্যৱহাৰ কৰি যিকোনো সময়তে ফট’ উঠাব পাৰে আৰু ভিডিঅ’ ৰেকৰ্ড কৰিব পাৰে। লগতে এপ্টোৰো android.permission.CAMERAৰ অনুমতি থকাটো প্ৰয়োজনীয়"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"কোনো এপ্লিকেশ্বন অথবা সেৱাক কেমেৰা ডিভাইচসমূহ খোলা অথবা বন্ধ কৰাৰ বিষয়ে কলবেকসমূহ গ্ৰহণ কৰিবলৈ অনুমতি দিয়ক।"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"যিকোনো কেমেৰা ডিভাইচ খুলি থকা অথবা বন্ধ কৰি থকাৰ সময়ত (কোনো এপ্লিকেশ্বনৰ দ্বাৰা) এই এপ্টোৱে কলবেক গ্ৰহণ কৰিব পাৰে।"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"কোনো এপ্লিকেশ্বন বা সেৱাক পৰিধীয় ডিভাইচহীন ছিষ্টেম ব্যৱহাৰকাৰী হিচাপে কেমেৰা এক্সেছ কৰা অনুমতি দিয়ক।"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"এই এপ্টোৱে পৰিধীয় ডিভাইচহীন ছিষ্টেম ব্যৱহাৰকাৰী হিচাপে কেমেৰা এক্সেছ কৰিব পাৰে।"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"কম্পন নিয়ন্ত্ৰণ কৰক"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"ভাইব্ৰেটৰ নিয়ন্ত্ৰণ কৰিবলৈ এপ্টোক অনুমতি দিয়ে।"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"এপ্টোক কম্পন স্থিতিটো এক্সেছ কৰিবলৈ অনুমতি দিয়ে।"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 0004ba87d9fd..208de1d51e7d 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Bu icazəli və ya sistem tətbiqi istənilən vaxt sistem kamerasından istifadə edərək şəkil və videolar çəkə bilər. android.permission.CAMERA icazəsinin də tətbiq tərəfindən saxlanılmasını tələb edir"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Tətbiqə və ya xidmətə kamera cihazlarının açılması və ya bağlanması haqqında geri zənglər qəbul etməyə icazə verin."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Hansısa kamera cihazı açıldıqda və ya bağlandıqda (hansısa tətbiq tərəfindən) bu tətbiq geri çağırışlar qəbul edə bilər."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"vibrasiyaya nəzarət edir"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Tətbiqə vibratoru idarə etmə icazəsi verir."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Tətbiqə vibrasiya vəziyyətinə daxil olmaq imkanı verir."</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 74ba52da3518..3fd8dcb2927a 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -502,6 +502,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ova privilegovana sistemska aplikacija može da snima slike i video snimke pomoću kamere sistema u bilo kom trenutku. Aplikacija treba da ima i dozvolu android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Dozvolite aplikaciji ili usluzi da dobija povratne pozive o otvaranju ili zatvaranju uređaja sa kamerom."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Ova aplikacija može da dobija povratne pozive kada se bilo koji uređaj sa kamerom otvara ili zatvara (pomoću neke aplikacije)."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Dozvolite aplikaciji ili usluzi da pristupa kameri kao korisnik sistema bez grafičkog korisničkog interfejsa."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Ova aplikacija može da pristupa kameri kao korisnik sistema bez grafičkog korisničkog interfejsa."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"kontrola vibracije"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Dozvoljava aplikaciji da kontroliše vibraciju."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Dozvoljava aplikaciji da pristupa stanju vibriranja."</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 3a79090ebf1c..5bf8a3b70a3e 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -503,6 +503,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Гэта прыярытэтная ці сістэмная праграма можа здымаць фота і запісваць відэа з дапамогай сістэмнай камеры. Праграме таксама патрэбны дазвол android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Дазволіць праграме ці сэрвісу атрымліваць зваротныя выклікі наконт адкрыцця ці закрыцця прылад камеры."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Гэта праграма можа атрымліваць зваротныя выклікі, калі адкрываецца (праграмай) або закрываецца прылада камеры."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"кіраванне вібрацыяй"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Дазваляе прыкладанням кіраваць вібрацыяй."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Дазваляе праграме атрымліваць доступ да вібрасігналу."</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 97345fcf4b3d..9dd971c14b81 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Това привилегировано или системно приложение може по всяко време да прави снимки и да записва видео посредством системна камера. Необходимо е също на приложението да бъде дадено разрешението android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Разрешаване на приложение или услуга да получават обратни повиквания за отварянето или затварянето на снимачни устройства."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Това приложение може да получава обратни повиквания, когато снимачно устройство бъде отворено (от кое приложение) или затворено."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Разрешаване на приложение или услуга да осъществяват достъп до камерата като потребител на система без графичен потребителски интерфейс."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Това приложение може да осъществява достъп до камерата като потребител на система без графичен потребителски интерфейс."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"контролиране на вибрирането"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Разрешава на приложението да контролира устройството за вибрация."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Дава възможност на приложението да осъществява достъп до състоянието на вибриране."</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index f6f5b0eb38ad..f428b86dc5a8 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"এই প্রিভিলেজ বা সিস্টেম অ্যাপ যেকোনও সময় সিস্টেম ক্যামেরা ব্যবহার করে ছবি তুলতে ও ভিডিও রেকর্ড করতে পারে। এই অ্যাপকে android.permission.CAMERA অনুমতি দিতে হবে"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"কোনও অ্যাপ্লিকেশন বা পরিষেবাকে ক্যামেরা ডিভাইসগুলি খোলা বা বন্ধ হওয়া সম্পর্কে কলব্যাকগুলি গ্রহণ করার অনুমতি দিন।"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"কোনও ক্যামেরা ডিভাইস খোলা (অ্যাপ্লিকেশনের সাহায্যে) বা বন্ধ করা হলে এই অ্যাপ কলব্যাক গ্রহণ করতে পারে।"</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"ভাইব্রেশন নিয়ন্ত্রণ করুন"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"অ্যাপ্লিকেশানকে কম্পক নিয়ন্ত্রণ করতে দেয়৷"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"ভাইব্রেট করার স্থিতি অ্যাক্সেস করার অনুমতি দিন।"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 3fd0637d27f9..e2ace072d6e0 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -502,6 +502,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ova povlaštena ili sistemska aplikacija u svakom trenutku može snimati fotografije i videozapise pomoću kamere sistema. Aplikacija također mora imati odobrenje android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Dozvoliti aplikaciji ili usluzi da prima povratne pozive o otvaranju ili zatvaranju kamera."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Ova aplikacija može primati povratne pozive kada se otvara ili zatvara bilo koji uređaj s kamerom (putem neke aplikacije)."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Dopustite aplikaciji ili usluzi da pristupi kameri kao korisnik sustava bez grafičkog korisničkog sučelja."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Ova aplikacija može pristupiti kameri kao korisnik sustava bez grafičkog korisničkog sučelja."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"kontrola vibracije"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Dozvoljava aplikaciji upravljanje vibracijom."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Dozvoljava aplikaciji pristup stanju vibracije."</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 010e22ce781d..be95847849ba 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -502,6 +502,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Aquesta aplicació del sistema amb privilegis pot fer fotos i gravar vídeos amb una càmera del sistema en qualsevol moment. L\'aplicació també ha de tenir el permís android.permission.CAMERA per accedir-hi."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permet que una aplicació o un servei pugui rebre crides de retorn sobre els dispositius de càmera que s\'obren o es tanquen."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Aquesta aplicació pot rebre crides de retorn quan s\'obre o es tanca un dispositiu de càmera mitjançant l\'aplicació en qüestió."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Permetre que una aplicació o un servei accedeixi a la càmera com a usuari del sistema sense interfície gràfica."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Aquesta aplicació pot accedir a la càmera com a usuari del sistema sense interfície gràfica."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"controlar la vibració"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Permet que l\'aplicació controli el vibrador."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Permet que l\'aplicació accedeixi a l\'estat de vibració."</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 89d25015d4db..34fe496eecef 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -503,6 +503,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Tato privilegovaná nebo systémová aplikace může pomocí fotoaparátu kdykoli pořídit snímek nebo nahrát video. Aplikace musí zároveň mít oprávnění android.permission.CAMERA."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Povolte aplikaci nebo službě přijímat zpětná volání o otevření nebo zavření zařízení s fotoaparátem."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Tato aplikace může přijímat zpětná volání při otevírání nebo zavírání libovolného fotoaparátu (s informacemi o tom, která aplikace tuto akci provádí)."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"ovládání vibrací"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Umožňuje aplikaci ovládat vibrace."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Umožňuje aplikaci přístup ke stavu vibrací."</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index e816391f44b4..97f850813af6 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Denne privilegerede app eller systemapp kan til enhver tid tage billeder og optage video med et systemkamera. Appen skal også have tilladelsen android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Tillad, at en app eller tjeneste modtager tilbagekald om kameraenheder, der åbnes eller lukkes."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Denne app kan modtage tilbagekald, når en kameraenhed åbnes (via appen) eller lukkes."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"administrere vibration"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Tillader, at appen kan administrere vibratoren."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Tillader, at appen bruger vibration."</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 3f960f70a800..33195a5293d4 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Diese privilegierte App oder System-App kann jederzeit mit einer Systemkamera Bilder und Videos aufnehmen. Die App benötigt auch die Berechtigung \"android.permission.CAMERA\"."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Einer App oder einem Dienst den Empfang von Callbacks erlauben, wenn eine Kamera geöffnet oder geschlossen wird."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Diese App kann Callbacks empfangen, wenn eine der Kameras des Geräts von einer Anwendung geöffnet oder geschlossen wird."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"Vibrationsalarm steuern"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Ermöglicht der App, den Vibrationsalarm zu steuern"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Ermöglicht der App, auf den Vibrationsstatus zuzugreifen."</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 1d3471c0db95..f38116aff016 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -74,7 +74,7 @@ <string name="serviceNotProvisioned" msgid="8289333510236766193">"Η υπηρεσία δεν προβλέπεται."</string> <string name="CLIRPermanent" msgid="166443681876381118">"Δεν μπορείτε να αλλάξετε τη ρύθμιση του αναγνωριστικού καλούντος."</string> <string name="auto_data_switch_title" msgid="3286350716870518297">"Έγινε εναλλαγή των δεδομένων σε <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string> - <string name="auto_data_switch_content" msgid="803557715007110959">"Μπορείτε να αλλάξετε αυτήν την επιλογή ανά πάσα στιγμή στις Ρυθμίσεις"</string> + <string name="auto_data_switch_content" msgid="803557715007110959">"Μπορείτε να αλλάξετε αυτή την επιλογή ανά πάσα στιγμή στις Ρυθμίσεις"</string> <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Δεν υπάρχει υπηρεσία δεδομένων κινητής τηλεφωνίας"</string> <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Οι κλήσεις έκτακτης ανάγκης δεν είναι διαθέσιμες"</string> <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Δεν υπάρχει φωνητική υπηρεσία"</string> @@ -253,9 +253,9 @@ <string name="bugreport_title" msgid="8549990811777373050">"Αναφορά σφάλματος"</string> <string name="bugreport_message" msgid="5212529146119624326">"Θα συλλέξει πληροφορίες σχετικά με την τρέχουσα κατάσταση της συσκευής σας και θα τις στείλει μέσω μηνύματος ηλεκτρονικού ταχυδρομείου. Απαιτείται λίγος χρόνος για τη σύνταξη της αναφοράς σφάλματος και την αποστολή της. Κάντε λίγη υπομονή."</string> <string name="bugreport_option_interactive_title" msgid="7968287837902871289">"Διαδραστική αναφορά"</string> - <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Χρησιμοποιήστε αυτήν την επιλογή στις περισσότερες περιπτώσεις. Σας επιτρέπει να παρακολουθείτε την πρόοδο της αναφοράς, να εισάγετε περισσότερες λεπτομέρειες σχετικά με το πρόβλημα που αντιμετωπίζετε και να τραβήξετε στιγμιότυπα οθόνης. Ενδέχεται να παραλείψει ορισμένες ενότητες που δεν χρησιμοποιούνται συχνά και για τις οποίες απαιτείται μεγάλο χρονικό διάστημα για τη δημιουργία αναφορών."</string> + <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Χρησιμοποιήστε αυτή την επιλογή στις περισσότερες περιπτώσεις. Σας επιτρέπει να παρακολουθείτε την πρόοδο της αναφοράς, να εισάγετε περισσότερες λεπτομέρειες σχετικά με το πρόβλημα που αντιμετωπίζετε και να τραβήξετε στιγμιότυπα οθόνης. Ενδέχεται να παραλείψει ορισμένες ενότητες που δεν χρησιμοποιούνται συχνά και για τις οποίες απαιτείται μεγάλο χρονικό διάστημα για τη δημιουργία αναφορών."</string> <string name="bugreport_option_full_title" msgid="7681035745950045690">"Πλήρης αναφορά"</string> - <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Χρησιμοποιήστε αυτήν την επιλογή για την ελάχιστη δυνατή παρέμβαση συστήματος, όταν η συσκευή σας δεν ανταποκρίνεται ή παρουσιάζει μεγάλη καθυστέρηση στη λειτουργία ή όταν χρειάζεστε όλες τις ενότητες αναφοράς. Δεν σας επιτρέπει να προσθέσετε περισσότερες λεπτομέρειες ή να τραβήξετε επιπλέον στιγμιότυπα οθόνης."</string> + <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Χρησιμοποιήστε αυτή την επιλογή για την ελάχιστη δυνατή παρέμβαση συστήματος, όταν η συσκευή σας δεν ανταποκρίνεται ή παρουσιάζει μεγάλη καθυστέρηση στη λειτουργία ή όταν χρειάζεστε όλες τις ενότητες αναφοράς. Δεν σας επιτρέπει να προσθέσετε περισσότερες λεπτομέρειες ή να τραβήξετε επιπλέον στιγμιότυπα οθόνης."</string> <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Λήψη στιγμιότυπου οθόνης για αναφορά σφάλματος σε # δευτερόλεπτο.}other{Λήψη στιγμιότυπου οθόνης για αναφορά σφάλματος σε # δευτερόλεπτα.}}"</string> <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Έγινε λήψη στιγμιότυπου οθόνης με αναφορά σφάλματος"</string> <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Αποτυχία λήψης στιγμιότυπου οθόνης με αναφορά σφάλματος"</string> @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Αυτή η προνομιακή εφαρμογή ή εφαρμογή συστήματος μπορεί να τραβάει φωτογραφίες και να εγγράφει βίντεο, χρησιμοποιώντας μια κάμερα του συστήματος ανά πάσα στιγμή. Απαιτείται, επίσης, η εφαρμογή να έχει την άδεια android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Επιτρέψτε σε μια εφαρμογή ή μια υπηρεσία να λαμβάνει επανάκλησεις σχετικά με το άνοιγμα ή το κλείσιμο συσκευών κάμερας."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Αυτή η εφαρμογή μπορεί να λαμβάνει επανακλήσεις κατά το άνοιγμα οποιασδήποτε συσκευής κάμερας (από οποιαδήποτε εφαρμογή) ή κατά το κλείσιμο."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Επιτρέψτε σε μια εφαρμογή ή υπηρεσία να αποκτήσει πρόσβαση στην κάμερα ως χρήστης συστήματος Headless."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Αυτή η εφαρμογή μπορεί να αποκτήσει πρόσβαση στην κάμερα ως χρήστης συστήματος Headless."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"ελέγχει τη δόνηση"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Επιτρέπει στην εφαρμογή τον έλεγχο της δόνησης."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Επιτρέπει στην εφαρμογή να έχει πρόσβαση στην κατάσταση δόνησης."</string> @@ -785,7 +787,7 @@ <string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"έχει πρόσβαση σε πιστοποιητικά DRM"</string> <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Επιτρέπει σε μια εφαρμογή να παρέχει και να χρησιμοποιεί πιστοποιητικά DRM. Δεν θα χρειαστεί ποτέ για κανονικές εφαρμογές."</string> <string name="permlab_handoverStatus" msgid="7620438488137057281">"λήψη κατάστασης μεταφοράς Android Beam"</string> - <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Επιτρέπει σε αυτήν την εφαρμογή να λαμβάνει πληροφορίες σχετικά με τις τρέχουσες μεταφορές Android Beam"</string> + <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Επιτρέπει σε αυτή την εφαρμογή να λαμβάνει πληροφορίες σχετικά με τις τρέχουσες μεταφορές Android Beam"</string> <string name="permlab_removeDrmCertificates" msgid="710576248717404416">"καταργεί πιστοποιητικά DRM"</string> <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Επιτρέπει σε μια εφαρμογή την κατάργηση πιστοποιητικών DRM. Δεν χρειάζεται ποτέ για κανονικές εφαρμογές."</string> <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"δεσμεύεται σε υπηρεσία ανταλλαγής μηνυμάτων παρόχου κινητής τηλεφωνίας"</string> @@ -1209,12 +1211,12 @@ <string name="whichImageCaptureApplication" msgid="2737413019463215284">"Λήψη εικόνας με"</string> <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Λήψη εικόνας με %1$s"</string> <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Λήψη εικόνας"</string> - <string name="alwaysUse" msgid="3153558199076112903">"Χρήση από προεπιλογή για αυτήν την ενέργεια."</string> + <string name="alwaysUse" msgid="3153558199076112903">"Χρήση από προεπιλογή για αυτή την ενέργεια."</string> <string name="use_a_different_app" msgid="4987790276170972776">"Χρήση άλλης εφαρμογής"</string> <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Εκκθάριση προεπιλογής στις Ρυθμίσεις συστήματος > Εφαρμογές > Ληφθείσες."</string> <string name="chooseActivity" msgid="8563390197659779956">"Επιλέξτε μια ενέργεια"</string> <string name="chooseUsbActivity" msgid="2096269989990986612">"Επιλέξτε μια εφαρμογή για τη συσκευή USB"</string> - <string name="noApplications" msgid="1186909265235544019">"Δεν υπάρχουν εφαρμογές, οι οποίες μπορούν να εκτελέσουν αυτήν την ενέργεια."</string> + <string name="noApplications" msgid="1186909265235544019">"Δεν υπάρχουν εφαρμογές, οι οποίες μπορούν να εκτελέσουν αυτή την ενέργεια."</string> <string name="aerr_application" msgid="4090916809370389109">"Η λειτουργία της εφαρμογής <xliff:g id="APPLICATION">%1$s</xliff:g> διακόπηκε"</string> <string name="aerr_process" msgid="4268018696970966407">"Η διαδικασία <xliff:g id="PROCESS">%1$s</xliff:g> έχει διακοπεί"</string> <string name="aerr_application_repeated" msgid="7804378743218496566">"Η εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g> διακόπτεται επανειλημμένα"</string> @@ -1328,7 +1330,7 @@ <string name="decline" msgid="6490507610282145874">"Απόρριψη"</string> <string name="select_character" msgid="3352797107930786979">"Εισαγωγή χαρακτήρα"</string> <string name="sms_control_title" msgid="4748684259903148341">"Αποστολή μηνυμάτων SMS"</string> - <string name="sms_control_message" msgid="6574313876316388239">"Η εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> στέλνει έναν μεγάλο αριθμό μηνυμάτων SMS. Θέλετε να επιτρέψετε σε αυτήν την εφαρμογή να συνεχίσει να στέλνει μηνύματα;"</string> + <string name="sms_control_message" msgid="6574313876316388239">"Η εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> στέλνει έναν μεγάλο αριθμό μηνυμάτων SMS. Θέλετε να επιτρέψετε σε αυτή την εφαρμογή να συνεχίσει να στέλνει μηνύματα;"</string> <string name="sms_control_yes" msgid="4858845109269524622">"Αποδοχή"</string> <string name="sms_control_no" msgid="4845717880040355570">"Άρνηση"</string> <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"Η εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> θέλει να αποστείλει ένα μήνυμα στη διεύθυνση <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>."</string> @@ -1489,8 +1491,8 @@ <string name="permission_request_notification_title" msgid="1810025922441048273">"Απαιτείται άδεια"</string> <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"Ζητήθηκε άδεια\nγια τον λογαριασμό <xliff:g id="ACCOUNT">%s</xliff:g>."</string> <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"Ζητήθηκε άδεια από την εφαρμογή <xliff:g id="APP">%1$s</xliff:g>\nγια πρόσβαση στον λογαριασμό <xliff:g id="ACCOUNT">%2$s</xliff:g>."</string> - <string name="forward_intent_to_owner" msgid="4620359037192871015">"Χρησιμοποιείτε αυτήν την εφαρμογή εκτός του προφίλ εργασίας σας"</string> - <string name="forward_intent_to_work" msgid="3620262405636021151">"Χρησιμοποιείτε αυτήν την εφαρμογή στο προφίλ εργασίας"</string> + <string name="forward_intent_to_owner" msgid="4620359037192871015">"Χρησιμοποιείτε αυτή την εφαρμογή εκτός του προφίλ εργασίας σας"</string> + <string name="forward_intent_to_work" msgid="3620262405636021151">"Χρησιμοποιείτε αυτή την εφαρμογή στο προφίλ εργασίας"</string> <string name="input_method_binding_label" msgid="1166731601721983656">"Μέθοδος εισόδου"</string> <string name="sync_binding_label" msgid="469249309424662147">"Συγχρονισμός"</string> <string name="accessibility_binding_label" msgid="1974602776545801715">"Προσβασιμότητα"</string> @@ -2082,7 +2084,7 @@ <string name="zen_upgrade_notification_title" msgid="8198167698095298717">"Η λειτουργία \"Μην ενοχλείτε\" άλλαξε"</string> <string name="zen_upgrade_notification_content" msgid="5228458567180124005">"Πατήστε για να ελέγξετε το περιεχόμενο που έχει αποκλειστεί."</string> <string name="review_notification_settings_title" msgid="5102557424459810820">"Έλεγχος ρυθμίσεων ειδοποιήσεων"</string> - <string name="review_notification_settings_text" msgid="5916244866751849279">"Από το Android 13 και έπειτα, οι εφαρμογές που εγκαθιστάτε θα χρειάζονται την άδειά σας για την αποστολή ειδοποιήσεων. Πατήστε για να αλλάξετε αυτήν την άδεια για υπάρχουσες εφαρμογές."</string> + <string name="review_notification_settings_text" msgid="5916244866751849279">"Από το Android 13 και έπειτα, οι εφαρμογές που εγκαθιστάτε θα χρειάζονται την άδειά σας για την αποστολή ειδοποιήσεων. Πατήστε για να αλλάξετε αυτή την άδεια για υπάρχουσες εφαρμογές."</string> <string name="review_notification_settings_remind_me_action" msgid="1081081018678480907">"Υπενθύμιση αργότερα"</string> <string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Παράβλεψη"</string> <string name="notification_app_name_system" msgid="3045196791746735601">"Σύστημα"</string> @@ -2132,7 +2134,7 @@ <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # αρχείο}other{{file_name} + # αρχεία}}"</string> <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Δεν υπάρχουν προτεινόμενα άτομα για κοινοποίηση"</string> <string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Λίστα εφαρμογών"</string> - <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Δεν έχει εκχωρηθεί άδεια εγγραφής σε αυτήν την εφαρμογή, αλλά μέσω αυτής της συσκευής USB θα μπορεί να εγγράφει ήχο."</string> + <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Δεν έχει εκχωρηθεί άδεια εγγραφής σε αυτή την εφαρμογή, αλλά μέσω αυτής της συσκευής USB θα μπορεί να εγγράφει ήχο."</string> <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Αρχική οθόνη"</string> <string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Επιστροφή"</string> <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Πρόσφατες εφαρμογές"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 8c33f168f17d..bc231f56c602 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"This app can receive callbacks when any camera device is being opened (by what application) or closed."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Allow an application or service to access camera as headless system user."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"This app can access camera as headless system user."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"control vibration"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Allows the app to control the vibrator."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Allows the app to access the vibrator state."</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 6dc9ce4ce735..29f4c78d9395 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"This app can receive callbacks when any camera device is being opened (by what application) or closed."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Allow an application or service to access camera as Headless System User."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"This app can access camera as Headless System User."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"control vibration"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Allows the app to control the vibrator."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Allows the app to access the vibrator state."</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 3daab6cc1cfa..5576054309dc 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"This app can receive callbacks when any camera device is being opened (by what application) or closed."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Allow an application or service to access camera as headless system user."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"This app can access camera as headless system user."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"control vibration"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Allows the app to control the vibrator."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Allows the app to access the vibrator state."</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 67ebab14d297..ea95a513288e 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"This app can receive callbacks when any camera device is being opened (by what application) or closed."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Allow an application or service to access camera as headless system user."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"This app can access camera as headless system user."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"control vibration"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Allows the app to control the vibrator."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Allows the app to access the vibrator state."</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index d8b5016449b1..c09e6ceabf14 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"This app can receive callbacks when any camera device is being opened (by what application) or closed."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Allow an application or service to access camera as Headless System User."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"This app can access camera as Headless System User."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"control vibration"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Allows the app to control the vibrator."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Allows the app to access the vibrator state."</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index e61fe1a36211..723f833190e6 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -502,6 +502,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esta app del sistema o con privilegios puede tomar fotografías y grabar videos con una cámara del sistema en cualquier momento. Para ello, requiere tener el permiso android.permission.CAMERA."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permite que una aplicación o un servicio reciba devoluciones de llamada cuando se abren o cierran dispositivos de cámara."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Esta app puede recibir devoluciones de llamada cuando se cierra o se abre cualquier dispositivo de cámara (y qué aplicación lo hace)."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Permite que una aplicación o servicio acceda a la cámara como un usuario del sistema sin interfaz gráfica"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Esta app puede acceder a la cámara como un usuario del sistema sin interfaz gráfica."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"controlar la vibración"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Permite que la aplicación controle la vibración."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Permite que la app acceda al estado del modo de vibración."</string> @@ -807,11 +809,11 @@ <string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"Permite al titular actualizar la app que instaló previamente sin acción del usuario"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Establecer reglas de contraseña"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Controlar la longitud y los caracteres permitidos en las contraseñas y los PIN para el bloqueo de pantalla."</string> - <string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisa los intentos para desbloquear la pantalla"</string> + <string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisar los intentos para desbloquear la pantalla"</string> <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Controla la cantidad de contraseñas incorrectas ingresadas al desbloquear la pantalla y bloquea la tablet o borra todos los datos de la tablet si se ingresaron demasiadas contraseñas incorrectas."</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Supervisa la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquea el dispositivo Android TV o borra todos sus datos si se ingresan demasiadas contraseñas incorrectas."</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Permite controlar la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquear el sistema de infoentretenimiento, o borrar todos los datos del sistema, si se ingresaron demasiadas contraseñas incorrectas."</string> - <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Supervisar la cantidad de contraseñas ingresadas incorrectamente al desbloquear la pantalla, y bloquear el dispositivo o borrar todos sus datos si se ingresan demasiadas contraseñas incorrectas."</string> + <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Supervisa la cantidad de contraseñas ingresadas incorrectamente al desbloquear la pantalla, y bloquea el dispositivo o borra todos sus datos si se ingresan demasiadas contraseñas incorrectas."</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Permite controlar la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquear la tablet, o borrar todos los datos del usuario, si se ingresan demasiadas contraseñas incorrectas."</string> <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Supervisa la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquea el dispositivo Android TV o borra todos los datos del usuario si se ingresan demasiadas contraseñas incorrectas."</string> <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Permite controlar la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquear el sistema de infoentretenimiento, o borrar todos los datos de perfil, si se ingresaron demasiadas contraseñas incorrectas."</string> @@ -1913,7 +1915,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Se cambió la solicitud SS por una videollamada"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Se cambió la solicitud SS por una solicitud USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Se cambió a una nueva solicitud SS"</string> - <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerta de suplantación de identidad (phishing)"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerta de phishing"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de trabajo"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerta enviada"</string> <string name="notification_verified_content_description" msgid="6401483602782359391">"Verificado"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index cbcf40306bd6..bf93533f2574 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -502,6 +502,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esta aplicación del sistema o con privilegios puede hacer fotos y grabar vídeos en cualquier momento con una cámara del sistema, aunque debe tener también el permiso android.permission.CAMERA para hacerlo"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permitir que una aplicación o servicio reciba retrollamadas cada vez que se abra o cierre una cámara."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Esta aplicación puede recibir retrollamadas cuando se abre o se cierra la cámara con cualquier aplicación."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"controlar la vibración"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Permite que la aplicación controle la función de vibración."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Permite que la aplicación acceda al ajuste de vibración."</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 22600e527270..f3d1455ff7e6 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"See privileegidega või süsteemirakendus saab süsteemi kaameraga alati pilte ja videoid jäädvustada. Rakendusel peab olema ka luba android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Lubab rakendusel või teenusel kaameraseadmete avamise või sulgemise kohta tagasikutseid vastu võtta."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"See rakendus saab mis tahes kaameraseadme avamisel (vastava rakendusega) või sulgemisel tagasikutseid vastu võtta."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"juhtige vibreerimist"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Võimaldab rakendusel juhtida vibreerimist."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Võimaldab rakendusel juurde pääseda vibreerimise olekule."</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 019296d2bec0..ab2e23611d88 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Pribilegioa duen edo sistemakoa den aplikazio honek edonoiz erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko. Halaber, android.permission.CAMERA baimena izan behar du aplikazioak."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"eman jakinarazpenak jasotzeko baimena aplikazioari edo zerbitzuari kamerak ireki edo ixten direnean."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Kamera ireki edo itxi dela (eta zer aplikaziorekin) dioten jakinarazpenak jaso ditzake aplikazio honek."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"kontrolatu dardara"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Bibragailua kontrolatzeko baimena ematen die aplikazioei."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Dardara-egoera erabiltzeko baimena ematen die aplikazioei."</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 6210b62a2915..ce3109489ed9 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"این برنامه سیستم یا دارای امتیاز، میتواند با استفاده از دوربین سیستم در هرزمانی عکسبرداری و فیلمبرداری کند. برنامه به اجازه android.permission.CAMERA هم نیاز دارد."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"مجاز کردن برنامه یا سرویس برای دریافت پاسخ تماس درباره دستگاههای دوربینی که باز یا بسته میشوند."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"این برنامه میتواند هروقت دستگاه دوربین باز (براساس برنامه) یا بسته میشود، پاسخ تماس دریافت کند."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"به برنامه یا سرویس اجازه داده شود بهعنوان «کاربر سیستم بیسَر» به دوربین دسترسی پیدا کند."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"این برنامه میتواند بهعنوان «کاربر سیستم بیسَر» به دوربین دسترسی پیدا کند."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"کنترل لرزش"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"به برنامه اجازه میدهد تا لرزاننده را کنترل کند."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"به برنامه اجازه میدهد تا به وضعیت لرزاننده دسترسی داشته باشد."</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index ef09aee19b31..22d48e2850ff 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Tämä oikeutettu tai järjestelmäsovellus voi ottaa järjestelmän kameralla kuvia ja videoita koska tahansa. Sovelluksella on oltava myös android.permission.CAMERA-lupa"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Salli sovelluksen tai palvelun vastaanottaa vastakutsuja kameralaitteiden avaamisesta tai sulkemisesta."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Tämä sovellus voi saada vastakutsuja, kun jokin kameralaite avataan tai suljetaan (jollakin sovelluksella)."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Myönnä sovellukselle tai palvelulle pääsy kameraan järjestelmäkäyttäjänä, jolla ei ole graafista käyttöliittymää."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Tämä sovellus saa pääsyn kameraan järjestelmäkäyttäjänä, jolla ei ole graafista käyttöliittymää."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"hallita värinää"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Antaa sovelluksen hallita värinää."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Sallii sovelluksen käyttää värinätilaa."</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index d38f65651bba..ed876527a81a 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -502,6 +502,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Cette application privilégiée ou système peut prendre des photos ou filmer des vidéos à l\'aide d\'un appareil photo système en tout temps. L\'application doit également posséder l\'autorisation android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Autoriser une application ou un service de recevoir des rappels relatifs à l\'ouverture ou à la fermeture des appareils photos."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Cette application peut recevoir des rappels lorsque l\'appareil photo est ouvert ou fermé (par l\'application en question)."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"gérer le vibreur"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Permet à l\'application de gérer le vibreur de l\'appareil."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Permet à l\'application d\'accéder au mode vibration."</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 1049d49468f4..7dc560d9cc96 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -502,6 +502,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Cette application privilégiée ou système peut utiliser une caméra photo système pour prendre des photos et enregistrer des vidéos à tout moment. Pour cela, l\'application doit également disposer de l\'autorisation android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Autoriser une application ou un service à recevoir des rappels liés à l\'ouverture ou à la fermeture de caméras"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Cette application peut recevoir des rappels lors de la fermeture ou de l\'ouverture d\'une caméra (par une application)."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"contrôler le vibreur"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Permet à l\'application de contrôler le vibreur."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Permet à l\'application d\'accéder à l\'état du vibreur."</string> @@ -1595,9 +1599,9 @@ <string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Vous avez dépassé votre limite définie de <xliff:g id="SIZE">%s</xliff:g>"</string> <string name="data_usage_restricted_title" msgid="126711424380051268">"Données en arrière-plan limitées"</string> <string name="data_usage_restricted_body" msgid="5338694433686077733">"Appuyez pour suppr. restriction."</string> - <string name="data_usage_rapid_title" msgid="2950192123248740375">"Conso données mobiles élevée"</string> + <string name="data_usage_rapid_title" msgid="2950192123248740375">"Conso élevée des données mobiles"</string> <string name="data_usage_rapid_body" msgid="3886676853263693432">"Vos applications ont utilisé plus de données que d\'habitude"</string> - <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> a utilisé plus de données que d\'habitude"</string> + <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"L\'application <xliff:g id="APP">%s</xliff:g> a utilisé plus de données que d\'habitude"</string> <string name="ssl_certificate" msgid="5690020361307261997">"Certificat de sécurité"</string> <string name="ssl_certificate_is_valid" msgid="7293675884598527081">"Ce certificat est valide."</string> <string name="issued_to" msgid="5975877665505297662">"Délivré à :"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 1b8a8fd9ec58..ef5306c37fe2 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esta aplicación do sistema con privilexios pode utilizar unha cámara do sistema en calquera momento para tirar fotos e gravar vídeos. Require que a aplicación tamén teña o permiso android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permitir que unha aplicación ou servizo reciba retrochamadas cando se abran ou se pechen dispositivos con cámara."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Esta aplicación pode recibir retrochamadas cando outra aplicación abra ou peche un dispositivo con cámara."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"controlar a vibración"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Permite á aplicación controlar o vibrador."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Permite que a aplicación acceda ao estado de vibrador"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 9ad7ff19b712..d634b1e39c62 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"આ વિશેષાધિકૃત અથવા સિસ્ટમ ઍપ કોઈપણ સમયે સિસ્ટમ કૅમેરાનો ઉપયોગ કરીને ફોટા લઈ અને વીડિયો રેકૉર્ડ કરી શકે છે. ઍપ દ્વારા આયોજિત કરવા માટે android.permission.CAMERAની પરવાનગી પણ જરૂરી છે"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"કૅમેરા ડિવાઇસ ચાલુ કે બંધ થવા વિશે કૉલબૅક પ્રાપ્ત કરવાની ઍપ્લિકેશન કે સેવાને મંજૂરી આપો."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"જ્યારે કોઈ કૅમેરા ડિવાઇસ (કયા ઍપ્લિકેશન વડે) ખોલવા કે બંધ કરવામાં આવે, ત્યારે આ ઍપ કૉલબૅક પ્રાપ્ત કરી શકે છે."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"વાઇબ્રેશન નિયંત્રિત કરો"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"એપ્લિકેશનને વાઇબ્રેટરને નિયંત્રિત કરવાની મંજૂરી આપે છે."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"ઍપને વાઇબ્રેટર સ્થિતિને ઍક્સેસ કરવાની મંજૂરી આપે છે."</string> @@ -806,7 +810,7 @@ <string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"હોલ્ડરને વપરાશકર્તા દ્વારા કરવામાં આવતી ક્રિયા વિના, અગાઉ ઇન્સ્ટૉલ કરેલી ઍપને અપડેટ કરવાની મંજૂરી આપે છે"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"પાસવર્ડ નિયમો સેટ કરો"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"સ્ક્રીન લૉક પાસવર્ડ અને પિનમાં મંજૂર લંબાઈ અને અક્ષરોને નિયંત્રિત કરો."</string> - <string name="policylab_watchLogin" msgid="7599669460083719504">"સ્ક્રીનને અનલૉક કરવાના પ્રયત્નોનું નિયમન કરો"</string> + <string name="policylab_watchLogin" msgid="7599669460083719504">"સ્ક્રીનને અનલૉક કરવાના પ્રયત્નોને મૉનિટર કરો"</string> <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડ્સની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ્સ લખ્યાં છે તો ટેબ્લેટને લૉક કરો અથવા ટેબ્લેટનો તમામ ડેટા કાઢી નાખો."</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"સ્ક્રીનને અનલૉક કરતી વખતે ટાઇપ કરેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ ટાઇપ કર્યા હોય, તો તમારા Android TV ડિવાઇસના ડેટાને લૉક કરો અથવા આ વપરાશકર્તાનો બધો ડેટા કાઢી નાખો."</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ લખ્યા હોય તો ઇન્ફોટેનમેન્ટ સિસ્ટમને લૉક કરો અથવા ઇન્ફોટેનમેન્ટ સિસ્ટમનો બધો ડેટા કાઢી નાખો."</string> @@ -814,7 +818,7 @@ <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડ્સની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ્સ લખ્યાં છે તો ટેબ્લેટને લૉક કરો અથવા આ વપરાશકર્તાનો તમામ ડેટા કાઢી નાખો."</string> <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"સ્ક્રીનને અનલૉક કરતી વખતે ટાઇપ કરેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ ટાઇપ કર્યા હોય તો તમારા Android TV ડિવાઇસને લૉક કરો અથવા આ વપરાશકર્તાનો બધો ડેટા કાઢી નાખો."</string> <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ લખ્યા હોય તો ઇન્ફોટેનમેન્ટ સિસ્ટમને લૉક કરો અથવા આ પ્રોફાઇલનો બધો ડેટા કાઢી નાખો."</string> - <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડ્સની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ્સ લખ્યાં છે તો ફોનને લૉક કરો અથવા આ વપરાશકર્તાનો તમામ ડેટા કાઢી નાખો."</string> + <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ લખ્યાં છે તો ફોનને લૉક કરો અથવા આ વપરાશકર્તાનો તમામ ડેટા કાઢી નાખો."</string> <string name="policylab_resetPassword" msgid="214556238645096520">"સ્ક્રીન લૉક બદલો"</string> <string name="policydesc_resetPassword" msgid="4626419138439341851">"સ્ક્રીન લૉક બદલો."</string> <string name="policylab_forceLock" msgid="7360335502968476434">"સ્ક્રીન લૉક કરો"</string> @@ -823,7 +827,7 @@ <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ફેક્ટરી ડેટા ફરીથી સેટ કરોને કરીને ચેતવણી વિના ટેબ્લેટનો ડેટા કાઢી નાખો."</string> <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ફેક્ટરી ડેટા રીસેટ કરીને ચેતવણી વિના તમારા Android TV ડિવાઇસનો ડેટા કાઢી નાખો."</string> <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ફેક્ટરી ડેટા રીસેટ કરીને કોઈ ચેતવણી વિના જ ઇન્ફોટેનમેન્ટ સિસ્ટમનો ડેટા કાઢી નાખો."</string> - <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ફેક્ટરી ડેટા ફરીથી સેટ કરોને કરીને ચેતવણી વિના ફોનનો ડેટા કાઢી નાખો."</string> + <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ફેક્ટરી ડેટા ફરીથી સેટ કરીને ચેતવણી વિના ફોનનો ડેટા કાઢી નાખો."</string> <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"પ્રોફાઇલનો ડેટા કાઢી નાખો"</string> <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"વપરાશકર્તા ડેટા કાઢી નાખો"</string> <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ચેતવણી વિના આ ટેબ્લેટ પરનો આ વપરાશકર્તાનો ડેટા કાઢી નાખો."</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 2cd150963695..1f3a377e512b 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"यह खास सिस्टम ऐप्लिकेशन जब चाहे, तस्वीरें लेने और वीडियो रिकॉर्ड करने के लिए सिस्टम के कैमरे का इस्तेमाल कर सकता है. इसके लिए ऐप्लिकेशन को android.permission.CAMERA की अनुमति देना भी ज़रूरी है"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"डिवाइस का कैमरे चालू या बंद होने पर, किसी ऐप्लिकेशन या सेवा को कॉलबैक पाने की मंज़ूरी दें."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"यह ऐप्लिकेशन, डिवाइस के कैमरे को चालू या बंद करते समय (किसी ऐप्लिकेशन से) कॉलबैक पा सकता है."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"ऐप्लिकेशन को हेडलेस सिस्टम यूज़र के तौर पर कैमरा ऐक्सेस करने की अनुमति दें."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"यह ऐप्लिकेशन, हेडलेस सिस्टम यूजर के तौर पर कैमरे को ऐक्सेस कर सकता है."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"कंपन (वाइब्रेशन) को नियंत्रित करें"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"ऐप्स को कंपनकर्ता नियंत्रित करने देता है."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"इससे ऐप्लिकेशन, डिवाइस का वाइब्रेटर ऐक्सेस कर पाएगा."</string> @@ -1596,7 +1598,7 @@ <string name="data_usage_restricted_body" msgid="5338694433686077733">"प्रतिबंध निकालने के लिए टैप करें."</string> <string name="data_usage_rapid_title" msgid="2950192123248740375">"माेबाइल डेटा का ज़्यादा इस्तेमाल"</string> <string name="data_usage_rapid_body" msgid="3886676853263693432">"आपके ऐप्लिकेशन ने आम तौर पर इस्तेमाल होने वाले डेटा से ज़्यादा डेटा खर्च कर दिया है"</string> - <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> ने आम तौर पर इस्तेमाल होने वाले डेटा से ज़्यादा डेटा खर्च कर दिया है"</string> + <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> ने आम तौर पर इस्तेमाल होने वाले डेटा से ज़्यादा डेटा खर्च किया है"</string> <string name="ssl_certificate" msgid="5690020361307261997">"सुरक्षा प्रमाणपत्र"</string> <string name="ssl_certificate_is_valid" msgid="7293675884598527081">"यह प्रमाणपत्र मान्य है."</string> <string name="issued_to" msgid="5975877665505297662">"इन्हें जारी किया गया:"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 4a9dbf534771..389a9562f5e8 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -502,6 +502,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ova povlaštena aplikacija ili aplikacija sustava u svakom trenutku može snimati fotografije i videozapise kamerom sustava. Aplikacija mora imati i dopuštenje android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Dopustite aplikaciji ili usluzi da prima povratne pozive o otvaranju ili zatvaranju fotoaparata."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Ta aplikacija može primati povratne pozive prilikom otvaranja (putem neke aplikacije) ili zatvaranja fotoaparata."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Dopustite aplikaciji ili usluzi da pristupi kameri kao korisnik sustava bez grafičkog korisničkog sučelja."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Ova aplikacija može pristupiti kameri kao korisnik sustava bez grafičkog korisničkog sučelja."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"upravljanje vibracijom"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Aplikaciji omogućuje nadzor nad vibratorom."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Aplikaciji omogućuje da pristupi stanju vibracije."</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 57b81a0b4ac6..4bce83b917a2 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"A rendszerkamera használatával ez az előnyben részesített vagy rendszeralkalmazás bármikor készíthet fényképeket és videókat. Az alkalmazásnak az „android.permission.CAMERA” engedéllyel is rendelkeznie kell."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Visszahívás fogadásának engedélyezése alkalmazás vagy szolgáltatás számára, ha a kamerákat megnyitják vagy bezárják."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Ez az alkalmazás fogadhat visszahívásokat bármelyik kamera (adott alkalmazás általi) megnyitásakor vagy bezárásakor."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Engedélyezheti a kívánt alkalmazás vagy szolgáltatás számára, hogy hozzáférjen a kamerához headless rendszerfelhasználóként."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Az alkalmazás hozzáférhet a kamerához headless rendszerfelhasználóként."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"rezgés szabályozása"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Lehetővé teszi az alkalmazás számára a rezgés vezérlését."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Lehetővé teszi az alkalmazás számára a rezgés állapotához való hozzáférést."</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 9190a6382c4b..e74ec542102d 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Այս արտոնյալ կամ համակարգային հավելվածը կարող է ցանկացած պահի լուսանկարել և տեսագրել՝ օգտագործելով համակարգի տեսախցիկները։ Հավելվածին նաև անհրաժեշտ է android.permission.CAMERA թույլտվությունը։"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Թույլատրել հավելվածին կամ ծառայությանը հետզանգեր ստանալ՝ տեսախցիկների բացվելու և փակվելու դեպքում։"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Այս հավելվածը կարող է հետզանգեր ստանալ՝ ցանկացած տեսախցիկի բացվելու (կնշվի բացող հավելվածը) և փակվելու դեպքում։"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Թույլատրել հավելվածին կամ ծառայությանը օգտագործել որպես միջերեսի համակարգային օգտատեր։"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Այս հավելվածին ձեր տեսախցիկը հասանելի է որպես առանց միջերեսի համակարգային օգտատեր։"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"կառավարել թրթռումը"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Թույլ է տալիս հավելվածին կառավարել թրթռոցը:"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Հավելվածին թույլ է տալիս օգտագործել սարքի թրթռալու ռեժիմը։"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 3417c2af249f..dc0993e4720f 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Aplikasi sistem atau yang diberi hak istimewa ini dapat mengambil gambar dan merekam video menggunakan kamera sistem kapan saja. Mewajibkan aplikasi untuk memiliki izin android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Izinkan aplikasi atau layanan untuk menerima callback tentang perangkat kamera yang sedang dibuka atau ditutup."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Aplikasi ini dapat menerima callback saat perangkat kamera dibuka (oleh aplikasi) atau ditutup."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Izinkan aplikasi atau layanan mengakses kamera sebagai Pengguna Sistem Headless."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Aplikasi ini dapat mengakses kamera sebagai Pengguna Sistem Headless."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"kontrol getaran"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Mengizinkan aplikasi untuk mengendalikan vibrator."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Mengizinkan aplikasi untuk mengakses status vibrator."</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 7b2bf029a23a..3a8c0ea775dc 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Þetta forgangs- eða kerfisforrit hefur heimild til að taka myndir og taka upp myndskeið með myndavél kerfisins hvenær sem er. Forritið þarf einnig að vera með heimildina android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Leyfa forriti eða þjónustu að taka við svörum um myndavélar sem verið er að opna eða loka."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Þetta forrit getur tekið við svörum þegar verið er að opna eða loka myndavél í hvaða forriti sem er."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"stjórna titringi"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Leyfir forriti að stjórna titraranum."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Veitir forritinu aðgang að stöðu titrings."</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index b0bde08f0b42..7ea502cc94ef 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -502,6 +502,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Questa app di sistema o con privilegi può scattare foto e registrare video tramite una videocamera di sistema in qualsiasi momento. Richiede che anche l\'app disponga dell\'autorizzazione android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Consenti a un\'applicazione o a un servizio di ricevere callback relativi all\'apertura o alla chiusura di videocamere."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Questa app può ricevere callback quando una videocamera viene aperta (da una specifica applicazione) o chiusa."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Consenti a un\'applicazione o a un servizio di accedere alla fotocamera come utente di sistema senza testa."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Questa app può accedere alla fotocamera come utente di sistema senza testa."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"controllo vibrazione"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Consente all\'applicazione di controllare la vibrazione."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Consente all\'app di accedere allo stato di vibrazione."</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 000adcef9df6..22cbab2d04d1 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -502,6 +502,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"האפליקציה הזו בעלת ההרשאות, או אפליקציית המערכת הזו, יכולה לצלם תמונות ולהקליט סרטונים באמצעות מצלמת מערכת בכל זמן. בנוסף, לאפליקציה נדרשת ההרשאה android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"אפליקציה או שירות יוכלו לקבל קריאות חוזרות (callback) כשמכשירי מצלמה ייפתחו או ייסגרו."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"האפליקציה הזו יכולה לקבל קריאות חוזרות (callback) כשמכשיר מצלמה כלשהו נפתח (באמצעות אפליקציה) או נסגר."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"מתן גישה למצלמת המערכת עבור אפליקציה או שירות כמשתמש באפליקציית מערכת ללא ממשק גרפי"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"האפליקציה הזו יכולה לגשת למצלמה כמשתמש באפליקציית מערכת ללא ממשק גרפי."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"שליטה ברטט"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"מאפשרת לאפליקציה לשלוט ברטט."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"מאפשרת לאפליקציה לקבל גישה למצב רטט."</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index d7618754f200..06b3445e80ee 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"権限を付与されたこのアプリまたはシステムアプリは、いつでもシステムカメラを使用して写真と動画を撮影できます。アプリには android.permission.CAMERA 権限も必要です"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"カメラデバイスが起動または終了したときにコールバックを受け取ることを、アプリまたはサービスに許可してください。"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"このアプリは、カメラデバイスが(なんらかのアプリによって)起動するとき、または終了するときにコールバックを受け取ることができます。"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"ヘッドレス システム ユーザーとしてカメラにアクセスすることをアプリまたはサービスに許可してください。"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"このアプリはヘッドレス システム ユーザーとしてカメラにアクセスできます。"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"バイブレーションの制御"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"バイブレーションの制御をアプリに許可します。"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"バイブレーションのオン / オフ状態の把握をアプリに許可します。"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 513b392abee0..c8f6621f4e60 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"ამ პრივილეგირებულ ან სისტემის აპს შეუძლია ფოტოების გადაღება და ვიდეოების ჩაწერა ნებისმიერ დროს სისტემის კამერის გამოყენებით. საჭიროა, რომ აპს ჰქოდეს android.permission.CAMERA ნებართვაც"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ნება დაერთოს აპლიკაციას ან სერვისს, მიიღოს გადმორეკვები კამერის მოწყობილობის გახსნის ან დახურვისას."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"ამ აპს შეუძლია მიიღოს გადმორეკვები, როდესაც რომელიმე კამერის მოწყობილობა იხსნება (რომელიმე აპლიკაციით) ან იხურება."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"დაუშვით აპლიკაციის ან სერვისის, როგორც სისტემის (გრაფიკული ინტერფეისის გარეშე) მომხმარებლის, წვდომა კამერაზე."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"ამ აპს შეუძლია კამერაზე წვდომა, როგორც სისტემის (გრაფიკული ინტერფეისის გარეშე) მომხმარებელს."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"ვიბრაციის კონტროლი"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"აპს შეეძლება, მართოს ვიბრირება."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"ნებას რთავს აპს, ჰქონდეს წვდომა ვიბრაციის მდგომარეობაზე."</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index bddd15c809fd..315ff29ab738 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Осы айрықша немесе жүйе қолданбасы кез келген уақытта жүйелік камера арқылы суретке не бейнеге түсіре алады. Қолданбаға android.permission.CAMERA рұқсаты қажет болады."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Қолданбаға не қызметке ашылып не жабылып жатқан камера құрылғылары туралы кері шақыру алуға рұқсат ету"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Кез келген камера ашылып (көрсетілген қолданба арқылы) не жабылып жатқанда, бұл қолданба кері шақыру алады."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"тербелісті басқару"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Қолданбаға вибраторды басқаруға рұқсат береді."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Қолданбаға діріл күйін пайдалануға мүмкіндік береді."</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 9047666092a5..807761ef6dc5 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"កម្មវិធីប្រព័ន្ធ ឬកម្មវិធីដែលមានសិទ្ធិអនុញ្ញាតនេះអាចថតរូប និងថតវីដេអូ ដោយប្រើកាមេរ៉ាប្រព័ន្ធបានគ្រប់ពេល។ តម្រូវឱ្យមានការអនុញ្ញាត android.permission.CAMERA ដើម្បីឱ្យកម្មវិធីអាចធ្វើសកម្មភាពបានផងដែរ"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"អនុញ្ញាតឱ្យកម្មវិធី ឬសេវាកម្មទទួលការហៅត្រឡប់វិញអំពីកាមេរ៉ាដែលកំពុងបិទ ឬបើក។"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"កម្មវិធីនេះអាចទទួលការហៅត្រឡប់វិញបាន នៅពេលកំពុងបិទ ឬបើកកាមេរ៉ា (ដោយកម្មវិធី)។"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"អនុញ្ញាតឱ្យកម្មវិធី ឬសេវាកម្មចូលប្រើកាមេរ៉ាជា Headless System User។"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"កម្មវិធីនេះអាចចូលប្រើកាមេរ៉ាជា Headless System User។"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"ពិនិត្យការញ័រ"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"ឲ្យកម្មវិធីគ្រប់គ្រងកម្មវិធីញ័រ។"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"អនុញ្ញាតឱ្យកម្មវិធីចូលប្រើស្ថានភាពកម្មវិធីញ័រ។"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index aa543d851322..953ed68d4d4d 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -331,7 +331,7 @@ <string name="permgrouplab_notifications" msgid="5472972361980668884">"ನೋಟಿಫಿಕೇಶನ್ಗಳು"</string> <string name="permgroupdesc_notifications" msgid="4608679556801506580">"ಅಧಿಸೂಚನೆಗಳನ್ನು ತೋರಿಸಿ"</string> <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ವಿಂಡೋ ವಿಷಯವನ್ನು ಹಿಂಪಡೆಯುತ್ತದೆ"</string> - <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ನೀವು ಬಳಸುತ್ತಿರುವ ವಿಂಡೋದ ವಿಷಯ ಪರೀಕ್ಷಿಸುತ್ತದೆ."</string> + <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ನೀವು ಸಂವಹನ ನಡೆಸುತ್ತಿರುವ ವಿಂಡೋದ ಕಂಟೆಂಟ್ ಅನ್ನು ಪರೀಕ್ಷಿಸಿ."</string> <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"ಸ್ಪರ್ಶ-ಎಕ್ಸ್ಪ್ಲೋರ್ ಆನ್ ಮಾಡುತ್ತದೆ"</string> <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"ಟ್ಯಾಪ್ ಮಾಡಲಾದ ಐಟಂಗಳನ್ನು ಗಟ್ಟಿಯಾಗಿ ಹೇಳಲಾಗುತ್ತದೆ ಮತ್ತು ಗೆಸ್ಚರ್ಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಪರದೆಯನ್ನು ಎಕ್ಸ್ಪ್ಲೋರ್ ಮಾಡಬಹುದಾಗಿದೆ."</string> <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"ನೀವು ಟೈಪ್ ಮಾಡುವ ಪಠ್ಯವನ್ನು ಗಮನಿಸುತ್ತದೆ"</string> @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"ಈ ವಿಶೇಷ ಅಥವಾ ಸಿಸ್ಟಂ ಆ್ಯಪ್, ಯಾವುದೇ ಸಮಯದಲ್ಲಾದರೂ ಸಿಸ್ಟಂ ಕ್ಯಾಮರಾವನ್ನು ಬಳಸಿಕೊಂಡು ಫೋಟೋಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಬಹುದು ಮತ್ತು ವೀಡಿಯೋಗಳನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಬಹುದು. ಆ್ಯಪ್ಗೆ android.permission.CAMERA ಅನುಮತಿಯ ಅಗತ್ಯವಿರುತ್ತದೆ"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ಕ್ಯಾಮರಾ ಸಾಧನಗಳನ್ನು ತೆರೆಯುತ್ತಿರುವ ಅಥವಾ ಮುಚ್ಚುತ್ತಿರುವ ಕುರಿತು ಕಾಲ್ಬ್ಯಾಕ್ಗಳನ್ನು ಸ್ವೀಕರಿಸಲು ಆ್ಯಪ್ ಅಥವಾ ಸೇವೆಗೆ ಅನುಮತಿಸಿ."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"ಯಾವುದೇ ಕ್ಯಾಮರಾ ಸಾಧನವನ್ನು ತೆರೆಯುತ್ತಿರುವಾಗ ಅಥವಾ ಮುಚ್ಚುತ್ತಿರುವಾಗ (ಯಾವ ಅಪ್ಲಿಕೇಶನ್ನಿಂದ ಎಂಬ ಮಾಹಿತಿಯ ಮೂಲಕ) ಈ ಆ್ಯಪ್, ಕಾಲ್ಬ್ಯಾಕ್ಗಳನ್ನು ಸ್ವೀಕರಿಸಬಹುದು."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"ಹೆಡ್ಲೆಸ್ ಸಿಸ್ಟಂ ಬಳಕೆದಾರರಂತೆ ಕ್ಯಾಮರಾವನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಆ್ಯಪ್ ಅಥವಾ ಸೇವೆಗೆ ಅನುಮತಿಸಿ."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"ಈ ಆ್ಯಪ್ ಹೆಡ್ಲೆಸ್ ಸಿಸ್ಟಂ ಬಳಕೆದಾರರಂತೆ ಕ್ಯಾಮರಾವನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಬಹುದು."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"ವೈಬ್ರೇಷನ್ ನಿಯಂತ್ರಿಸಿ"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"ವೈಬ್ರೇಟರ್ ನಿಯಂತ್ರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"ವೈಬ್ರೇಟರ್ ಸ್ಥಿತಿಯನ್ನು ಪ್ರವೇಶಿಸಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string> @@ -1429,7 +1431,7 @@ <string name="ext_media_unsupported_notification_message" product="tv" msgid="1595482802187036532">"ಬೆಂಬಲಿಸಲಾಗುವ ಫಾರ್ಮ್ಯಾಟ್ನಲ್ಲಿ <xliff:g id="NAME">%s</xliff:g> ಅನ್ನು ಸೆಟಪ್ ಮಾಡಲು ಆಯ್ಕೆಮಾಡಿ."</string> <string name="ext_media_unsupported_notification_message" product="automotive" msgid="3412494732736336330">"ನೀವು ಸಾಧನವನ್ನು ಮರು ಫಾರ್ಮ್ಯಾಟ್ ಮಾಡಬೇಕಾಗಬಹುದು"</string> <string name="ext_media_badremoval_notification_title" msgid="4114625551266196872">"<xliff:g id="NAME">%s</xliff:g> ಅನಿರೀಕ್ಷಿತವಾಗಿ ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string> - <string name="ext_media_badremoval_notification_message" msgid="1986514704499809244">"ವಿಷಯ ನಷ್ಟವನ್ನು ತಪ್ಪಿಸಲು ತೆಗೆದುಹಾಕುವುದಕ್ಕೂ ಮುನ್ನ ಮಾಧ್ಯಮವನ್ನು ಎಜೆಕ್ಟ್ ಮಾಡಿ"</string> + <string name="ext_media_badremoval_notification_message" msgid="1986514704499809244">"ಕಂಟೆಂಟ್ ನಷ್ಟವನ್ನು ತಪ್ಪಿಸಲು ತೆಗೆದುಹಾಕುವುದಕ್ಕೂ ಮುನ್ನ ಮಾಧ್ಯಮವನ್ನು ಎಜೆಕ್ಟ್ ಮಾಡಿ"</string> <string name="ext_media_nomedia_notification_title" msgid="742671636376975890">"<xliff:g id="NAME">%s</xliff:g> ಅವರನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string> <string name="ext_media_nomedia_notification_message" msgid="2832724384636625852">"ಕೆಲವು ಕಾರ್ಯಚಟುವಟಿಕೆಗಳು ಸರಿಯಾಗಿ ಕೆಲಸ ಮಾಡದಿರಬಹುದು. ಹೊಸ ಸಂಗ್ರಹಣೆ ಸೇರಿಸಿ."</string> <string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"<xliff:g id="NAME">%s</xliff:g> ಎಜೆಕ್ಟ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> @@ -1442,7 +1444,7 @@ <string name="ext_media_missing_message" msgid="4408988706227922909">"ಸಾಧನವನ್ನು ಪುನಃ ಸೇರಿಸಿ"</string> <string name="ext_media_move_specific_title" msgid="8492118544775964250">"<xliff:g id="NAME">%s</xliff:g> ಸರಿಸಲಾಗುತ್ತಿದೆ"</string> <string name="ext_media_move_title" msgid="2682741525619033637">"ಡೇಟಾ ಸರಿಸಲಾಗುತ್ತಿದೆ"</string> - <string name="ext_media_move_success_title" msgid="4901763082647316767">"ವಿಷಯ ವರ್ಗಾವಣೆ ಪೂರ್ಣಗೊಂಡಿದೆ"</string> + <string name="ext_media_move_success_title" msgid="4901763082647316767">"ಕಂಟೆಂಟ್ ವರ್ಗಾವಣೆ ಪೂರ್ಣಗೊಂಡಿದೆ"</string> <string name="ext_media_move_success_message" msgid="9159542002276982979">"ವಿಷಯವನ್ನು <xliff:g id="NAME">%s</xliff:g> ಗೆ ಸರಿಸಲಾಗಿದೆ"</string> <string name="ext_media_move_failure_title" msgid="3184577479181333665">"ವಿಷಯವನ್ನು ಸರಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string> <string name="ext_media_move_failure_message" msgid="4197306718121869335">"ವಿಷಯವನ್ನು ಪುನಃ ಸರಿಸಲು ಪ್ರಯತ್ನಿಸಿ"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 2b50395da43f..1bdd30000074 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"이 권한이 있는 시스템 앱은 언제든지 시스템 카메라를 사용하여 사진을 촬영하고 동영상을 녹화할 수 있습니다. 또한 앱에 android.permission.CAMERA 권한이 필요합니다."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"애플리케이션 또는 서비스에서 카메라 기기 열림 또는 닫힘에 대한 콜백을 수신하도록 허용"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"이 앱은 애플리케이션이 카메라 기기를 열거나 닫을 때 콜백을 수신할 수 있습니다."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"애플리케이션 또는 서비스에서 헤드리스 시스템 사용자로 카메라에 액세스하도록 허용"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"이 앱에서 헤드리스 시스템 사용자로 카메라에 액세스할 수 있습니다."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"진동 제어"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"앱이 진동을 제어할 수 있도록 허용합니다."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"앱이 진동 상태에 액세스하도록 허용합니다."</string> @@ -810,7 +812,7 @@ <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"화면 잠금해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고, 잘못된 비밀번호 입력 횟수가 너무 많은 경우 태블릿을 잠그거나 태블릿에 있는 데이터를 모두 지웁니다."</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"화면 잠금 해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 Android TV 기기를 잠그거나 Android TV 기기의 데이터를 모두 삭제합니다."</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"화면 잠금 해제 시 잘못된 비밀번호를 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 인포테인먼트 시스템을 잠그거나 인포테인먼트 시스템의 데이터를 모두 삭제합니다."</string> - <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"화면 잠금해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고, 잘못된 비밀번호 입력 횟수가 너무 많은 경우 휴대전화를 잠그거나 휴대전화에 있는 데이터를 모두 지웁니다."</string> + <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"화면 잠금 해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고, 잘못된 비밀번호 입력 횟수가 너무 많은 경우 휴대전화를 잠그거나 휴대전화에 있는 데이터를 모두 지웁니다."</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"화면 잠금 해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 태블릿을 잠그거나 이 사용자의 데이터를 모두 삭제합니다."</string> <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"화면 잠금 해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 Android TV 기기를 잠그거나 사용자 데이터를 모두 삭제합니다."</string> <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"화면 잠금 해제 시 잘못된 비밀번호를 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 인포테인먼트 시스템을 잠그거나 이 프로필의 데이터를 모두 삭제합니다."</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 63e4b273ca58..d528ee3d9694 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Бул артыкчылыктуу тутум колдонмосу системанын камерасын каалаган убакта колдонуп, сүрөткө тартып, видео жаздыра алат. Ошондой эле колдонмого android.permission.CAMERA уруксатын берүү керек"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Колдонмого же кызматка камера ачылып же жабылып жатканда чалууларды кабыл алууга уруксат берүү."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Бул колдонмо камера ачылып (аны ачып жаткан колдонмо көрсөтүлгөндө) же жабылып жатканда чалууларды кабыл алат."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"титирөөнү башкаруу"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Колдонмого дирилдегичти көзөмөлдөө мүмкүнчүлүгүн берет."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Колдонмого дирилдөө абалына кирүүгө уруксат берет."</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 0f8ccf5ee1e4..8892f6047542 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"ສິດ ຫຼື ແອັບລະບົບນີ້ສາມາດຖ່າຍຮູບ ແລະ ບັນທຶກວິດີໂອໂດຍໃຊ້ກ້ອງຂອງລະບົບຕອນໃດກໍໄດ້. ຕ້ອງໃຊ້ສິດອະນຸຍາດ android.permission.CAMERA ໃຫ້ແອັບຖືນຳ"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນ ຫຼື ບໍລິການຮັບການເອີ້ນກັບກ່ຽວກັບອຸປະກອນກ້ອງຖືກເປີດ ຫຼື ປິດໄດ້."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"ແອັບນີ້ສາມາດຮັບການເອີ້ນກັບໄດ້ເມື່ອມີອຸປະກອນກ້ອງໃດຖືກເປີດ (ໂດຍແພັກເກດແອັບພລິເຄຊັນຫຍັງ) ຫຼື ຖືກປິດ."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນ ຫຼື ບໍລິການເຂົ້າເຖິງກ້ອງໃນຖານະຜູ້ໃຊ້ລະບົບແບບບໍ່ມີສ່ວນຫົວ."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"ແອັບນີ້ສາມາດເຂົ້າເຖິງກ້ອງໃນຖານະຜູ້ໃຊ້ລະບົບແບບບໍ່ມີສ່ວນຫົວໄດ້."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"ຄວບຄຸມການສັ່ນ"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"ອະນຸຍາດໃຫ້ແອັບຯຄວບຄຸມໂຕສັ່ນ."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"ອະນຸຍາດໃຫ້ແອັບເຂົ້າເຖິງສະຖານະການສັ່ນໄດ້."</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 33168458ffb9..8b4ff9793a15 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -503,6 +503,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ši privilegijuota arba sistemos programa gali daryti nuotraukas ir įrašyti vaizdo įrašus naudodama sistemos fotoaparatą bet kuriuo metu. Programai taip pat būtinas leidimas „android.permission.CAMERA“"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Leisti programai ar paslaugai sulaukti atgalinio skambinimo, kai atidaromas ar uždaromas fotoaparatas."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Ši programa gali sulaukti atgalinio skambinimo, kai atidaromas ar uždaromas (kurios nors programos) koks nors fotoaparatas."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Leisti programai ar paslaugai pasiekti vaizdo kamerą kaip sistemos be grafinės naudotojo sąsajos naudotojui."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Ši programa gali pasiekti vaizdo kamerą kaip sistemos be grafinės naudotojo sąsajos naudotojas."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"valdyti vibraciją"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Leidžiama programai valdyti vibravimą."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Programai leidžiama pasiekti vibratoriaus būseną."</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index ab6998cc552f..0b0d50204444 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -502,6 +502,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Šī privileģētā vai sistēmas lietotne var jebkurā brīdī uzņemt attēlus un ierakstīt videoklipus, izmantojot sistēmas kameru. Lietotnei nepieciešama arī atļauja android.permission.CAMERA."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Atļaut lietojumprogrammai vai pakalpojumam saņemt atzvanus par kameras ierīču atvēršanu vai aizvēršanu"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Šajā lietotnē var saņemt atzvanus, ja tiek atvērta vai aizvērta jebkāda kameras ierīce (atkarībā no lietojumprogrammas)."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"kontrolēt vibrosignālu"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Ļauj lietotnei kontrolēt vibrosignālu."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Ļauj lietotnei piekļūt vibrosignāla statusam."</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index ae0a0350a34b..109e967acad4 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Оваа привилегирана или системска апликација може да фотографира и да снима видеа со системската камера во секое време. Потребно е апликацијата да ја има и дозволата android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Дозволете апликацијатa или услугата да прима повратни повици за отворањето или затворањето на уредите со камера."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Оваа апликација може да прима повратни повици кога кој било уред со камера се отвора (од некоја апликација) или затвора."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Дозволете апликација или услуга да пристапува до камерата како Headless System User."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Апликацијава може да пристапи до камерата како Headless System User."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"контролирај вибрации"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Дозволува апликацијата да ги контролира вибрациите."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Ѝ дозволува на апликацијата да пристапи до состојбата на вибрации."</string> @@ -806,11 +808,11 @@ <string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"Дозволува сопственикот да ја ажурира апликацијата што претходно ја инсталирал без дејство од корисникот"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Постави правила за лозинката"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролирај ги должината и знаците што се дозволени за лозинки и PIN-броеви за отклучување екран."</string> - <string name="policylab_watchLogin" msgid="7599669460083719504">"Следи ги обидите за отклучување на екранот"</string> - <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Посматрај го бројот на неточни лозинки што се напишани за да се отклучи екранот и заклучи го таблетот или избриши ги сите податоци од него ако бидат напишани премногу неточни лозинки."</string> + <string name="policylab_watchLogin" msgid="7599669460083719504">"Следење на обидите за отклучување на екранот"</string> + <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Го следи бројот на неточни лозинки што се внесени за отклучување на екранот и го заклучува таблетот или ги брише сите податоци од него ако се внесат голем број неточни лозинки."</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Го следи бројот на погрешно внесени лозинки при отклучување на екранот и го заклучува уредот Android TV или ги брише сите податоци од уредот Android TV доколку се внесени премногу погрешни лозинки."</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Набљудувај го бројот на погрешно внесени лозинки при отклучување на екранот и заклучи го системот за информации и забава или избриши ги сите негови податоци доколку се внесени премногу погрешни лозинки."</string> - <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Посматрај го бројот на неточни лозинки што се напишани за да се отклучи екранот и заклучи го телефонот или избриши ги сите податоци од него ако бидат напишани премногу неточни лозинки."</string> + <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Го следи бројот на неточни лозинки што се внесени за отклучување на екранот и го заклучува телефонот или ги брише сите податоци од него ако се внесат голем број неточни лозинки."</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Набљудувај го бројот на погрешно внесени лозинки при отклучување на екранот и заклучи го таблетот или избриши ги сите податоци од овој корисник доколку се внесени премногу погрешни лозинки."</string> <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Го следи бројот на погрешно внесени лозинки при отклучување на екранот и го заклучува уредот Android TV или ги брише сите податоци од овој корисник доколку се внесени премногу погрешни лозинки."</string> <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Набљудувај го бројот на погрешно внесени лозинки при отклучување на екранот и заклучи го системот за информации и забава или избриши ги сите податоци од овој профил доколку се внесени премногу погрешни лозинки."</string> @@ -819,11 +821,11 @@ <string name="policydesc_resetPassword" msgid="4626419138439341851">"Промени го заклучувањето на екранот."</string> <string name="policylab_forceLock" msgid="7360335502968476434">"Заклучи го екранот"</string> <string name="policydesc_forceLock" msgid="1008844760853899693">"Контролирај како и кога се заклучува екранот."</string> - <string name="policylab_wipeData" msgid="1359485247727537311">"Избриши ги сите податоци"</string> + <string name="policylab_wipeData" msgid="1359485247727537311">"Бришење на сите податоци"</string> <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Избриши ги податоците во таблетот без предупредување со ресетирање на фабрички податоци."</string> <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ги брише податоците на вашиот уред Android TV без предупредување, така што ќе изврши ресетирање на фабричките податоци."</string> <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Избриши ги податоците во системот за информации и забава без предупредување со ресетирање на фабрички податоци."</string> - <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Избриши ги податоците во телефонот без предупредување со ресетирање на фабрички податоци."</string> + <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ги брише податоците од телефонот без предупредување вршејќи ресетирање на фабрички податоци."</string> <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Избриши ги податоците на профилот"</string> <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Избриши ги податоците на корисникот"</string> <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Избриши ги податоците на овој корисник на таблетот без предупредување."</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 80e80618185e..9ccf8b515985 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"സിസ്റ്റം ക്യാമറ ഉപയോഗിച്ച് ഏത് സമയത്തും ചിത്രങ്ങളെടുക്കാനും വീഡിയോകൾ റെക്കോർഡ് ചെയ്യാനും ഈ വിശേഷാധികാര അല്ലെങ്കിൽ സിസ്റ്റം ആപ്പിന് കഴിയും. ആപ്പിലും android.permission.CAMERA അനുമതി ഉണ്ടായിരിക്കണം"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ക്യാമറയുള്ള ഉപകരണങ്ങൾ ഓണാക്കുന്നതിനെയോ അടയ്ക്കുന്നതിനെയോ കുറിച്ചുള്ള കോൾബാക്കുകൾ സ്വീകരിക്കാൻ ആപ്പിനെയോ സേവനത്തെയോ അനുവദിക്കുക."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"ഏതെങ്കിലും ക്യാമറ ഉപകരണം തുറക്കുമ്പോഴോ (ഏത് ആപ്പ് ഉപയോഗിച്ചും) അടയ്ക്കുമ്പോഴോ ഈ ആപ്പിന് കോൾബാക്കുകൾ സ്വീകരിക്കാനാവും."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"ഹെഡ്ലെസ് സിസ്റ്റം യൂസറായി ക്യാമറ ആക്സസ് ചെയ്യാൻ ഒരു ആപ്പിനെയോ സേവനത്തെയോ അനുവദിക്കുക."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"ഹെഡ്ലെസ് സിസ്റ്റം യൂസറായി ക്യാമറ ആക്സസ് ചെയ്യാൻ ഈ ആപ്പിന് കഴിയും."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"വൈബ്രേറ്റുചെയ്യൽ നിയന്ത്രിക്കുക"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"വൈബ്രേറ്റർ നിയന്ത്രിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"വൈബ്രേറ്റ് ചെയ്യൽ ആക്സസ് ചെയ്യാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 7906fc1e255f..20de779773d7 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Энэ хамгаалагдсан эсвэл системийн апп нь системийн камер ашиглан ямар ч үед зураг авч, видео бичих боломжтой. Мөн түүнчлэн, апп нь android.permission.CAMERA-н зөвшөөрөлтэй байх шаардлагатай"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Аппликэйшн эсвэл үйлчилгээнд камерын төхөөрөмжүүдийг нээж эсвэл хааж байгаа тухай залгасан дуудлага хүлээн авахыг зөвшөөрөх."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Энэ апп нь дурын камерын төхөөрөмжийг нээх (ямар аппликэйшнээр болох) эсвэл хаах үед буцааж залгасан дуудлага хүлээн авах боломжтой."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"чичиргээг удирдах"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Апп нь чичиргээг удирдах боломжтой."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Аппыг чичиргээний төлөвт хандахыг зөвшөөрдөг."</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 74990485c63e..ebabd211dbbf 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"हे विशेषाधिकृत किंवा सिस्टम ॲप कधीही सिस्टम कॅमेरा वापरून फोटो आणि व्हिडिओ रेकॉर्ड करू शकते. ॲपकडे android.permission.CAMERA परवानगी असण्याचीदेखील आवश्यकता आहे"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"एखाद्या अॅप्लिकेशन किंवा सेवेला कॅमेरा डिव्हाइस सुरू किंवा बंद केल्याची कॉलबॅक मिळवण्याची अनुमती द्या."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"कोणतेही कॅमेरा डिव्हाइस (कोणत्या अॅप्लिकेशनने) सुरू किंवा बंद केले जाते तेव्हा हे ॲप कॉलबॅक मिळवू शकते."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"अॅप्लिकेशन किंवा सेवेला हेडलेस सिस्टीम वापरकर्ता म्हणून कॅमेरा अॅक्सेस करण्याची अनुमती द्या."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"हे अॅप हेडलेस सिस्टीम वापरकर्ता म्हणून कॅमेरा अॅक्सेस करू शकते."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"व्हायब्रेट नियंत्रित करा"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"अॅप ला व्हायब्रेटर नियंत्रित करण्यासाठी अनुमती देते."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"अॅपला व्हायब्रेटर स्थितीचा अॅक्सेस करण्याची अनुमती देते."</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 3faeb7f65b39..3d3fc7c83bd8 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Apl terlindung atau apl sistem ini boleh mengambil gambar dan merakam video menggunakan kamera sistem pada bila-bila masa. Apl juga perlu mempunyai kebenaran android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Benarkan aplikasi atau perkhidmatan menerima panggilan balik tentang peranti kamera yang dibuka atau ditutup."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Apl ini boleh menerima panggilan balik apabila mana-mana peranti kamera dibuka (oleh aplikasi) atau ditutup."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Benarkan aplikasi atau perkhidmatan mengakses kamera sebagai Pengguna Sistem Tanpa Kepala."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Apl ini boleh mengakses kamera sebagai Pengguna Sistem Tanpa Kepala."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"kawal getaran"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Membenarkan apl mengawal penggetar."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Membenarkan apl mengakses keadaan penggetar."</string> @@ -810,7 +812,7 @@ <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Memantau bilangan kata laluan yang tersilap ditaip apabila membuka skrin, dan mengunci tablet atau memadam semua data tablet jika terlalu banyak kesilapan menaip kata laluan."</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Pantau bilangan kata laluan salah yang ditaip semasa membuka kunci skrin, dan kunci peranti Android TV anda atau padamkan semua data peranti Android TV jika terlalu banyak kata laluan yang salah ditaip."</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Memantau bilangan kata laluan tidak betul yang ditaip semasa membuka kunci skrin dan mengunci sistem maklumat hibur atau memadam semua data sistem maklumat hibur jika terlalu banyak kata laluan yang tidak betul ditaip."</string> - <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Memantau bilangan kata laluan salah yang ditaip semasa membuka skrin, dan mengunci telefon atau memadam semua data telefon jika terlalu banyak kata laluan salah ditaip."</string> + <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Memantau bilangan kata laluan salah yang ditaip semasa membuka skrin, dan mengunci telefon atau memadamkan semua data telefon jika terlalu banyak kata laluan salah ditaip."</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Pantau bilangan kata laluan tidak betul yang ditaip semasa membuka kunci skrin dan kunci tablet atau padam semua data pengguna ini jika terlalu banyak kata laluan yang tidak betul ditaip."</string> <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Pantau bilangan kata laluan salah yang ditaip semasa membuka kunci skrin, dan kunci peranti Android TV anda atau padamkan semua data pengguna ini jika terlalu banyak kata laluan yang salah ditaip."</string> <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Memantau bilangan kata laluan tidak betul yang ditaip semasa membuka kunci skrin dan mengunci sistem maklumat hibur atau memadam semua data profil ini jika terlalu banyak kata laluan yang tidak betul ditaip."</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 5752fa3870da..39dd04316fc7 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -238,7 +238,7 @@ <string name="reboot_safemode_title" msgid="5853949122655346734">"safe mode ဖြင့် ပြန်လည် စ တင်ရန်"</string> <string name="reboot_safemode_confirm" msgid="1658357874737219624">"safe mode ကို ပြန်လည် စတင် မလား? ဒီလို စတင်ခြင်းဟာ သင် သွင်းထားသော တတိယပါတီ အပလီကေးရှင်းများအား ရပ်ဆိုင်းထားပါမည်။ ပုံမှန်အတိုင်း ပြန်စလျှင် ထိုအရာများ ပြန်လည် ရောက်ရှိလာပါမည်။"</string> <string name="recent_tasks_title" msgid="8183172372995396653">"လတ်တလော"</string> - <string name="no_recent_tasks" msgid="9063946524312275906">"မကြာမီတုန်းက အက်ပ်များ မရှိပါ"</string> + <string name="no_recent_tasks" msgid="9063946524312275906">"မကြာသေးမီက အက်ပ်များ မရှိပါ"</string> <string name="global_actions" product="tablet" msgid="4412132498517933867">"Tabletဆိုင်ရာရွေးချယ်မှုများ"</string> <string name="global_actions" product="tv" msgid="3871763739487450369">"Android TV ရွေးချယ်စရာများ"</string> <string name="global_actions" product="default" msgid="6410072189971495460">"ဖုန်းဆိုင်ရာရွေးချယ်မှုများ"</string> @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"ဤခွင့်ပြုထားသည့် သို့မဟုတ် စနစ်အက်ပ်က စနစ်ကင်မရာအသုံးပြုပြီး ဓာတ်ပုံနှင့် ဗီဒီယိုများကို အချိန်မရွေး ရိုက်ကူးနိုင်သည်။ အက်ပ်ကလည်း android.permission.CAMERA ခွင့်ပြုချက် ရှိရပါမည်"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ကင်မရာစက်များ ပွင့်နေခြင်း သို့မဟုတ် ပိတ်နေခြင်းနှင့် ပတ်သက်ပြီး ပြန်လည်ခေါ်ဆိုမှုများ ရယူရန် အပလီကေးရှင်း သို့မဟုတ် ဝန်ဆောင်မှုကို ခွင့်ပြုခြင်း။"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"(မည်သည့် အပလီကေးရှင်းကြောင့်) ကင်မရာစက်တစ်ခုခု ပွင့်နေသည့်အခါ သို့မဟုတ် ပိတ်နေသည့်အခါ ဤအက်ပ်က ပြန်လည်ခေါ်ဆိုမှုများ ရယူနိုင်သည်။"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"‘မမြင်နိုင်သော စနစ်အသုံးပြုသူ’ အဖြစ် ကင်မရာသုံးရန် အပလီကေးရှင်း (သို့) ဝန်ဆောင်မှုကို ခွင့်ပြုပါ။"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"ဤအက်ပ်သည် ‘မမြင်နိုင်သော စနစ်အသုံးပြုသူ’ အဖြစ် ကင်မရာသုံးနိုင်သည်။"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"တုန်ခုန်မှုအား ထိန်းချုပ်ခြင်း"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"အက်ပ်အား တုန်ခါစက်ကို ထိန်းချုပ်ခွင့် ပြုသည်။"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"အက်ပ်ကို တုန်ခါမှုအခြေအနေအား သုံးခွင့်ပေးပါ။"</string> @@ -2135,7 +2137,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"ဤအက်ပ်ကို အသံဖမ်းခွင့် ပေးမထားသော်လည်း ၎င်းသည် ဤ USB စက်ပစ္စည်းမှတစ်ဆင့် အသံများကို ဖမ်းယူနိုင်ပါသည်။"</string> <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"ပင်မစာမျက်နှာ"</string> <string name="accessibility_system_action_back_label" msgid="4205361367345537608">"နောက်သို့"</string> - <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"လတ်တလောသုံး အက်ပ်များ"</string> + <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"မကြာသေးမီက အက်ပ်များ"</string> <string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"အကြောင်းကြားချက်များ"</string> <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"အမြန် ဆက်တင်များ"</string> <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"ပါဝါ ဒိုင်ယာလော့"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 0cc55e48873f..5b1f77c9a068 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Denne privilegerte appen eller systemappen kan når som helst ta bilder og spille inn videoer med et systemkamera. Dette krever at appen også har tillatelsen android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Tillat at en app eller tjeneste mottar tilbakekallinger om kameraenheter som åpnes eller lukkes."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Denne appen kan motta tilbakekallinger når en kameraenhet blir åpnet (av hvilken app) eller lukket."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Tillat at en app eller tjeneste bruker kameraet som en hodeløs systembruker."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Denne appen kan bruke kameraet som en hodeløs systembruker."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"kontrollere vibreringen"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Lar appen kontrollere vibreringsfunksjonen."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Gir appen tilgang til vibreringstilstanden."</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index e38898eb468b..ad018dd6b05e 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -392,7 +392,7 @@ <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"एपहरू बन्द गर्नुहोस्"</string> <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"एपलाई अन्य अनुप्रयोगहरूको पृष्ठभूमि प्रक्रियाहरू बन्द गर्न अनुमति दिन्छ। यसले अन्य एपहरूलाई चल्नबाट रोक्न सक्दछ।"</string> <string name="permlab_systemAlertWindow" msgid="5757218350944719065">"यो एप अन्य एपहरूमाथि देखा पर्न सक्छ"</string> - <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"यो एप अन्य एपहरूमाथि वा स्क्रिनका अन्य भागहरूमा देखा पर्न सक्छ। यसले एपको सामान्य प्रयोगमा अवरोध पुर्याउन सक्छ र अन्य एपहरू देखा पर्ने तरिकालाई परिवर्तन गर्न सक्छ।"</string> + <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"यो एप अन्य एपहरूमाथि वा स्क्रिनका अन्य भागहरूमा देखा पर्न सक्छ। यसले एपको सामान्य प्रयोगमा अवरोध पुर्याउन सक्छ र अन्य एपहरू देखा पर्ने तरिकालाई परिवर्तन गर्न सक्छ।"</string> <string name="permlab_hideOverlayWindows" msgid="6382697828482271802">"एपका अन्य ओभरलेहरू लुकाउने अनुमति"</string> <string name="permdesc_hideOverlayWindows" msgid="5660242821651958225">"यो एपले सिस्टमलाई एपहरूबाट उत्पन्न हुने ओभरलेहरू यो एपको माथि नदेखिने गरी लुकाउन अनुरोध गर्न सक्छ।"</string> <string name="permlab_runInBackground" msgid="541863968571682785">"पृष्ठभूमिमा चलाउनुहोस्"</string> @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"प्रणालीको यस विशेषाधिकार प्राप्त अनुप्रयोगले जुनसुकै बेला प्रणालीको क्यामेरा प्रयोग गरी फोटो खिच्न र भिडियो रेकर्ड गर्न सक्छ। एपसँग पनि android.permission.CAMERA प्रयोग गर्ने अनुमति हुनु पर्छ"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"कुनै एप वा सेवालाई खोलिँदै वा बन्द गरिँदै गरेका क्यामेरा यन्त्रहरूका बारेमा कलब्याक प्राप्त गर्ने अनुमति दिनुहोस्।"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"कुनै क्यामेरा यन्त्र खोलिँदा (कुन अनुप्रयोगले खोलेको भन्ने बारेमा) वा बन्द गरिँदा यो एपले कलब्याक प्राप्त गर्न सक्छ।"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"कुनै एप वा सेवालाई हेडलेस सिस्टमको प्रयोगकर्ताका रूपमा क्यामेरा एक्सेस गर्ने अनुमति दिनुहोस्।"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"यो एपले हेडलेस सिस्टमको प्रयोगकर्ताका रूपमा क्यामेरा एक्सेस गर्न सक्छ।"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"कम्पन नियन्त्रण गर्नुहोस्"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"एपलाई भाइब्रेटर नियन्त्रण गर्न अनुमति दिन्छ।"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"यो एपलाई कम्पनको स्थितिमाथि पहुँच राख्न दिनुहोस्।"</string> @@ -810,7 +812,7 @@ <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप भएको संख्या निरीक्षण गर्नुहोस् र यदि निकै धेरै गलत पासवर्डहरू टाइप भएका छन भने ट्याब्लेट लक गर्नुहोस् वा ट्याब्लेटका सबै डेटा मेट्नुहोस्।"</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android टिभी यन्त्र लक गर्नुहोस् वा डिभाइसमा भएको सम्पूर्ण डेटा मेटाउनुहोस्।"</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"स्क्रिन अनलक गर्दा कति पटक गलत पासवर्ड टाइप गरिन्छ भन्ने कुरा निगरानी गरियोस् र अत्यन्तै धेरै पटक गलत पासवर्ड टाइप गरिएका खण्डमा यो इन्फोटेनमेन्ट प्रणाली लक गरियोस् वा यस इन्फोटेनमेन्ट प्रणालीका सबै डेटा मेटाइयोस्।"</string> - <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"स्क्रिनअनलक गर्दा गलत पासवर्ड टाइप भएको संख्या निरीक्षण गर्नुहोस् र यदि निकै धेरै गलत पासवर्डहरू टाइप भएका छन भने फोन लक गर्नुहोस् वा फोनका सबै डेटा मेट्नुहोस्।"</string> + <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"स्क्रिन अनलक गर्दा कति पटक गलत पासवर्ड टाइप भएको छ हेर्नुहोस् र निकै धेरै पटक गलत पासवर्ड टाइप भएको भने फोन लक गर्नुहोस् वा फोनका सबै डेटा मेट्नुहोस्।"</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप संख्या अनुगमन गर्नुहोस्, र यदि निकै धेरै गलत पासवर्डहरू टाइप गरिएमा ट्याब्लेट लक गर्नुहोस् वा प्रयोगकर्ताको डेटा मेटाउनुहोस्।"</string> <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android टिभी यन्त्र लक गर्नुहोस् वा यो प्रयोगकर्ताको सम्पूर्ण डेटा मेटाउनुहोस्।"</string> <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"स्क्रिन अनलक गर्दा कति पटक गलत पासवर्ड टाइप गरिन्छ भन्ने कुरा निगरानी गरियोस् र अत्यन्तै धेरै पटक गलत पासवर्ड टाइप गरिएका खण्डमा यो इन्फोटेनमेन्ट प्रणाली लक गरियोस् वा यस प्रोफाइलका सबै डेटा मेटाइयोस्।"</string> @@ -820,10 +822,10 @@ <string name="policylab_forceLock" msgid="7360335502968476434">"स्क्रिन लक गर्ने"</string> <string name="policydesc_forceLock" msgid="1008844760853899693">"कसरी र कहिले स्क्रिन लक गर्ने भन्ने कुरा सेट गर्न"</string> <string name="policylab_wipeData" msgid="1359485247727537311">"सबै डेटा मेट्ने"</string> - <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"एउटा फ्याक्ट्रि डेटा रिसेट गरेर चेतावनी नआउँदै ट्याबल्टको डेटा मेट्नुहोस्।"</string> + <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"फ्याक्ट्रूी रिसेट गरेर चेतावनी नआउँदै ट्याबल्टको डेटा मेट्नुहोस्।"</string> <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"फ्याक्ट्री डेटा रिसेट गरेर चेतावनी नदिइकन आफ्नो Android टिभी डिभाइसको डेटा मेटाउनुहोस्।"</string> <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"यो इन्फोटेनमेन्ट प्रणालीको डेटा कुनै चेतावनीविनै फ्याक्ट्री डेटा रिसेट गरेर मेटाइयोस्।"</string> - <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"एउटा फ्याक्ट्रि डेटा रिसेट गरेर चेतावनी नदिइकन फोनको डेटा मेट्न।"</string> + <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"फ्याक्ट्रूी रिसेट गरेर चेतावनी नदिइकन फोनको डेटा मेट्न।"</string> <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"प्रोफाइल डेटा मेटाइयोस्"</string> <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"प्रयोगकर्ता डेटा मेट्नुहोस्"</string> <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"चेतावनी बिना यो ट्याब्लेटमा यस प्रयोगकर्ताको डेटा मेट्नुहोस्।"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 7d48a017b1be..50e261fd3bbc 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Deze gemachtigde app of systeem-app kan op elk gewenst moment foto\'s maken en video\'s opnemen met een systeemcamera. De app moet ook het recht android.permission.CAMERA hebben."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Een app of service toestaan callbacks te ontvangen over camera-apparaten die worden geopend of gesloten."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Deze app kan callbacks ontvangen als een camera-apparaat wordt geopend (en door welke app) of gesloten."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Toestaan dat een app of service toegang tot de camera heeft als gebruiker van een systeem zonder interface."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Deze app heeft toegang tot de camera als gebruiker van een systeem zonder interface."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"trilling beheren"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Hiermee kan de app de trilstand beheren."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Hiermee heeft de app toegang tot de status van de trilstand."</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 64736e556fbb..898fab2c9323 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"ବିଶେଷ ଅଧିକାର ଥିବା ଏହି ଆପ୍ କିମ୍ବା ସିଷ୍ଟମ୍ ଆପ୍ ଯେ କୌଣସି ସମୟରେ ଏକ ସିଷ୍ଟମ୍ କ୍ୟାମେରା ବ୍ୟବହାର କରି ଛବି ଉଠାଇପାରିବ ଏବଂ ଭିଡିଓ ରେକର୍ଡ କରିପାରିବ। ଆପରେ ମଧ୍ୟ android.permission.CAMERA ଅନୁମତି ଆବଶ୍ୟକ"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"କ୍ୟାମେରା ଡିଭାଇସଗୁଡ଼ିକ ଖୋଲିବା କିମ୍ବା ବନ୍ଦ କରିବା ବିଷୟରେ କଲବ୍ୟାକଗୁଡ଼ିକ ପାଇବାକୁ ଏକ ଆପ୍ଲିକେସନ୍ କିମ୍ବା ସେବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"ଯେ କୌଣସି କ୍ୟାମେରା ଡିଭାଇସ୍ ଖୋଲାଗଲେ (କେଉଁ ଆପ୍ଲିକେସନ୍ ଦ୍ୱାରା) କିମ୍ବା ବନ୍ଦ କରାଗଲେ ଏହି ଆପ୍ କଲବ୍ୟାକ୍ ପାଇପାରିବ।"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"ହେଡଲେସ ସିଷ୍ଟମ ୟୁଜର ଭାବେ କେମେରାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏକ ଆପ୍ଲିକେସନ କିମ୍ବା ସେବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"ଏହି ଆପ ହେଡଲେସ ସିଷ୍ଟମ ୟୁଜର ଭାବେ କେମେରାକୁ ଆକ୍ସେସ କରିପାରିବ।"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"କମ୍ପନ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"ଆପ୍କୁ, ଭାଇବ୍ରେଟର୍ ନିୟନ୍ତ୍ରଣ କରିବାକୁ ଦେଇଥାଏ।"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"ଭାଇବ୍ରେଟର୍ ସ୍ଥିତି ଆକ୍ସେସ୍ କରିବାକୁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string> @@ -806,11 +808,11 @@ <string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"ୟୁଜର ଆକ୍ସନ ବିନା ପୂର୍ବରୁ ଇନଷ୍ଟଲ କରାଯାଇଥିବା ଆପକୁ ଅପଡେଟ କରିବା ପାଇଁ ଏହା ହୋଲ୍ଡରକୁ ଅନୁମତି ଦିଏ"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"ପାସ୍ୱର୍ଡ ନିୟମାବଳୀ ସେଟ୍ କରନ୍ତୁ"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"ଲକ୍ ସ୍କ୍ରୀନ୍ ପାସ୍ୱର୍ଡ ଓ PINରେ ଅନୁମୋଦିତ ଦୀର୍ଘତା ଓ ବର୍ଣ୍ଣ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ।"</string> - <string name="policylab_watchLogin" msgid="7599669460083719504">"ସ୍କ୍ରୀନ୍-ଅନଲକ୍ କରିବା ଉଦ୍ୟମ ନୀରିକ୍ଷଣ କରନ୍ତୁ"</string> + <string name="policylab_watchLogin" msgid="7599669460083719504">"ସ୍କ୍ରିନ-ଅନଲକ କରିବା ଉଦ୍ୟମ ନୀରିକ୍ଷଣ କରିବା"</string> <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରିଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟାବଲେଟ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଟାବଲେଟ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ସ୍କ୍ରିନ୍ ଅନ୍ଲକ୍ କରିବା ସମୟରେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱାର୍ଡଗୁଡ଼ିକର ସଂଖ୍ୟାକୁ ନିରୀକ୍ଷଣ କରନ୍ତୁ ଏବଂ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଲକ୍ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱାର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଆପଣଙ୍କ Android TV ଡିଭାଇସ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ସ୍କ୍ରିନ ଅନଲକ କରିବା ସମୟରେ ଟାଇପ କରାଯାଇଥିବା ଭୁଲ ପାସୱାର୍ଡର ସଂଖ୍ୟାକୁ ମନିଟର କରନ୍ତୁ ଏବଂ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମକୁ ଲକ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକଗୁଡ଼ିଏ ଭୁଲ ପାସୱାର୍ଡ ଟାଇପ କରାଯାଇଥାଏ ତେବେ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମର ସମସ୍ତ ଡାଟା ଖାଲି କରନ୍ତୁ।"</string> - <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରିଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଫୋନ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଫୋନ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string> + <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ଟାଇପ କରାଯାଇଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ। ସ୍କ୍ରିନ ଅନଲକ କରିବାବେଳେ ଏବଂ ଫୋନକୁ ଲକ କରିବା ସମୟରେ ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ କରାଯାଇଥାଏ, ତେବେ ଫୋନର ସମସ୍ତ ଡାଟା ଡିଲିଟ କରେ।"</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟାବଲେଟ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string> <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ସ୍କ୍ରିନ୍ ଅନ୍ଲକ୍ କରିବା ସମୟରେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱାର୍ଡଗୁଡ଼ିକର ସଂଖ୍ୟାକୁ ନିରୀକ୍ଷଣ କରନ୍ତୁ ଏବଂ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଲକ୍ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱାର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string> <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ସ୍କ୍ରିନ ଅନଲକ କରିବା ସମୟରେ ଟାଇପ କରାଯାଇଥିବା ଭୁଲ ପାସୱାର୍ଡର ସଂଖ୍ୟାକୁ ମନିଟର କରନ୍ତୁ ଏବଂ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମକୁ ଲକ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକଗୁଡ଼ିଏ ଭୁଲ ପାସୱାର୍ଡ ଟାଇପ କରାଯାଇଥାଏ ତେବେ ଏହି ପ୍ରୋଫାଇଲର ସମସ୍ତ ଡାଟା ଖାଲି କରନ୍ତୁ।"</string> @@ -823,7 +825,7 @@ <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ସେଟିଙ୍ଗ କରାଇ ଟାବ୍ଲେଟ୍ର ଡାଟା ଲିଭାଇଥାଏ।"</string> <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ଏକ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍ କରି ବିନା ଚେତାବନୀରେ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ର ଡାଟା ଲିଭାନ୍ତୁ।"</string> <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ଏକ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ କରି ବିନା ଚେତାବନୀରେ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମର ଡାଟା ଖାଲି କରନ୍ତୁ।"</string> - <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍ କରି ଫୋନ୍ର ଡାଟା ଲିଭାଇଥାଏ।"</string> + <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ କରି ଫୋନର ଡାଟା ଲିଭାଇଥାଏ।"</string> <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ପ୍ରୋଫାଇଲ ଡାଟା ଖାଲି କରନ୍ତୁ"</string> <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ୟୁଜର୍ ଡାଟା ଲିଭାନ୍ତୁ"</string> <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ବିନା ଚେତାବନୀରେ ଏହି ଟାବଲେଟରେ ଥିବା ଏହି ୟୁଜରଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string> @@ -2104,7 +2106,7 @@ <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ନିୟମିତ ମୋଡ୍ ସୂଚନା ବିଜ୍ଞପ୍ତି"</string> <string name="dynamic_mode_notification_title" msgid="1388718452788985481">"ବେଟେରୀ ସେଭର ଚାଲୁ କରାଯାଇଛି"</string> <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"ବ୍ୟାଟେରୀ ଲାଇଫ ବଢ଼ାଇବା ପାଇଁ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କମ୍ କରିବା"</string> - <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"ବ୍ୟାଟେରୀ ସେଭର୍"</string> + <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"ବେଟେରୀ ସେଭର"</string> <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"ବ୍ୟାଟେରୀ ସେଭର୍ ବନ୍ଦ ଅଛି"</string> <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"ଫୋନରେ ଯଥେଷ୍ଟ ଚାର୍ଜ ଅଛି। ଫିଚରଗୁଡ଼ିକ ଆଉ ପ୍ରତିବନ୍ଧିତ ନୁହେଁ।"</string> <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"ଟାବଲେଟରେ ଯଥେଷ୍ଟ ଚାର୍ଜ ଅଛି। ଫିଚରଗୁଡ଼ିକ ଆଉ ପ୍ରତିବନ୍ଧିତ ନୁହେଁ।"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 8cfc0e4279d1..8034be88dc74 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"ਇਹ ਵਿਸ਼ੇਸ਼ ਅਧਿਕ੍ਰਿਤ ਜਾਂ ਸਿਸਟਮ ਐਪ ਕਿਸੇ ਵੇਲੇ ਵੀ ਸਿਸਟਮ ਕੈਮਰੇ ਨੂੰ ਵਰਤ ਕੇ ਤਸਵੀਰਾਂ ਖਿੱਚ ਸਕਦੀ ਹੈ ਅਤੇ ਵੀਡੀਓ ਫ਼ਾਈਲਾਂ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਨੂੰ ਵੀ android.permission.CAMERA ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ਐਪਲੀਕੇਸ਼ਨ ਜਾਂ ਸੇਵਾ ਨੂੰ ਕੈਮਰਾ ਡੀਵਾਈਸਾਂ ਦੇ ਚਾਲੂ ਜਾਂ ਬੰਦ ਕੀਤੇ ਜਾਣ ਬਾਰੇ ਕਾਲਬੈਕ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ।"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"ਇਹ ਐਪ ਕੋਈ ਵੀ ਕੈਮਰਾ ਡੀਵਾਈਸ ਚਾਲੂ ਹੋਣ (ਕਿਸ ਐਪਲੀਕੇਸ਼ਨ ਰਾਹੀਂ) ਜਾਂ ਬੰਦ ਹੋਣ \'ਤੇ ਕਾਲਬੈਕ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦੀ ਹੈ।"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"ਕਿਸੇ ਐਪਲੀਕੇਸ਼ਨ ਜਾਂ ਸੇਵਾ ਨੂੰ Headless System ਦੇ ਵਰਤੋਂਕਾਰ ਵਜੋਂ ਕੈਮਰੇ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦਿਓ।"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"ਇਹ ਐਪ Headless System ਦੇ ਵਰਤੋਂਕਾਰ ਵਜੋਂ ਕੈਮਰੇ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੀ ਹੈ।"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"ਵਾਈਬ੍ਰੇਸ਼ਨ ਤੇ ਨਿਯੰਤਰਣ ਪਾਓ"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"ਐਪ ਨੂੰ ਵਾਈਬ੍ਰੇਟਰ ਤੇ ਨਿਯੰਤਰਣ ਪਾਉਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"ਐਪ ਨੂੰ ਥਰਥਰਾਹਟ ਸਥਿਤੀ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦਿੰਦਾ ਹੈ।"</string> @@ -810,7 +812,7 @@ <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਟੈਬਲੈੱਟ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਟੈਬਲੈੱਟ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਆਪਣੇ Android TV ਡੀਵਾਈਸ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਆਪਣੇ Android TV ਡੀਵਾਈਸ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦੀ ਨਿਗਰਾਨੀ ਕਰੋ ਅਤੇ ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ, ਤਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string> - <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਫ਼ੋਨ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਫ਼ੋਨ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string> + <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਗਿਣਤੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰੋ ਅਤੇ ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ, ਤਾਂ ਫ਼ੋਨ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਫ਼ੋਨ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਟੈਬਲੈੱਟ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਟੈਬਲੈੱਟ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string> <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਆਪਣੇ Android TV ਡੀਵਾਈਸ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string> <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦੀ ਨਿਗਰਾਨੀ ਕਰੋ ਅਤੇ ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ, ਤਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਇਸ ਪ੍ਰੋਫਾਈਲ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index a40fae86a5f3..21e42aa6bad9 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -503,6 +503,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ta aplikacja systemowa z podwyższonymi uprawnieniami może w dowolnym momencie robić zdjęcia i nagrywać filmy przy użyciu aparatu systemowego. Wymaga przyznania uprawnień android.permission.CAMERA również aplikacji."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Zezwól na dostęp aplikacji lub usługi na otrzymywanie wywoływania zwrotnego o urządzeniach z aparatem, kiedy są one uruchamiane lub zamykane."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Ta aplikacja może otrzymywać wywołania zwrotne, kiedy urządzenie z aparatem jest uruchamiane (przez jaką aplikację) albo zamykane."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"sterowanie wibracjami"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Pozwala aplikacji na sterowanie wibracjami."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Zezwala aplikacji na dostęp do stanu wibracji"</string> @@ -812,7 +816,7 @@ <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Przy odblokowywaniu ekranu monitoruj, ile razy wpisano nieprawidłowe hasło i blokuj tablet lub usuń z niego wszystkie dane, jeśli nieprawidłowe hasło podano zbyt wiele razy."</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitorowanie liczby nieudanych prób odblokowania ekranu za pomocą hasła oraz blokowanie urządzenia z Androidem TV lub kasowanie z niego wszystkich danych w razie wpisania błędnego hasła zbyt wiele razy."</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorowanie przypadków nieprawidłowego wpisania hasła podczas odblokowywania ekranu i blokowanie systemu multimedialno-rozrywkowego lub usuwanie z niego wszystkich danych przy zbyt dużej liczbie błędnych prób."</string> - <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Przy odblokowywaniu ekranu monitoruje, ile razy wpisano nieprawidłowe hasło, i blokuje telefon lub usuwa z niego wszystkie dane, jeśli nieprawidłowe hasło podano zbyt wiele razy"</string> + <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Przy odblokowywaniu ekranu monitoruje, ile razy wpisano nieprawidłowe hasło, i blokuje telefon lub usuwa z niego wszystkie dane, jeśli nieprawidłowe hasło podano zbyt wiele razy."</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorowanie, ile razy wpisano błędne hasło podczas odblokowywania ekranu, oraz blokowanie tabletu albo kasowanie wszystkich danych tego użytkownika, gdy zbyt wiele razy wpisano błędne hasło."</string> <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitorowanie, ile razy wpisano błędne hasło podczas odblokowywania ekranu, oraz blokowanie urządzenia z Androidem TV albo kasowanie wszystkich danych tego użytkownika, gdy błędne hasło zostało wpisane zbyt wiele razy."</string> <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorowanie przypadków nieprawidłowego wpisania hasła podczas odblokowywania ekranu i blokowanie systemu multimedialno-rozrywkowego lub usuwanie wszystkich danych z profilu przy zbyt dużej liczbie błędnych prób."</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 70f80a81750b..eb7a802ee4be 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -502,6 +502,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esse app do sistema ou com privilégios pode tirar fotos e gravar vídeos a qualquer momento usando a câmera do sistema. É necessário que o app tenha também a permissão android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permitir que um aplicativo ou serviço receba callbacks sobre dispositivos de câmera sendo abertos ou fechados."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Esse app pode receber callbacks quando um dispositivo de câmera é aberto (por qualquer app) ou fechado."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Permitir que um aplicativo ou serviço acesse a câmera como usuário do sistema headless."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Este app pode acessar a câmera como um usuário do sistema headless."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"controlar vibração"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Permite que o app controle a vibração."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Permite que o app acesse o estado da vibração."</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 7341500d8b46..c83491e8d760 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -502,6 +502,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esta app do sistema ou privilegiada pode tirar fotos e gravar vídeos através de uma câmara do sistema em qualquer altura. Também necessita da autorização android.permission.CAMERA para a app."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permitir que uma app ou um serviço receba chamadas de retorno sobre dispositivos de câmara que estão a ser abertos ou fechados"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Esta app pode receber chamadas de retorno quando qualquer dispositivo de câmara está a ser aberto (e por que app) ou fechado."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Permita que uma aplicação ou um serviço aceda à câmara como utilizador do sistema sem interface."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Esta app pode aceder à câmara como utilizador do sistema sem interface."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"controlar vibração"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Permite à app controlar o vibrador."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Permite que a app aceda ao estado de vibração."</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 70f80a81750b..eb7a802ee4be 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -502,6 +502,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esse app do sistema ou com privilégios pode tirar fotos e gravar vídeos a qualquer momento usando a câmera do sistema. É necessário que o app tenha também a permissão android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permitir que um aplicativo ou serviço receba callbacks sobre dispositivos de câmera sendo abertos ou fechados."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Esse app pode receber callbacks quando um dispositivo de câmera é aberto (por qualquer app) ou fechado."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Permitir que um aplicativo ou serviço acesse a câmera como usuário do sistema headless."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Este app pode acessar a câmera como um usuário do sistema headless."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"controlar vibração"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Permite que o app controle a vibração."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Permite que o app acesse o estado da vibração."</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index cf428b70b7c9..3389c6356c36 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -502,6 +502,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Această aplicație de sistem privilegiată poate să fotografieze și să înregistreze videoclipuri folosind o cameră de sistem în orice moment. Necesită și permisiunea android.permission.CAMERA pentru aplicație"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permite unei aplicații sau unui serviciu să primească apeluri inverse atunci când sunt deschise sau închise dispozitive cu cameră."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Această aplicație poate primi apeluri inverse atunci când este deschis (de aplicație) sau închis orice dispozitiv cu cameră."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Permite unei aplicații sau unui serviciu să acceseze camera ca utilizator de sistem fără interfață grafică."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Aplicația poate accesa camera ca utilizator de sistem fără interfață grafică."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"controlează vibrarea"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Permite aplicației să controleze mecanismul de vibrare."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Permite aplicației să acceseze modul de vibrații."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 4986c10f7522..dec9eb3a37ba 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -503,6 +503,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Это привилегированное или системное приложение может в любое время делать фотографии и записывать видео с помощью камеры. Для этого приложению также требуется разрешение android.permission.CAMERA."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Разрешить приложению или сервису получать обратные вызовы при открытии и закрытии камер"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Это приложение сможет получать обратные вызовы при открытии (с указанием открывающего приложения) и закрытии любых камер."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Разрешить приложению или сервису доступ к камере на правах консольного системного пользователя"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"У этого приложения есть доступ к камере на правах консольного системного пользователя."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"Управление функцией вибросигнала"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Приложение сможет контролировать вибросигналы."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Приложение сможет получать доступ к состоянию виброотклика."</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 1b6230d3d340..1a71238ea7f4 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"මෙම වරප්රසාද ලත් හෝ පද්ධති යෙදුමට ඕනෑම වේලාවක පද්ධති කැමරාව භාවිත කර පින්තූර ගැනීමට සහ වීඩියෝ පටිගත කිරීමට හැකිය. යෙදුම විසින් රඳවා තබා ගැනීමට android.permission.CAMERA ප්රවේශයද අවශ්ය වේ"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"විවෘත වෙමින් හෝ වැසෙමින් පවතින කැමරා උපාංග පිළිබඳ පසු ඇමතුම් ලබා ගැනීමට යෙදුමකට හෝ සේවාවකට ඉඩ දෙන්න."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"මෙම යෙදුමට ඕනෑම කැමරා උපාංගයක් විවෘත වෙමින් පවතින විට (කුමන යෙදුමකින්) හෝ වැසෙමින් පවතින විට පසු ඇමතුම් ලබා ගැනීමට හැකිය."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"කම්පනය පාලනය කිරීම"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"කම්පකය පාලනයට යෙදුමට අවසර දෙන්න."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"යෙදුමට කම්පන තත්ත්වයට ප්රවේශ වීමට ඉඩ දෙන්න."</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 9784308ca57f..37c1540a9ce3 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -503,6 +503,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Táto oprávnená alebo systémová aplikácia môže kedykoľvek fotiť a nahrávať videá fotoaparátom systému. Aplikácia musí mať tiež povolenie android.permission.CAMERA."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Povoliť aplikácii alebo službe prijímať spätné volanie, keď sú zariadenia s kamerou otvorené alebo zatvorené."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Táto aplikácia môže prijímať spätné volania pri otváraní alebo zatváraní ľubovoľného fotoaparátu (s infomáciou o aplikácii, ktorá to robí)."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Povoľte aplikácii alebo službe prístup ku kamere ako systém bez grafického rozhrania."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Táto aplikácia má prístup ku kamere ako systém bez grafického rozhrania."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"ovládať vibrovanie"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Umožňuje aplikácii ovládať vibrácie."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Povoľuje aplikácii prístup k stavu vibrátora."</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 16ea77f1b0c6..3999f9f30cfe 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -503,6 +503,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ta prednostna ali sistemska aplikacija lahko z vgrajenim fotoaparatom kadar koli snema fotografije in videoposnetke. Aplikacija mora imeti omogočeno tudi dovoljenje android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Aplikaciji ali storitvi dovoli prejemanje povratnih klicev o odpiranju ali zapiranju naprav s fotoaparati."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Ta aplikacija lahko prejema povratne klice, ko se odpira (s katero aplikacijo) ali zapira katera koli naprava s fotoaparatom."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Aplikaciji ali storitvi dovoli dostop do fotoaparata kot sistemskemu uporabniku brez grafičnega uporabniškega vmesnika."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Ta aplikacija lahko dostopa do fotoaparata kot sistemski uporabnik brez grafičnega uporabniškega vmesnika."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"nadzor vibriranja"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Aplikaciji omogoča nadzor vibriranja."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Aplikaciji dovoljuje dostop do stanja vibriranja."</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index a3d648f204fa..a9d748687b5a 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ky aplikacion sistemi ose i privilegjuar mund të nxjerrë fotografi dhe të regjistrojë video duke përdorur një kamerë në çdo moment. Kërkon që autorizimi i android.permission.CAMERA të mbahet edhe nga aplikacioni"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Lejo që një aplikacion ose shërbim të marrë telefonata mbrapsht për pajisjet e kamerës që hapen ose mbyllen."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Ky aplikacion mund të marrë telefonata mbrapsht kur hapet ose mbyllet një pajisje e kamerës (nga një aplikacion)."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"kontrollo dridhjen"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Lejon aplikacionin të kontrollojë dridhësin."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Lejon që aplikacioni të ketë qasje te gjendja e dridhësit."</string> @@ -806,11 +810,11 @@ <string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"Lejon zotëruesin të përditësojë aplikacionin që e ka instaluar më parë pa veprimin e përdoruesit"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Cakto rregullat e fjalëkalimit"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrollo gjatësinë dhe karakteret e lejuara në fjalëkalimet dhe kodet PIN të kyçjes së ekranit."</string> - <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitoro tentativat e shkyçjes së ekranit"</string> + <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitoron tentativat e shkyçjes së ekranit"</string> <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitoro numrin e fjalëkalimeve të shkruar gabim kur shkyç ekranin. Kyç tabletin ose fshi të gjitha të dhënat e tij, nëse shkruhen shumë fjalëkalime të pasakta."</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin dhe kyç pajisjen tënde Android TV ose spastro të gjitha të dhënat e pajisjes sate Android TV nëse shkruhen gabim shumë fjalëkalime."</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin dhe kyç sistemin info-argëtues ose spastro të gjitha të dhënat e tij nëse shkruhen shumë fjalëkalime të gabuara."</string> - <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitoro numrin e fjalëkalimeve të shkruar gabim kur shkyç ekranin. Kyç telefonin ose fshi të gjitha të dhënat e tij, nëse shkruhen shumë fjalëkalime të pasakta."</string> + <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitoron numrin e fjalëkalimeve të shkruar gabim kur shkyç ekranin. Kyç telefonin ose fshin të gjitha të dhënat e tij, nëse shkruhen shumë fjalëkalime të pasakta."</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin. Kyçe tabletin ose spastro të gjitha të dhënat e këtij përdoruesi nëse shkruhen shumë fjalëkalime të gabuara."</string> <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin dhe kyçe pajisjen tënde Android TV ose spastro të gjitha të dhënat e këtij përdoruesi nëse shkruhen shumë fjalëkalime të gabuara."</string> <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin dhe kyç sistemin info-argëtues ose spastro të gjitha të dhënat e këtij profili nëse shkruhen shumë fjalëkalime të gabuara."</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 712aeb99dd98..b5aefaafbec4 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -502,6 +502,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ова привилегована системска апликација може да снима слике и видео снимке помоћу камере система у било ком тренутку. Апликација треба да има и дозволу android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Дозволите апликацији или услузи да добија повратне позиве о отварању или затварању уређаја са камером."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Ова апликација може да добија повратне позиве када се било који уређај са камером отвара или затвара (помоћу неке апликације)."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Дозволите апликацији или услузи да приступа камери као корисник система без графичког корисничког интерфејса."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Ова апликација може да приступа камери као корисник система без графичког корисничког интерфејса."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"контрола вибрације"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Дозвољава апликацији да контролише вибрацију."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Дозвољава апликацији да приступа стању вибрирања."</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 3624f71d1e7e..1745ff00f82c 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Denna systemapp med särskild behörighet kan ta bilder och spela in videor med systemets kamera när som helst. Appen måste även ha behörigheten android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Tillåt att en app eller tjänst får återanrop när en kameraenhet öppnas eller stängs."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Den här appen kan få återanrop när en kameraenhet öppnas (efter app) eller stängs."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"styra vibration"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Tillåter att appen styr vibrationen."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Appen beviljas åtkomst till vibrationsstatus."</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 1ec2eeff1575..341a8d322f7a 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Programu hii ya mfumo au inayopendelewa inaweza kupiga picha na kurekodi video ikitumia kamera ya mfumo wakati wowote. Inahitaji ruhusa ya android.permission.CAMERA iwepo kwenye programu pia"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Ruhusu programu au huduma ipokee simu zinazopigwa tena kuhusu vifaa vya kamera kufunguliwa au kufungwa."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Programu hii inaweza kupokea misimbo ya kutekeleza wakati kifaa chochote cha kamera kinafunguliwa (na programu) au kufungwa."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"Kudhibiti mtetemo"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Inaruhusu programu kudhibiti kitingishi."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Huruhusu programu kufikia hali ya kitetemeshaji."</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 8c2ddb0cf395..fa8f20d3558f 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"இந்த முன்னுரிமை பெற்ற அல்லது சிஸ்டம் ஆப்ஸால் சிஸ்டம் கேமராவைப் பயன்படுத்தி எப்போது வேண்டுமானாலும் படங்களை எடுக்கவோ வீடியோக்களை ரெக்கார்டு செய்யவோ முடியும். android.permission.CAMERA அனுமதியும் ஆப்ஸிற்குத் தேவை"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"கேமரா சாதனங்கள் திறக்கப்படும்போதோ மூடப்படும்போதோ அது குறித்த கால்பேக்குகளைப் பெற ஒரு ஆப்ஸையோ சேவையையோ அனுமதிக்கவும்."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"எந்தக் கேமரா சாதனமும் (எந்த ஆப்ஸாலும்) திறக்கப்படும்போதோ மூடப்படும்போதோ இந்த ஆப்ஸால் கால்பேக்குகளைப் பெற முடியும்."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"அதிர்வைக் கட்டுப்படுத்துதல்"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"அதிர்வைக் கட்டுப்படுத்தப் ஆப்ஸை அனுமதிக்கிறது."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"அதிர்வு நிலையை அணுக ஆப்ஸை அனுமதிக்கும்."</string> @@ -806,7 +810,7 @@ <string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"பயனர் நடவடிக்கை இல்லாமல் ஏற்கெனவே நிறுவப்பட்ட ஆப்ஸைப் புதுப்பிக்க ஹோல்டரை அனுமதிக்கும்"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"கடவுச்சொல் விதிகளை அமைக்கவும்"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"திரைப் பூட்டின் கடவுச்சொற்கள் மற்றும் பின்களில் அனுமதிக்கப்படும் நீளத்தையும் எழுத்துக்குறிகளையும் கட்டுப்படுத்தும்."</string> - <string name="policylab_watchLogin" msgid="7599669460083719504">"திரையை அன்லாக் செய்வதற்கான முயற்சிகளைக் கண்காணி"</string> + <string name="policylab_watchLogin" msgid="7599669460083719504">"திரையை அன்லாக் செய்வதற்கான முயற்சிகளைக் கண்காணித்தல்"</string> <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"திரையைத் திறக்கும்போது உள்ளிட்ட தவறான கடவுச்சொற்களின் எண்ணிக்கையைக் கண்காணிக்கும், மேலும் கடவுச்சொற்கள் பலமுறை தவறாக உள்ளிட்டிருந்தால், டேப்லெட்டைப் பூட்டும் அல்லது டேப்லெட்டின் எல்லா தரவையும் அழிக்கும்."</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"திரையைத் திறக்கும்போது எத்தனை முறை தவறான கடவுச்சொற்களை உள்ளிட்டீர்கள் என்பதைக் கண்காணிக்கும், பலமுறை தவறாக உள்ளிட்டிருந்தால் Android TVயைப் பூட்டும் அல்லது Android TVயின் அனைத்துத் தரவையும் அழிக்கும்."</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"திரையை அன்லாக் செய்யும்போது உள்ளிடப்படும் தவறான கடவுச்சொற்களின் எண்ணிக்கையைக் கண்காணிக்கும். மேலும் கடவுச்சொற்கள் பலமுறை தவறாக உள்ளிடப்பட்டிருந்தால் இன்ஃபோடெயின்மென்ட் சிஸ்டமைப் பூட்டும் அல்லது அதன் அனைத்துத் தரவையும் அழிக்கும்."</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 4c7835f66b41..8573c20c8021 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"ఈ విశేష లేదా సిస్టమ్ యాప్ ఎప్పుడైనా సిస్టమ్ కెమెరాను ఉపయోగించి ఫోటోలు తీయగలదు, వీడియోలను రికార్డ్ చేయగలదు. యాప్కు android.permission.CAMERA అనుమతి ఇవ్వడం కూడా అవసరం"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"కెమెరా పరికరాలు తెరుచుకుంటున్నప్పుడు లేదా మూసుకుంటున్నప్పుడు కాల్బ్యాక్లను స్వీకరించడానికి యాప్ను లేదా సర్వీస్ను అనుమతించండి."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"ఏదైనా కెమెరా పరికరం తెరుచుకుంటున్నప్పుడు (ఏదైనా యాప్ ద్వారా) లేదా మూసుకుంటున్నప్పుడు ఈ యాప్ కాల్బ్యాక్లను అందుకోగలదు."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"హెడ్లెస్ సిస్టమ్ యూజర్గా కెమెరాను యాక్సెస్ చేయడానికి అప్లికేషన్ లేదా సర్వీస్ను అనుమతించండి."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"ఈ యాప్ హెడ్లెస్ సిస్టమ్ యూజర్గా కెమెరాను యాక్సెస్ చేయగలదు."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"వైబ్రేషన్ను నియంత్రించడం"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"వైబ్రేటర్ను నియంత్రించడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"వైబ్రేటర్ స్థితిని యాక్సెస్ చేసేందుకు యాప్ను అనుమతిస్తుంది."</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 804562a90f62..eb4e2f71b085 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"แอปของระบบหรือที่ได้รับสิทธิ์นี้จะถ่ายภาพและบันทึกวิดีโอโดยใช้กล้องของระบบได้ทุกเมื่อ แอปต้องมีสิทธิ์ android.permission.CAMERA ด้วย"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"อนุญาตให้แอปพลิเคชันหรือบริการได้รับโค้ดเรียกกลับเมื่อมีการเปิดหรือปิดอุปกรณ์กล้อง"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"แอปนี้จะได้รับโค้ดเรียกกลับเมื่อมีการปิดหรือเปิดอุปกรณ์กล้อง (โดยแอปพลิเคชันที่เปิด)"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"อนุญาตให้แอปพลิเคชันหรือบริการเข้าถึงกล้องในฐานะผู้ใช้ระบบแบบไม่มีส่วนหัว"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"แอปนี้เข้าถึงกล้องในฐานะผู้ใช้ระบบแบบไม่มีส่วนหัว"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"ควบคุมการสั่นเตือน"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"อนุญาตให้แอปพลิเคชันควบคุมการสั่นเตือน"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"อนุญาตให้แอปเข้าถึงสถานะการสั่น"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 51e7ec641e1b..7249c51ea9f5 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ang may pribilehiyong app o system app na ito ay makakakuha ng mga larawan at makakapag-record ng mga video gamit ang isang camera ng system anumang oras. Kinakailangang may android.permission.CAMERA na pahintulot din ang app"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Payagan ang isang application o serbisyo na makatanggap ng mga callback tungkol sa pagbubukas o pagsasara ng mga camera device."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Puwedeng makatanggap ang app na ito ng mga callback kapag binubuksan (kung anong application) o isinasara ang anumang camera device."</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Payagan ang isang application o serbisyo na i-access ang camera bilang Headless System User."</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Maa-access ng app na ito ang camera bilang Headless System User."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"kontrolin ang pag-vibrate"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Pinapayagan ang app na kontrolin ang vibrator."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Pinapayagan ang app na ma-access ang naka-vibrate na status."</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index ee45ede2df89..90035c6d5a76 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ayrıcalık tanınmış bu veya sistem uygulaması herhangi bir zamanda sistem kamerası kullanarak fotoğraf çekebilir ve video kaydedebilir. Uygulamanın da bu ayrıcalığa sahip olması için android.permission.CAMERA izni gerektirir"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Bir uygulama veya hizmetin açılıp kapatılan kamera cihazları hakkında geri çağırmalar almasına izin verin."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Bu uygulama, herhangi bir kamera cihazı açıldığında (kamerayı açan uygulama tarafından) veya kapatıldığında geri çağırmalar alabilir."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"titreşimi denetleme"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Uygulamaya, titreşimi denetleme izni verir."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Uygulamanın titreşim durumuna erişimesine izni verir."</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 63b7560140e7..bb578d8b831f 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -503,6 +503,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Цей пріоритетний системний додаток може будь-коли робити фото й записувати відео, використовуючи камеру системи. Додатку потрібен дозвіл android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Дозволити додатку або сервісу отримувати зворотні виклики щодо відкриття чи закриття камер."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Цей додаток може отримувати зворотні виклики, коли одна з камер вмикається (певним додатком) чи вимикається."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"контролювати вібросигнал"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Дозволяє програмі контролювати вібросигнал."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Надає додатку доступ до стану вібрації."</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index bd6ea76a820f..631a573cfcf4 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"یہ مراعات یافتہ یا سسٹم ایپ کسی بھی وقت ایک سسٹم کیمرا استعمال کرتے ہوئے تصاویر اور ویڈیوز ریکارڈ کر سکتی ہے۔ ایپ کے پاس android.permission.CAMERA کے ليے بھی اجازت ہونا ضروری ہے۔"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ایپلیکیشن یا سروس کو کیمرا کے آلات کے کُھلنے یا بند ہونے سے متعلق کال بیکس موصول کرنے کی اجازت دیں۔"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"یہ ایپ کال بیکس موصول کر سکتی ہے جب کوئی بھی کیمرا کا آلہ (کسی ایپلیکیشن سے) کھولا جا رہا ہو یا بند کیا جا رہا ہو۔"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"ایپلیکیشن یا سروس کو ہیڈ لیس سسٹم صارف کے طور پر کیمرا تک رسائی حاصل کرنے کی اجازت دیں۔"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"یہ ایپ ہیڈ لیس سسٹم صارف کے طور پر کیمرے تک رسائی حاصل کر سکتی ہے۔"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"ارتعاش کو کنٹرول کریں"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"ایپ کو وائبریٹر کنٹرول کرنے کی اجازت دیتا ہے۔"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"ایپ کو وائبریٹر اسٹیٹ تک رسائی حاصل کرنے کی اجازت دیتا ہے۔"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 696edd384309..e480a3deb339 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Bu imtiyozli yoki tizim ilovasi istalgan vaqtda tizim kamerasi orqali surat va videolar olishi mumkin. Ilovada android.permission.CAMERA ruxsati ham yoqilgan boʻlishi talab qilinadi"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Ilova yoki xizmatga kamera qurilmalari ochilayotgani yoki yopilayotgani haqida qayta chaqiruvlar qabul qilishi uchun ruxsat berish."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Bu ilova har qanday kamera qurilmasi ochilayotganda (istalgan ilova tarafidan) yoki yopilayotganda qayta chaqiruvlar qabul qilishi mumkin."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"tebranishni boshqarish"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Ilova tebranishli signallarni boshqarishi mumkin."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Ilovaga tebranish holatini aniqlash ruxsatini beradi."</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index c02adbc5d2c7..b9ae37ce7e4a 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ứng dụng hệ thống có đặc quyền này có thể dùng máy ảnh hệ thống để chụp ảnh và quay video bất cứ lúc nào. Ngoài ra, ứng dụng này cũng cần có quyền android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Cho phép một ứng dụng hoặc dịch vụ nhận lệnh gọi lại khi các thiết bị máy ảnh đang được mở/đóng."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Ứng dụng này có thể nhận các lệnh gọi lại khi có bất kỳ thiết bị camera nào đang được mở (bằng ứng dụng) hoặc đóng."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"kiểm soát rung"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Cho phép ứng dụng kiểm soát bộ rung."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Cho phép ứng dụng truy cập vào trạng thái bộ rung."</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index f900b59c8eba..16c10138dfb6 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"这个具有特权的系统应用随时可以使用系统相机拍照及录制视频。另外,应用还需要获取 android.permission.CAMERA 权限"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"允许应用或服务接收与打开或关闭摄像头设备有关的回调。"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"此应用可在任何摄像头设备(被某些应用)打开或关闭时收到相应回调。"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"允许应用或服务以无头系统用户的身份使用摄像头"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"此应用能够以无头系统用户的身份使用摄像头。"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"控制振动"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"允许应用控制振动器。"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"允许该应用访问振动器状态。"</string> @@ -807,10 +809,10 @@ <string name="policylab_limitPassword" msgid="4851829918814422199">"设置密码规则"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"控制锁屏密码和 PIN 码所允许的长度和字符。"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"监控屏幕解锁尝试次数"</string> - <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"监视在解锁屏幕时输错密码的次数,如果输错次数过多,则锁定平板电脑或清除其所有数据。"</string> + <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定平板电脑或清除其所有数据。"</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"监控用户在解锁屏幕时输错密码的次数;如果用户输错密码的次数超出上限,系统就会锁定 Android TV 设备或清空 Android TV 设备上的所有数据。"</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定信息娱乐系统或清除信息娱乐系统上的所有数据。"</string> - <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"监视在解锁屏幕时输错密码的次数,如果输错次数过多,则锁定手机或清除其所有数据。"</string> + <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定手机或清除其所有数据。"</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定平板电脑或清空此用户的所有数据。"</string> <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"监控用户在解锁屏幕时输错密码的次数;如果用户输错密码的次数超出上限,系统就会锁定 Android TV 设备或清空该用户的所有数据。"</string> <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定信息娱乐系统或清除此个人资料的所有数据。"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 7140539391e7..680b7168a3f5 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"這個獲特別權限的系統應用程式可以在任何時候使用系統相機來拍照和攝錄。此外,應用程式亦需要 android.permission.CAMERA 權限"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"允許應用程式或服務接收相機裝置開啟或關閉的相關回電。"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"當任何相機裝置在開啟 (由應用程式) 或關閉時,此應用程式就能接收回電。"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"允許應用程式或服務以無使用者介面系統使用者權限存取相機。"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"這個應用程式可運用無使用者介面系統使用者權限存取相機。"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"控制震動"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"允許應用程式控制震動。"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"允許應用程式存取震動狀態。"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 61fe93977e92..81bdc37309f5 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -501,6 +501,8 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"這個具有特殊權限的系統應用程式隨時可以使用系統攝影機拍照及錄影。此外,你也必須將 android.permission.CAMERA 權限授予這個應用程式"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"允許應用程式或服務接收相機裝置開啟或關閉的相關回呼。"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"當任何相機裝置在開啟 (由應用程式) 或關閉時,這個應用程式就能接收回呼。"</string> + <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"允許應用程式或服務以無使用者介面系統使用者權限存取相機。"</string> + <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"這個應用程式可運用無使用者介面系統使用者權限存取相機。"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"控制震動"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"允許應用程式控制震動。"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"允許應用程式存取震動功能狀態。"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 77a41fce44e3..2059430fd47a 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -501,6 +501,10 @@ <string name="permdesc_systemCamera" msgid="5938360914419175986">"Lolu hlelo lokusebenza oluhle noma lwesistimu lingathatha izithombe futhi lirekhode amavidiyo lisebenzisa ikhamera yesistimu noma kunini. Idinga imvume ye-android.permission.CAMERA ukuthi iphathwe nawuhlelo lokusebenza"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Vumela uhlelo lokusebenza noma isevisi ukwamukela ukuphinda ufonelwe mayelana namadivayisi wekhamera avuliwe noma avaliwe."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Lolu hlelo lokusebenza lungakwazi ukuthola ukuphinda ufonelwe uma noma iyiphi idivayisi yekhamera ivulwa (ngephakheji yohlelo lokusebenza) noma ivalwa."</string> + <!-- no translation found for permlab_cameraHeadlessSystemUser (680194666834500050) --> + <skip /> + <!-- no translation found for permdesc_cameraHeadlessSystemUser (6963163319710996412) --> + <skip /> <string name="permlab_vibrate" msgid="8596800035791962017">"lawula ukudlidliza"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Ivumela uhlelo lokusebenza ukulawula isidlidlizi."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Ivumela uhlelo lokusebenza ukuthi lufinyelele kusimo sesidlidlizeli."</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3d0af3dbde3d..c00a776fc3da 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5227,6 +5227,28 @@ non-zero. --> <integer name="config_defaultPeakRefreshRate">0</integer> + <!-- External display peak refresh rate for the given device. Change this value if you want to + prevent the framework from using higher refresh rates, even if display modes with higher + refresh rates are available from hardware composer. Only has an effect if this value and + config_externalDisplayPeakWidth and config_externalDisplayPeakHeight are non-zero. --> + <integer name="config_externalDisplayPeakRefreshRate">0</integer> + + <!-- External display peak width for the given device. Change this value if you want + to prevent the framework from using higher resolution, even if display modes with higher + resolutions are available from hardware composer. Only has an effect if this value and + config_externalDisplayPeakRefreshRate and config_externalDisplayPeakHeight are non-zero.--> + <integer name="config_externalDisplayPeakWidth">0</integer> + + <!-- External display peak height for the given device. Change this value if you want + to prevent the framework from using higher resolution, even if display modes with higher + resolutions are available from hardware composer. Only has an effect if this value and + config_externalDisplayPeakRefreshRate and config_externalDisplayPeakWidth are non-zero. --> + <integer name="config_externalDisplayPeakHeight">0</integer> + + <!-- Enable synchronization of the displays refresh rates by applying the default low refresh + rate. --> + <bool name="config_refreshRateSynchronizationEnabled">false</bool> + <!-- The display uses different gamma curves for different refresh rates. It's hard for panel vendors to tune the curves to have exact same brightness for different refresh rate. So flicker could be observed at switch time. The issue is worse at the gamma lower end. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 83fb0986a19f..b0eee1cecc89 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4231,6 +4231,10 @@ <!-- For high refresh rate displays --> <java-symbol type="integer" name="config_defaultRefreshRate" /> <java-symbol type="integer" name="config_defaultPeakRefreshRate" /> + <java-symbol type="integer" name="config_externalDisplayPeakRefreshRate" /> + <java-symbol type="integer" name="config_externalDisplayPeakWidth" /> + <java-symbol type="integer" name="config_externalDisplayPeakHeight" /> + <java-symbol type="bool" name="config_refreshRateSynchronizationEnabled" /> <java-symbol type="integer" name="config_defaultRefreshRateInZone" /> <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" /> <java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" /> diff --git a/core/tests/coretests/src/android/colormodel/CamTest.java b/core/tests/coretests/src/android/colormodel/CamTest.java index 5bcc5930e9a6..05fc0e04515c 100644 --- a/core/tests/coretests/src/android/colormodel/CamTest.java +++ b/core/tests/coretests/src/android/colormodel/CamTest.java @@ -18,7 +18,7 @@ package com.android.internal.graphics.cam; import static org.junit.Assert.assertEquals; -import android.platform.test.annotations.LargeTest; +import androidx.test.filters.LargeTest; import org.junit.Assert; import org.junit.Test; diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java index 87e4a42ae0ad..989c992de855 100644 --- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java +++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java @@ -55,7 +55,6 @@ import java.util.Map; * Due to how the classes are structured, we have to test it in a somewhat roundabout way. We're * mocking out the contentProvider and are handcrafting very specific Bundles to answer the queries. */ -@Ignore("b/297724333") @Presubmit @RunWith(AndroidJUnit4.class) @SmallTest @@ -229,6 +228,8 @@ public class NameValueCacheTest { @After public void cleanUp() throws IOException { + Settings.Config.clearProviderForTest(); + Settings.Secure.clearProviderForTest(); mConfigsStorage.clear(); mSettingsStorage.clear(); mSettingsCacheGenerationStore.close(); diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java index e082c25fa499..c7eddabefc11 100644 --- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java +++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java @@ -17,7 +17,6 @@ package com.android.internal.content; import static com.google.common.truth.Truth.assertThat; - import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -28,6 +27,7 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; @@ -36,6 +36,7 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -45,6 +46,7 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class PackageMonitorTest { private static final String FAKE_PACKAGE_NAME = "com.android.internal.content.fakeapp"; + private static final String FAKE_EXTRA_REASON = "android.intent.extra.fakereason"; private static final int FAKE_PACKAGE_UID = 123; private static final int FAKE_USER_ID = 0; private static final int WAIT_CALLBACK_CALLED_IN_MS = 300; @@ -245,6 +247,7 @@ public class PackageMonitorTest { intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null)); intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID); intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID); + intent.putExtra(Intent.EXTRA_REASON, FAKE_EXTRA_REASON); String [] packageList = new String[]{FAKE_PACKAGE_NAME}; intent.putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, packageList); spyPackageMonitor.doHandlePackageEvent(intent); @@ -253,6 +256,20 @@ public class PackageMonitorTest { verify(spyPackageMonitor, times(1)) .onPackageChanged(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID), eq(packageList)); verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME)); + + ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(spyPackageMonitor, times(1)).onPackageChangedWithExtras(eq(FAKE_PACKAGE_NAME), + argumentCaptor.capture()); + + Bundle capturedExtras = argumentCaptor.getValue(); + Bundle expectedExtras = intent.getExtras(); + assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE)); + assertThat(capturedExtras.getInt(Intent.EXTRA_UID)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID)); + assertThat(capturedExtras.getInt(Intent.EXTRA_REASON)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REASON)); + verify(spyPackageMonitor, times(1)).onSomePackagesChanged(); verify(spyPackageMonitor, times(1)).onFinishPackageChanges(); } @@ -272,6 +289,21 @@ public class PackageMonitorTest { verify(spyPackageMonitor, times(1)).onBeginPackageChanges(); verify(spyPackageMonitor, times(1)) .onPackageUpdateStarted(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID)); + + ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(spyPackageMonitor, times(1)).onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME), + argumentCaptor.capture()); + Bundle capturedExtras = argumentCaptor.getValue(); + Bundle expectedExtras = intent.getExtras(); + assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE)); + assertThat(capturedExtras.getInt(Intent.EXTRA_UID)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID)); + assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING)); + assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS)); + verify(spyPackageMonitor, times(1)) .onPackageDisappeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING)); verify(spyPackageMonitor, times(1)).onFinishPackageChanges(); @@ -295,6 +327,21 @@ public class PackageMonitorTest { .onPackageRemoved(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID)); verify(spyPackageMonitor, times(1)) .onPackageRemovedAllUsers(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID)); + + ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(spyPackageMonitor, times(1)).onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME), + argumentCaptor.capture()); + Bundle capturedExtras = argumentCaptor.getValue(); + Bundle expectedExtras = intent.getExtras(); + assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE)); + assertThat(capturedExtras.getInt(Intent.EXTRA_UID)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID)); + assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING)); + assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS)); + verify(spyPackageMonitor, times(1)).onPackageDisappeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_PERMANENT_CHANGE)); verify(spyPackageMonitor, times(1)).onSomePackagesChanged(); @@ -316,6 +363,19 @@ public class PackageMonitorTest { verify(spyPackageMonitor, times(1)) .onPackageUpdateFinished(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID)); verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME)); + + ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(spyPackageMonitor, times(1)).onPackageAppearedWithExtras(eq(FAKE_PACKAGE_NAME), + argumentCaptor.capture()); + Bundle capturedExtras = argumentCaptor.getValue(); + Bundle expectedExtras = intent.getExtras(); + assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE)); + assertThat(capturedExtras.getInt(Intent.EXTRA_UID)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID)); + assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING)); + verify(spyPackageMonitor, times(1)) .onPackageAppeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING)); verify(spyPackageMonitor, times(1)).onSomePackagesChanged(); @@ -336,6 +396,19 @@ public class PackageMonitorTest { verify(spyPackageMonitor, times(1)).onBeginPackageChanges(); verify(spyPackageMonitor, times(1)) .onPackageAdded(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID)); + + ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(spyPackageMonitor, times(1)).onPackageAppearedWithExtras(eq(FAKE_PACKAGE_NAME), + argumentCaptor.capture()); + Bundle capturedExtras = argumentCaptor.getValue(); + Bundle expectedExtras = intent.getExtras(); + assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE)); + assertThat(capturedExtras.getInt(Intent.EXTRA_UID)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID)); + assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING)); + verify(spyPackageMonitor, times(1)).onPackageAppeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_PERMANENT_CHANGE)); verify(spyPackageMonitor, times(1)).onSomePackagesChanged(); diff --git a/core/tests/vibrator/TEST_MAPPING b/core/tests/vibrator/TEST_MAPPING index f3333d82d298..2f3afa6f6399 100644 --- a/core/tests/vibrator/TEST_MAPPING +++ b/core/tests/vibrator/TEST_MAPPING @@ -3,7 +3,7 @@ { "name": "FrameworksVibratorCoreTests", "options": [ - {"exclude-annotation": "android.platform.test.annotations.LargeTest"}, + {"exclude-annotation": "androidx.test.filters.LargeTest"}, {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"}, {"exclude-annotation": "org.junit.Ignore"} diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index ad0ead78f492..9c6528785584 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1123,12 +1123,6 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowSurfaceController.java" }, - "-1076978367": { - "message": "thawRotation: mRotation=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, "-1075136930": { "message": "startLockTaskMode: Can't lock due to auth", "level": "WARN", @@ -1231,6 +1225,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-962760979": { + "message": "thawRotation: mRotation=%d, caller=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "-961053385": { "message": "attachWindowContextToDisplayArea: calling from non-existing process pid=%d uid=%d", "level": "WARN", @@ -2779,6 +2779,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "364992694": { + "message": "freezeDisplayRotation: current rotation=%d, new rotation=%d, caller=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "371173718": { "message": "finishSync cancel=%b for %s", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java index 3e6457919031..78d257f86613 100644 --- a/graphics/java/android/graphics/RuntimeShader.java +++ b/graphics/java/android/graphics/RuntimeShader.java @@ -49,11 +49,11 @@ import libcore.util.NativeAllocationRegistry; * possible antialiasing logic for border pixels).</li> * <li>Logic for the {@link Shader}, {@link ColorFilter}, and {@link BlendMode} on the * {@link Paint}.</li> - * <li>Color space conversion code, as part of Android’s color management.</li> + * <li>Color space conversion code, as part of Android's color management.</li> * </ul> * * <p>A {@link RuntimeShader}, like other {@link Shader} types, effectively contributes a function - * to the GPU’s fragment shader.</p> + * to the GPU's fragment shader.</p> * * <h3>AGSL Shader Execution</h3> * <p>Just like a GLSL shader, an AGSL shader begins execution in a main function. Unlike GLSL, the @@ -78,10 +78,10 @@ import libcore.util.NativeAllocationRegistry; * {@link ColorSpace} for an AGSL shader is defined to be the color space of the destination, which * in most cases is determined by {@link Window#setColorMode(int)}.</p> * - * <p>When authoring an AGSL shader, you won’t know what the working color space is. For many + * <p>When authoring an AGSL shader, you won't know what the working color space is. For many * effects, this is fine because by default color inputs are automatically converted into the * working color space. For certain effects, it may be important to do some math in a fixed, known - * color space. A common example is lighting – to get physically accurate lighting, math should be + * color space. A common example is lighting - to get physically accurate lighting, math should be * done in a linear color space. To help with this, AGSL provides two intrinsic functions that * convert colors between the working color space and the * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB} color space: @@ -93,7 +93,7 @@ import libcore.util.NativeAllocationRegistry; * <h3>AGSL and Premultiplied Alpha</h3> * <p>When dealing with transparent colors, there are two (common) possible representations: * straight (unassociated) alpha and premultiplied (associated) alpha. In ASGL the color returned - * by the main function is expected to be premultiplied. AGSL’s use of premultiplied alpha + * by the main function is expected to be premultiplied. AGSL's use of premultiplied alpha * implies: * </p> * @@ -101,7 +101,7 @@ import libcore.util.NativeAllocationRegistry; * <li>If your AGSL shader will return transparent colors, be sure to multiply the RGB by A. The * resulting color should be [R*A, G*A, B*A, A], not [R, G, B, A].</li> * <li>For more complex shaders, you must understand which of your colors are premultiplied vs. - * straight. Many operations don’t make sense if you mix both kinds of color together.</li> + * straight. Many operations don't make sense if you mix both kinds of color together.</li> * </ul> * * <h3>Uniforms</h3> @@ -224,7 +224,7 @@ import libcore.util.NativeAllocationRegistry; * shader uniform is undefined if it is declared in the AGSL shader but not initialized.</p> * * <p>Although most {@link BitmapShader}s contain colors that should be color managed, some contain - * data that isn’t actually colors. This includes bitmaps storing normals, material properties + * data that isn't actually colors. This includes bitmaps storing normals, material properties * (e.g. roughness), heightmaps, or any other purely mathematical data that happens to be stored in * a bitmap. When using these kinds of shaders in AGSL, you probably want to initialize them with * {@link #setInputBuffer(String, BitmapShader)}. Shaders initialized this way work much like @@ -237,7 +237,7 @@ import libcore.util.NativeAllocationRegistry; * * <p>In addition, when sampling from a {@link BitmapShader} be aware that the shader does not use * normalized coordinates (like a texture in GLSL). It uses (0, 0) in the upper-left corner, and - * (width, height) in the bottom-right corner. Normally, this is exactly what you want. If you’re + * (width, height) in the bottom-right corner. Normally, this is exactly what you want. If you're * evaluating the shader with coordinates based on the ones passed to your AGSL program, the scale * is correct. However, if you want to adjust those coordinates (to do some kind of re-mapping of * the bitmap), remember that the coordinates are local to the canvas.</p> diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java index 7cca7f19da7d..5e4110590325 100644 --- a/graphics/java/android/graphics/fonts/FontFamily.java +++ b/graphics/java/android/graphics/fonts/FontFamily.java @@ -16,6 +16,9 @@ package android.graphics.fonts; +import static com.android.text.flags.Flags.FLAG_DEPRECATE_FONTS_XML; + +import android.annotation.FlaggedApi; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -138,6 +141,7 @@ public final class FontFamily { * @return A variable font family. null if a variable font cannot be built from the given * fonts. */ + @FlaggedApi(FLAG_DEPRECATE_FONTS_XML) public @Nullable FontFamily buildVariableFamily() { int variableFamilyType = analyzeAndResolveVariableType(mFonts); if (variableFamilyType == VARIABLE_FONT_FAMILY_TYPE_UNKNOWN) { diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java index 13540e0df789..e81525fb7d60 100644 --- a/graphics/java/android/graphics/text/LineBreakConfig.java +++ b/graphics/java/android/graphics/text/LineBreakConfig.java @@ -110,6 +110,7 @@ public final class LineBreakConfig { * This value is resolved to {@link #LINE_BREAK_STYLE_NONE} if this value is used for text * layout/rendering. */ + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final int LINE_BREAK_STYLE_UNSPECIFIED = -1; /** @@ -165,6 +166,7 @@ public final class LineBreakConfig { * This value is resolved to {@link #LINE_BREAK_WORD_STYLE_NONE} if this value is used for * text layout/rendering. */ + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final int LINE_BREAK_WORD_STYLE_UNSPECIFIED = -1; /** @@ -236,6 +238,7 @@ public final class LineBreakConfig { * @param config an override line break config * @return This {@code Builder}. */ + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public @NonNull Builder merge(@NonNull LineBreakConfig config) { if (config.mLineBreakStyle != LINE_BREAK_STYLE_UNSPECIFIED) { mLineBreakStyle = config.mLineBreakStyle; @@ -483,6 +486,7 @@ public final class LineBreakConfig { * @param config an overriding config. * @return newly created instance that is current style merged with passed config. */ + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public @NonNull LineBreakConfig merge(@NonNull LineBreakConfig config) { return new LineBreakConfig( config.mLineBreakStyle == LINE_BREAK_STYLE_UNSPECIFIED diff --git a/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java index 34ab8338fb6f..dc2e794a1df4 100644 --- a/graphics/java/android/graphics/text/LineBreaker.java +++ b/graphics/java/android/graphics/text/LineBreaker.java @@ -16,6 +16,9 @@ package android.graphics.text; +import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH; + +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.IntRange; @@ -248,6 +251,7 @@ public class LineBreaker { * @see Layout#getUseBoundsForWidth() * @see StaticLayout.Builder#setUseBoundsForWidth(boolean) */ + @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public @NonNull Builder setUseBoundsForWidth(boolean useBoundsForWidth) { mUseBoundsForWidth = useBoundsForWidth; return this; diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java index 49e9d0cf8372..569f9b6fe945 100644 --- a/graphics/java/android/graphics/text/PositionedGlyphs.java +++ b/graphics/java/android/graphics/text/PositionedGlyphs.java @@ -16,6 +16,9 @@ package android.graphics.text; +import static com.android.text.flags.Flags.FLAG_DEPRECATE_FONTS_XML; + +import android.annotation.FlaggedApi; import android.annotation.IntRange; import android.annotation.NonNull; import android.graphics.Paint; @@ -170,6 +173,7 @@ public final class PositionedGlyphs { * @param index the glyph index * @return true if the fake bold option is on, otherwise off. */ + @FlaggedApi(FLAG_DEPRECATE_FONTS_XML) public boolean getFakeBold(@IntRange(from = 0) int index) { Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index"); return nGetFakeBold(mLayoutPtr, index); @@ -181,6 +185,7 @@ public final class PositionedGlyphs { * @param index the glyph index * @return true if the fake italic option is on, otherwise off. */ + @FlaggedApi(FLAG_DEPRECATE_FONTS_XML) public boolean getFakeItalic(@IntRange(from = 0) int index) { Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index"); return nGetFakeItalic(mLayoutPtr, index); @@ -190,6 +195,7 @@ public final class PositionedGlyphs { * A special value returned by {@link #getWeightOverride(int)} and * {@link #getItalicOverride(int)} that indicates no font variation setting is overridden. */ + @FlaggedApi(FLAG_DEPRECATE_FONTS_XML) public static final float NO_OVERRIDE = Float.MIN_VALUE; /** @@ -199,6 +205,7 @@ public final class PositionedGlyphs { * @param index the glyph index * @return overridden weight value or {@link #NO_OVERRIDE}. */ + @FlaggedApi(FLAG_DEPRECATE_FONTS_XML) public float getWeightOverride(@IntRange(from = 0) int index) { Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index"); float value = nGetWeightOverride(mLayoutPtr, index); @@ -216,6 +223,7 @@ public final class PositionedGlyphs { * @param index the glyph index * @return overridden weight value or {@link #NO_OVERRIDE}. */ + @FlaggedApi(FLAG_DEPRECATE_FONTS_XML) public float getItalicOverride(@IntRange(from = 0) int index) { Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index"); float value = nGetItalicOverride(mLayoutPtr, index); diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml index 6622973007b4..202ea957971e 100644 --- a/libs/WindowManager/Shell/res/values-af/strings.xml +++ b/libs/WindowManager/Shell/res/values-af/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Beweeg na regs bo"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Beweeg na links onder"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Beweeg na regs onder"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"vou <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> uit"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"vou <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> in"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-instellings"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Maak borrel toe"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Moenie dat gesprek \'n borrel word nie"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Het dit"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen onlangse borrels nie"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Onlangse borrels en borrels wat toegemaak is, sal hier verskyn"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Klets met borrels"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nuwe gesprekke verskyn as ikone in ’n hoek onderaan jou skerm. Tik om hulle uit te vou, of sleep om hulle toe te maak."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Beheer borrels enige tyd"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tik hier om te bestuur watter apps en gesprekke in borrels kan verskyn"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string> diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml index a3f77411b0f6..4071e790d55a 100644 --- a/libs/WindowManager/Shell/res/values-am/strings.xml +++ b/libs/WindowManager/Shell/res/values-am/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ወደ ላይኛው ቀኝ አንቀሳቅስ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"የግርጌውን ግራ አንቀሳቅስ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ታችኛውን ቀኝ ያንቀሳቅሱ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ን ዘርጋ"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ን ሰብስብ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"የ<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ቅንብሮች"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"አረፋን አሰናብት"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ውይይቶችን በአረፋ አታሳይ"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ገባኝ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ምንም የቅርብ ጊዜ አረፋዎች የሉም"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"የቅርብ ጊዜ አረፋዎች እና የተሰናበቱ አረፋዎች እዚህ ብቅ ይላሉ"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"አረፋዎችን በመጠቀም ይወያዩ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"አዲስ ውይይቶች በማያ ገፅዎ የታችኛው ጥግ ውስጥ እንደ አዶዎች ይታያሉ። ለመዘርጋት መታ ያድርጓቸው ወይም ለማሰናበት ይጎትቷቸው።"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"በማንኛውም ጊዜ ዓረፋዎችን ይቆጣጠሩ"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"የትኛዎቹ መተግበሪያዎች እና ውይይቶች ዓረፋ መፍጠር እንደሚችሉ ለማስተዳደር እዚህ ጋር መታ ያድርጉ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string> diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml index ee4302e461df..d3890a779f87 100644 --- a/libs/WindowManager/Shell/res/values-ar/strings.xml +++ b/libs/WindowManager/Shell/res/values-ar/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"الانتقال إلى أعلى اليسار"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"نقل إلى أسفل يمين الشاشة"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"نقل إلى أسفل اليسار"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"توسيع <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"تصغير <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"إعدادات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"إغلاق فقاعة المحادثة"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"عدم عرض المحادثة كفقاعة محادثة"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"حسنًا"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ليس هناك فقاعات محادثات"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ستظهر هنا أحدث فقاعات المحادثات وفقاعات المحادثات التي تم إغلاقها."</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"الدردشة باستخدام فقاعات المحادثات"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"تظهر المحادثات الجديدة في شكل رموز بأسفل أحد جانبَي الشاشة. انقر على الرمز لتوسيع المحادثة أو اسحبه لإخفائها."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"التحكّم في إظهار الفقاعات في أي وقت"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"انقر هنا للتحكّم في إظهار فقاعات التطبيقات والمحادثات التي تريدها."</string> <string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string> diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml index a568d5841d32..05b8f7dca729 100644 --- a/libs/WindowManager/Shell/res/values-as/strings.xml +++ b/libs/WindowManager/Shell/res/values-as/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"শীৰ্ষৰ সোঁফালে নিয়ক"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"বুটামটো বাওঁফালে নিয়ক"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"তলৰ সোঁফালে নিয়ক"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> বিস্তাৰ কৰক"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> সংকোচন কৰক"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ছেটিং"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল অগ্ৰাহ্য কৰক"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"বাৰ্তালাপ বাবল নকৰিব"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"বুজি পালোঁ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"কোনো শেহতীয়া bubbles নাই"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"শেহতীয়া bubbles আৰু অগ্ৰাহ্য কৰা bubbles ইয়াত প্ৰদর্শিত হ\'ব"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bubbles ব্যৱহাৰ কৰি চাট কৰক"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"নতুন বাৰ্তালাপসমূহ আপোনাৰ স্ক্ৰীনৰ একেবাৰে তলৰ এটা কোণত চিহ্ন হিচাপে দেখা পোৱা যায়। সেইসমূহ বিস্তাৰ কৰিবলৈ টিপক বা অগ্ৰাহ্য কৰিবলৈ টানি আনি এৰক।"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"যিকোনো সময়তে বাবল নিয়ন্ত্ৰণ কৰক"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"কোনবোৰ এপ্ আৰু বাৰ্তালাপ বাবল হ’ব পাৰে সেয়া পৰিচালনা কৰিবলৈ ইয়াত টিপক"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string> diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml index 1a681e1c63a6..108593e4b948 100644 --- a/libs/WindowManager/Shell/res/values-az/strings.xml +++ b/libs/WindowManager/Shell/res/values-az/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuxarıya sağa köçürün"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Aşağıya sola köçürün"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Aşağıya sağa köçürün"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"genişləndirin: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"yığcamlaşdırın: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Yumrucuğu ləğv edin"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Söhbəti yumrucuqda göstərmə"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Anladım"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Yumrucuqlar yoxdur"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Son yumrucuqlar və buraxılmış yumrucuqlar burada görünəcək"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Yumrucuqlar vasitəsilə söhbət edin"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Yeni söhbətlər ekranın aşağı küncündə ikonalar kimi görünür. Toxunaraq genişləndirin, yaxud sürüşdürərək imtina edin."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Yumrucuqları idarə edin"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Bura toxunaraq yumrucuq göstərəcək tətbiq və söhbətləri idarə edin"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string> diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml index cba293b20c3d..76fd5b1c2d67 100644 --- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml +++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premesti gore desno"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premesti dole levo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premesti dole desno"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proširite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skupite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Podešavanja za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne koristi oblačiće za konverzaciju"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Važi"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovde se prikazuju nedavni i odbačeni oblačići"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Ćaskajte u oblačićima"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nove konverzacije se pojavljuju kao ikone u donjem uglu ekrana. Dodirnite da biste ih proširili ili prevucite da biste ih odbacili."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrolišite oblačiće u svakom trenutku"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dodirnite ovde i odredite koje aplikacije i konverzacije mogu da imaju oblačić"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string> diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml index 80e5a677e86d..473d15acc632 100644 --- a/libs/WindowManager/Shell/res/values-be/strings.xml +++ b/libs/WindowManager/Shell/res/values-be/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перамясціце правей і вышэй"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перамясціць лявей і ніжэй"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перамясціць правей і ніжэй"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: разгарнуць"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: згарнуць"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Налады \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Адхіліць апавяшчэнне"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не паказваць размову ў выглядзе ўсплывальных апавяшчэнняў"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Зразумела"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Няма нядаўніх усплывальных апавяшчэнняў"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Нядаўнія і адхіленыя ўсплывальныя апавяшчэнні будуць паказаны тут"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Чат з выкарыстаннем усплывальных апавяшчэнняў"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Новыя размовы паказваюцца ў выглядзе значкоў у ніжнім вугле экрана. Націсніце на іх, каб разгарнуць. Перацягніце іх, калі хочаце закрыць."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Кіруйце наладамі ўсплывальных апавяшчэнняў у любы час"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Каб кіраваць усплывальнымі апавяшчэннямі для праграм і размоў, націсніце тут"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string> diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml index ca5923919d36..7aa98e5bc218 100644 --- a/libs/WindowManager/Shell/res/values-bg/strings.xml +++ b/libs/WindowManager/Shell/res/values-bg/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Преместване горе вдясно"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Преместване долу вляво"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Преместване долу вдясно"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"разгъване на <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"свиване на <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Настройки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Отхвърляне на балончетата"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Без балончета за разговора"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Разбрах"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Няма скорошни балончета"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Скорошните и отхвърлените балончета ще се показват тук"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Чат с балончета"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Новите разговори се показват като икони в долния ъгъл на екрана ви. Докоснете, за да ги разгънете, или ги плъзнете за отхвърляне."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Управление на балончетата по всяко време"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Докоснете тук, за да управл. кои прил. и разговори могат да показват балончета"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string> diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml index c1eb469f73a4..caad87e8f05f 100644 --- a/libs/WindowManager/Shell/res/values-bn/strings.xml +++ b/libs/WindowManager/Shell/res/values-bn/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"উপরে ডানদিকে সরান"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"নিচে বাঁদিকে সরান"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"নিচে ডান দিকে সরান"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> বড় করুন"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> আড়াল করুন"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> সেটিংস"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল খারিজ করুন"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"কথোপকথন বাবল হিসেবে দেখাবে না"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"বুঝেছি"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"কোনও সাম্প্রতিক বাবল নেই"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"সাম্প্রতিক ও বাতিল করা বাবল এখানে দেখা যাবে"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"বাবল ব্যবহার করে চ্যাট করুন"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"নতুন কথোপকথন, আপনার স্ক্রিনের নিচে কোণার দিকে আইকন হিসেবে দেখায়। সেটি বড় করতে ট্যাপ করুন বা বাতিল করতে টেনে আনুন।"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"যেকোনও সময় বাবল নিয়ন্ত্রণ করুন"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"কোন অ্যাপ ও কথোপকথনের জন্য বাবলের সুবিধা চান তা ম্যানেজ করতে এখানে ট্যাপ করুন"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string> diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml index c97fc3dffb6d..66e67b385cb8 100644 --- a/libs/WindowManager/Shell/res/values-bs/strings.xml +++ b/libs/WindowManager/Shell/res/values-bs/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pomjerite gore desno"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pomjeri dolje lijevo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pomjerite dolje desno"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proširivanje oblačića <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sužavanje oblačića <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke aplikacije <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nemoj prikazivati razgovor u oblačićima"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Razumijem"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nedavni i odbačeni oblačići će se pojaviti ovdje"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatajte koristeći oblačiće"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novi razgovori se pojavljuju kao ikone u donjem uglu ekrana. Dodirnite da ih proširite ili prevucite da ih odbacite."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Upravljajte oblačićima u svakom trenutku"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dodirnite ovdje da upravljate time koje aplikacije i razgovori mogu imati oblačić"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string> diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml index a9195e4dbc03..62399ae07e7d 100644 --- a/libs/WindowManager/Shell/res/values-ca/strings.xml +++ b/libs/WindowManager/Shell/res/values-ca/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mou a dalt a la dreta"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mou a baix a l\'esquerra"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mou a baix a la dreta"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"desplega <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"replega <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuració de l\'aplicació <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora la bombolla"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostris la conversa com a bombolla"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entesos"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hi ha bombolles recents"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bombolles recents i les ignorades es mostraran aquí"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Xateja amb bombolles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Les converses noves es mostren com a icones en un extrem inferior de la pantalla. Toca per ampliar-les o arrossega per ignorar-les."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controla les bombolles en qualsevol moment"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toca aquí per gestionar quines aplicacions i converses poden fer servir bombolles"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string> diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index 89bd22203c8c..8e0aba0fdec6 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Přesunout vpravo nahoru"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Přesunout vlevo dolů"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Přesunout vpravo dolů"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozbalit <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sbalit <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavení <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zavřít bublinu"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovat konverzaci v bublinách"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Žádné nedávné bubliny"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Zde se budou zobrazovat nedávné bubliny a zavřené bubliny"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatujte pomocí bublin"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nové konverzace se zobrazí jako ikony v dolním rohu obrazovky. Klepnutím je rozbalíte, přetažením zavřete."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Nastavení bublin můžete kdykoli upravit"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Klepnutím sem lze spravovat, které aplikace a konverzace mohou vytvářet bubliny"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string> diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml index fd880bcfbd0c..d3989bcbd6f0 100644 --- a/libs/WindowManager/Shell/res/values-da/strings.xml +++ b/libs/WindowManager/Shell/res/values-da/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flyt op til højre"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flyt ned til venstre"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flyt ned til højre"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"udvid <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skjul <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Indstillinger for <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Afvis boble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Vis ikke samtaler i bobler"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen seneste bobler"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nye bobler og afviste bobler vises her"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat ved hjælp af bobler"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nye samtaler vises som ikoner nederst på din skærm. Tryk for at udvide dem, eller træk for at lukke dem."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Administrer bobler når som helst"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tryk her for at administrere, hvilke apps og samtaler der kan vises i bobler"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string> diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml index b28394d27e9a..5d0ee2951e90 100644 --- a/libs/WindowManager/Shell/res/values-de/strings.xml +++ b/libs/WindowManager/Shell/res/values-de/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Nach rechts oben verschieben"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Nach unten links verschieben"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Nach unten rechts verschieben"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> maximieren"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> minimieren"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Einstellungen für <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubble schließen"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Unterhaltung nicht als Bubble anzeigen"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bubbles zum Chatten verwenden"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Neue Unterhaltungen erscheinen als Symbole unten auf dem Display. Du kannst sie durch Antippen maximieren und durch Ziehen schließen."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Bubble-Einstellungen festlegen"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tippe hier, um zu verwalten, welche Apps und Unterhaltungen als Bubble angezeigt werden können"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml index 684c3bbddd82..2b73528f7aef 100644 --- a/libs/WindowManager/Shell/res/values-el/strings.xml +++ b/libs/WindowManager/Shell/res/values-el/strings.xml @@ -66,20 +66,20 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Μετακίνηση επάνω δεξιά"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Μετακίνηση κάτω αριστερά"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Μετακίνηση κάτω δεξιά"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ανάπτυξη <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"σύμπτυξη <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Ρυθμίσεις <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Παράβλ. για συννεφ."</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Να μην γίνει προβολή της συζήτησης σε συννεφάκια."</string> <string name="bubbles_user_education_title" msgid="2112319053732691899">"Συζητήστε χρησιμοποιώντας συννεφάκια."</string> <string name="bubbles_user_education_description" msgid="4215862563054175407">"Οι νέες συζητήσεις εμφανίζονται ως κινούμενα εικονίδια ή συννεφάκια. Πατήστε για να ανοίξετε το συννεφάκι. Σύρετε για να το μετακινήσετε."</string> <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Ελέγξτε τα συννεφάκια ανά πάσα στιγμή."</string> - <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Πατήστε Διαχείριση για να απενεργοποιήσετε τα συννεφάκια από αυτήν την εφαρμογή."</string> + <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Πατήστε Διαχείριση για να απενεργοποιήσετε τα συννεφάκια από αυτή την εφαρμογή."</string> <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Το κατάλαβα"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Δεν υπάρχουν πρόσφατα συννεφάκια"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Τα πρόσφατα συννεφάκια και τα συννεφάκια που παραβλέψατε θα εμφανίζονται εδώ."</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Συζητήστε χρησιμοποιώντας συννεφάκια"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Οι νέες συζητήσεις εμφανίζονται ως εικονίδια σε μια από τις κάτω γωνίες της οθόνης. Πατήστε για να τις αναπτύξετε ή σύρετε για να τις παραβλέψετε."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Ελέγξτε τα συννεφάκια ανά πάσα στιγμή."</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Πατήστε εδώ για τη διαχείριση εφαρμογών και συζητήσεων που προβάλλουν συννεφάκια"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml index 1890c3d28591..2f0e898d5db7 100644 --- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat using bubbles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Control bubbles at any time"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tap here to manage which apps and conversations can bubble"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml index 72189df6d65d..a338905fa299 100644 --- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml index 1890c3d28591..2f0e898d5db7 100644 --- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat using bubbles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Control bubbles at any time"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tap here to manage which apps and conversations can bubble"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml index 1890c3d28591..2f0e898d5db7 100644 --- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat using bubbles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Control bubbles at any time"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tap here to manage which apps and conversations can bubble"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml index 294bdb5edf3b..20344389ddcc 100644 --- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string> diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml index 54f2de06e5fb..ebf67f6e164e 100644 --- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml +++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ubicar arriba a la derecha"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ubicar abajo a la izquierda"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ubicar abajo a la derecha"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expandir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Descartar burbuja"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbuja"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hay burbujas recientes"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Las burbujas recientes y las que se descartaron aparecerán aquí"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat con burbujas"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Las conversaciones nuevas aparecen como íconos en la esquina inferior de la pantalla. Presiona para expandirlas, o bien arrastra para descartarlas."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controla las burbujas"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Presiona para administrar las apps y conversaciones que pueden mostrar burbujas"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string> diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml index 19a8a1402022..05171b2fc6dd 100644 --- a/libs/WindowManager/Shell/res/values-es/strings.xml +++ b/libs/WindowManager/Shell/res/values-es/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover arriba a la derecha"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover abajo a la izquierda."</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover abajo a la derecha"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"desplegar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Ajustes de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Cerrar burbuja"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar conversación en burbuja"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hay burbujas recientes"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Las burbujas recientes y las cerradas aparecerán aquí"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatea con burbujas"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Las nuevas conversaciones aparecen como iconos en una esquina inferior de la pantalla. Tócalas para ampliarlas o arrástralas para cerrarlas."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controla las burbujas cuando quieras"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toca aquí para gestionar qué aplicaciones y conversaciones pueden usar burbujas"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string> diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml index c0558e4fc95e..3af3c16ede24 100644 --- a/libs/WindowManager/Shell/res/values-et/strings.xml +++ b/libs/WindowManager/Shell/res/values-et/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Teisalda üles paremale"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Teisalda alla vasakule"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Teisalda alla paremale"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"laienda <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ahenda <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Rakenduse <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> seaded"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Sule mull"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ära kuva vestlust mullina"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Selge"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hiljutisi mulle pole"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Siin kuvatakse hiljutised ja suletud mullid."</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Vestelge mullide abil"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Uued vestlused kuvatakse ekraanikuva alanurgas ikoonidena. Puudutage nende laiendamiseks või lohistage neist loobumiseks."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Juhtige mulle igal ajal"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Puudutage siin, et hallata, milliseid rakendusi ja vestlusi saab mullina kuvada"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string> diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml index 7610f0dc4312..fae83883b7c4 100644 --- a/libs/WindowManager/Shell/res/values-eu/strings.xml +++ b/libs/WindowManager/Shell/res/values-eu/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Eraman goialdera, eskuinetara"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Eraman behealdera, ezkerretara"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Eraman behealdera, eskuinetara"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"zabaldu <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"tolestu <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aplikazioaren ezarpenak"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Baztertu burbuila"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ez erakutsi elkarrizketak burbuila gisa"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ados"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ez dago azkenaldiko burbuilarik"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Azken burbuilak eta baztertutakoak agertuko dira hemen"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Txateatu burbuilak erabilita"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Elkarrizketa berriak ikono gisa agertzen dira pantailaren beheko izkinan. Zabaltzeko, saka itzazu. Baztertzeko, aldiz, arrasta itzazu."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrolatu burbuilak edonoiz"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Sakatu hau burbuiletan zein aplikazio eta elkarrizketa ager daitezkeen kudeatzeko"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index f1fb51fa5ece..f7547602dcea 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"انتقال به بالا سمت چپ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"انتقال به پایین سمت راست"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"انتقال به پایین سمت چپ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ازهم باز کردن <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"جمع کردن <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"تنظیمات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"رد کردن حبابک"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"مکالمه در حباب نشان داده نشود"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجهام"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابکهای اخیر و حبابکهای ردشده اینجا ظاهر خواهند شد"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"گپ زدن بااستفاده از حبابک"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"مکالمههای جدید بهصورت نماد در گوشه پایین صفحهنمایش نشان داده میشود. برای ازهم بازکردن آنها ضربه بزنید یا برای بستن، آنها را بکشید."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"کنترل حبابکها در هرزمانی"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"برای مدیریت اینکه کدام برنامهها و مکالمهها حباب داشته باشند، ضربه بزنید"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string> diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml index 526925531ac0..9094c7335571 100644 --- a/libs/WindowManager/Shell/res/values-fi/strings.xml +++ b/libs/WindowManager/Shell/res/values-fi/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Siirrä oikeaan yläreunaan"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Siirrä vasempaan alareunaan"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Siirrä oikeaan alareunaan"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"laajenna <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"tiivistä <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: asetukset"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ohita kupla"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Älä näytä kuplia keskusteluista"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Okei"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ei viimeaikaisia kuplia"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Viimeaikaiset ja äskettäin ohitetut kuplat näkyvät täällä"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chattaile kuplien avulla"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Uudet keskustelut näkyvät kuvakkeina näytön alareunassa. Laajenna ne napauttamalla tai hylkää ne vetämällä."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Muuta kuplien asetuksia milloin tahansa"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Valitse napauttamalla tästä, mitkä sovellukset ja keskustelut voivat kuplia"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string> diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml index cd85f402cfab..b26c1b4e3018 100644 --- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer dans coin sup. droit"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer dans coin inf. gauche"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer dans coin inf. droit"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"développer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"réduire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorer la bulle"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher les conversations dans des bulles"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles récentes et les bulles ignorées s\'afficheront ici"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Clavarder en utilisant des bulles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Les nouvelles conversations apparaissent sous forme d\'icônes dans le coin inférieur de votre écran. Touchez-les icônes pour développer les conversations ou faites-les glisser pour les supprimer."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Gérez les bulles en tout temps"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Touchez ici pour gérer les applis et les conversations à inclure aux bulles"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string> diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml index 23ba785363f7..6e6420a22f50 100644 --- a/libs/WindowManager/Shell/res/values-fr/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer en haut à droite"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer en bas à gauche"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer en bas à droite"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Développer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Réduire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Fermer la bulle"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher la conversation dans une bulle"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles récentes et ignorées s\'afficheront ici"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatter via des bulles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Les nouvelles conversations apparaissent sous forme d\'icônes dans l\'un des coins inférieurs de votre écran. Appuyez dessus pour les développer ou faites-les glisser pour les supprimer."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Contrôlez les bulles à tout moment"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Appuyez ici pour gérer les applis et conversations s\'affichant dans des bulles"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string> diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml index 8693e42118fc..bf3a45b46484 100644 --- a/libs/WindowManager/Shell/res/values-gl/strings.xml +++ b/libs/WindowManager/Shell/res/values-gl/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover á parte superior dereita"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover á parte infer. esquerda"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover á parte inferior dereita"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"despregar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar burbulla"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mostrar a conversa como burbulla"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Non hai burbullas recentes"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"As burbullas recentes e ignoradas aparecerán aquí."</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatea usando burbullas"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"As conversas novas móstranse como iconas nunha das esquinas inferiores da pantalla. Tócaas para amplialas ou arrástraas para pechalas."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controlar as burbullas"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toca para xestionar as aplicacións e conversas que poden aparecer en burbullas"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string> diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml index a7cdf7352189..84c818230621 100644 --- a/libs/WindowManager/Shell/res/values-gu/strings.xml +++ b/libs/WindowManager/Shell/res/values-gu/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ઉપર જમણે ખસેડો"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"નીચે ડાબે ખસેડો"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"નીચે જમણે ખસેડો"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> મોટું કરો"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> નાનું કરો"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> સેટિંગ"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"બબલને છોડી દો"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"વાતચીતને બબલ કરશો નહીં"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"સમજાઈ ગયું"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"તાજેતરના કોઈ બબલ નથી"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"એકદમ નવા બબલ અને છોડી દીધેલા બબલ અહીં દેખાશે"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"બબલનો ઉપયોગ કરીને ચૅટ કરો"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"નવી વાતચીતો તમારી સ્ક્રીનના નીચેના ખૂણામાં આઇકન તરીકે દેખાય છે. તેમને મોટી કરવા માટે, તેમના પર ટૅપ કરો અથવા તેમને છોડી દેવા માટે, તેમને ખેંચો."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"બબલને કોઈપણ સમયે નિયંત્રિત કરે છે"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"કઈ ઍપ અને વાતચીતોને બબલ કરવા માગો છો તે મેનેજ કરવા માટે, અહીં ટૅપ કરો"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string> diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml index 13e0258ce87b..8068f50d9cf6 100644 --- a/libs/WindowManager/Shell/res/values-hi/strings.xml +++ b/libs/WindowManager/Shell/res/values-hi/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सबसे ऊपर दाईं ओर ले जाएं"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"बाईं ओर सबसे नीचे ले जाएं"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"सबसे नीचे दाईं ओर ले जाएं"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> को बड़ा करें"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> को छोटा करें"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> की सेटिंग"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारिज करें"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"बातचीत को बबल न करें"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ठीक है"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हाल ही के कोई बबल्स नहीं हैं"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ही के बबल्स और हटाए गए बबल्स यहां दिखेंगे"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"बबल्स का इस्तेमाल करके चैट करें"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"नई बातचीत, आपकी स्क्रीन पर सबसे नीचे आइकॉन के तौर पर दिखती हैं. किसी आइकॉन को बड़ा करने के लिए उस पर टैप करें या खारिज करने के लिए उसे खींचें और छोड़ें."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"जब चाहें, बबल्स की सुविधा को कंट्रोल करें"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"किसी ऐप्लिकेशन और बातचीत के लिए बबल की सुविधा को मैनेज करने के लिए यहां टैप करें"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string> diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml index 957e56c35e87..5f0b866ee4de 100644 --- a/libs/WindowManager/Shell/res/values-hr/strings.xml +++ b/libs/WindowManager/Shell/res/values-hr/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premjesti u gornji desni kut"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premjesti u donji lijevi kut"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premjestite u donji desni kut"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proširite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sažmite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Zaustavi razgovor u oblačićima"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Shvaćam"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovdje će se prikazivati nedavni i odbačeni oblačići"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Oblačići u chatu"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novi se razgovori prikazuju kao ikone u donjem kutu zaslona. Dodirnite da biste ih proširili ili ih povucite da biste ih odbacili."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Upravljanje oblačićima u svakom trenutku"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dodirnite ovdje da biste odredili koje aplikacije i razgovori mogu imati oblačić"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string> diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml index e9808ac6c2be..d89e2923b5fe 100644 --- a/libs/WindowManager/Shell/res/values-hu/strings.xml +++ b/libs/WindowManager/Shell/res/values-hu/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Áthelyezés fel és jobbra"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Áthelyezés le és balra"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Áthelyezés le és jobbra"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> kibontása"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> összecsukása"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> beállításai"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Buborék elvetése"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne jelenjen meg a beszélgetés buborékban"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Értem"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nincsenek buborékok a közelmúltból"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"A legutóbbi és az elvetett buborékok itt jelennek majd meg"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Buborékokat használó csevegés"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Az új beszélgetések ikonokként jelennek meg a képernyő alsó sarkában. Koppintással kibonthatja, húzással pedig elvetheti őket."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Buborékok vezérlése bármikor"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ide koppintva jeleníthetők meg az alkalmazások és a beszélgetések buborékként"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string> diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml index 8a9d89bd879f..9c517b29d291 100644 --- a/libs/WindowManager/Shell/res/values-hy/strings.xml +++ b/libs/WindowManager/Shell/res/values-hy/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Տեղափոխել վերև՝ աջ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Տեղափոխել ներքև՝ ձախ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Տեղափոխել ներքև՝ աջ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>. ծավալել"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>. ծալել"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – կարգավորումներ"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Փակել ամպիկը"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Զրույցը չցուցադրել ամպիկի տեսքով"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Եղավ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ամպիկներ չկան"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Այստեղ կցուցադրվեն վերջերս օգտագործված և փակված ամպիկները, որոնք կկարողանաք հեշտությամբ վերաբացել"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Զրույցի ամպիկներ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Նոր զրույցները հայտնվում են որպես պատկերակներ էկրանի ներքևի անկյունում։ Հպեք՝ դրանք ծավալելու, կամ քաշեք՝ մերժելու համար։"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Ամպիկների կարգավորումներ"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Հպեք այստեղ՝ ընտրելու, թե որ հավելվածների և զրույցների համար ամպիկներ ցուցադրել"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string> diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml index 6b84a1d9bdac..3dbd75087382 100644 --- a/libs/WindowManager/Shell/res/values-in/strings.xml +++ b/libs/WindowManager/Shell/res/values-in/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pindahkan ke kanan atas"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pindahkan ke kiri bawah"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pindahkan ke kanan bawah"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"luaskan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ciutkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Setelan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Tutup balon"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan gunakan percakapan balon"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Oke"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Tidak ada balon baru-baru ini"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Balon yang baru dipakai dan balon yang telah ditutup akan muncul di sini"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat dalam tampilan balon"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Percakapan baru muncul sebagai ikon di bagian pojok bawah layar. Ketuk untuk meluaskan percakapan atau tarik untuk menutup percakapan."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrol balon kapan saja"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ketuk di sini untuk mengelola balon aplikasi dan percakapan"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string> diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml index 913e1964c4df..2927d6a0d8ec 100644 --- a/libs/WindowManager/Shell/res/values-is/strings.xml +++ b/libs/WindowManager/Shell/res/values-is/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Færa efst til hægri"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Færa neðst til vinstri"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Færðu neðst til hægri"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"stækka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"minnka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Stillingar <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Loka blöðru"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ekki setja samtal í blöðru"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ég skil"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Engar nýlegar blöðrur"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nýlegar blöðrur og blöðrur sem þú hefur lokað birtast hér"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Spjall með blöðrum"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Ný samtöl birtast sem tákn neðst á horni skjásins. Ýttu til að stækka þau eða dragðu til að hunsa þau."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Hægt er að stjórna blöðrum hvenær sem er"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ýttu hér til að stjórna því hvaða forrit og samtöl mega nota blöðrur."</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string> diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml index 575210bffc7d..938a81466381 100644 --- a/libs/WindowManager/Shell/res/values-it/strings.xml +++ b/libs/WindowManager/Shell/res/values-it/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sposta in alto a destra"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sposta in basso a sinistra"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sposta in basso a destra"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"espandi <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"comprimi <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Impostazioni <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora bolla"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mettere la conversazione nella bolla"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nessuna bolla recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Le bolle recenti e ignorate appariranno qui"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatta utilizzando le bolle"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Le nuove conversazioni vengono visualizzate sotto forma di icone in un angolo inferiore dello schermo. Tocca per espanderle o trascina per chiuderle."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Gestisci le bolle in qualsiasi momento"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tocca qui per gestire le app e le conversazioni per cui mostrare le bolle"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string> diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml index fbc384f1be67..aaa0a3866031 100644 --- a/libs/WindowManager/Shell/res/values-iw/strings.xml +++ b/libs/WindowManager/Shell/res/values-iw/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"העברה לפינה הימנית העליונה"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"העברה לפינה השמאלית התחתונה"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"העברה לפינה הימנית התחתונה"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"הרחבה של <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"כיווץ של <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"הגדרות <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"סגירת בועה"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"אין להציג בועות לשיחה"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"הבנתי"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"אין בועות מהזמן האחרון"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"בועות אחרונות ובועות שנסגרו יופיעו כאן"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"צ\'אט בבועות"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"שיחות חדשות מופיעות כסמלים בפינה התחתונה של המסך. אפשר להקיש כדי להרחיב אותן או לגרור כדי לסגור אותן."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"שליטה בבועות בכל זמן"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"אפשר להקיש כאן כדי לקבוע אילו אפליקציות ושיחות יוכלו להופיע בבועות"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string> diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml index dce3a18f8cd0..1979fd52e5bb 100644 --- a/libs/WindowManager/Shell/res/values-ja/strings.xml +++ b/libs/WindowManager/Shell/res/values-ja/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"右上に移動"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"左下に移動"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"右下に移動"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>を開きます"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>を閉じます"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> の設定"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"バブルを閉じる"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"会話をバブルで表示しない"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近閉じたバブルはありません"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近表示されたバブルや閉じたバブルが、ここに表示されます"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"チャットでバブルを使う"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"新しい会話がアイコンとして画面下部に表示されます。タップすると開き、ドラッグして閉じることができます。"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"バブルはいつでも管理可能"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"バブルで表示するアプリや会話を管理するには、ここをタップします"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string> diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml index b396c8c89046..ce1ce6402637 100644 --- a/libs/WindowManager/Shell/res/values-ka/strings.xml +++ b/libs/WindowManager/Shell/res/values-ka/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"გადაანაცვლეთ ზევით და მარჯვნივ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ქვევით და მარცხნივ გადატანა"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"გადაანაცვ. ქვემოთ და მარჯვნივ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-ის გაფართოება"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-ის ჩაკეცვა"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-ის პარამეტრები"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ბუშტის დახურვა"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"აიკრძალოს საუბრის ბუშტები"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"გასაგებია"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ბოლო დროს გამოყენებული ბუშტები არ არის"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"აქ გამოჩნდება ბოლოდროინდელი ბუშტები და უარყოფილი ბუშტები"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ჩეთი ბუშტების გამოყენებით"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ახალი საუბრები ხატულას სახით გამოჩნდება თქვენი ეკრანის ქვედა კუთხეში. შეეხეთ გასაფართოებლად და ჩაავლეთ მათ დასახურად."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ამოხტომის გაკონტროლება ნებისმიერ დროს"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"აქ შეეხეთ იმის სამართავად, თუ რომელი აპები და საუბრები ამოხტეს"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string> diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml index 63ef3d2000dd..b4b05072f4a4 100644 --- a/libs/WindowManager/Shell/res/values-kk/strings.xml +++ b/libs/WindowManager/Shell/res/values-kk/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жоғары оң жаққа жылжыту"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төменгі сол жаққа жылжыту"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төменгі оң жаққа жылжыту"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: жаю"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: жию"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлері"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Қалқымалы хабарды жабу"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Әңгіменің қалқыма хабары көрсетілмесін"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түсінікті"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Жақындағы қалқыма хабарлар жоқ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Соңғы және жабылған қалқыма хабарлар осы жерде көрсетіледі."</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Қалқыма хабарлар арқылы чатта сөйлесу"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Жаңа әңгімелер экранның төменгі бөлігінде белгіше түрінде көрсетіледі. Оларды жаю үшін түртіңіз, ал жабу үшін сүйреңіз."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Қалқыма хабарларды кез келген уақытта басқарыңыз"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Қалқыма хабарда көрсетілетін қолданбалар мен әңгімелерді реттеу үшін осы жерді түртіңіз."</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string> diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml index 2ce8ba37b3bf..9b0a0dad5782 100644 --- a/libs/WindowManager/Shell/res/values-km/strings.xml +++ b/libs/WindowManager/Shell/res/values-km/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ផ្លាស់ទីទៅផ្នែកខាងលើខាងស្ដាំ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ផ្លាស់ទីទៅផ្នែកខាងក្រោមខាងឆ្វេង"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ផ្លាស់ទីទៅផ្នែកខាងក្រោមខាងស្ដាំ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ពង្រីក <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"បង្រួម <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ការកំណត់ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ច្រានចោលពពុះ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"កុំបង្ហាញការសន្ទនាជាពពុះ"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"យល់ហើយ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"មិនមានពពុះថ្មីៗទេ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ពពុះថ្មីៗ និងពពុះដែលបានបិទនឹងបង្ហាញនៅទីនេះ"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ជជែកដោយប្រើផ្ទាំងអណ្ដែត"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ការសន្ទនាថ្មីៗបង្ហាញជារូបតំណាងនៅជ្រុងខាងក្រោមនៃអេក្រង់របស់អ្នក។ សូមចុច ដើម្បីពង្រីកការសន្ទនាទាំងនោះ ឬអូស ដើម្បីច្រានចោល។"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"គ្រប់គ្រងផ្ទាំងអណ្ដែតនៅពេលណាក៏បាន"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ចុចត្រង់នេះ ដើម្បីគ្រប់គ្រងកម្មវិធី និងការសន្ទនាដែលអាចបង្ហាញជាផ្ទាំងអណ្ដែត"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string> diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml index 4b8aaa93b2a7..b19978aa09d7 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ಬಲ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ಸ್ಕ್ರೀನ್ನ ಎಡ ಕೆಳಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ಕೆಳಗಿನ ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ಅನ್ನು ವಿಸ್ತೃತಗೊಳಿಸಿ"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ಅನ್ನು ಕುಗ್ಗಿಸಿ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ಬಬಲ್ ವಜಾಗೊಳಿಸಿ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ಸಂಭಾಷಣೆಯನ್ನು ಬಬಲ್ ಮಾಡಬೇಡಿ"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ಅರ್ಥವಾಯಿತು"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಬಬಲ್ಸ್ ಇಲ್ಲ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ಇತ್ತೀಚಿನ ಬಬಲ್ಸ್ ಮತ್ತು ವಜಾಗೊಳಿಸಿದ ಬಬಲ್ಸ್ ಇಲ್ಲಿ ಗೋಚರಿಸುತ್ತವೆ"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ಬಬಲ್ಸ್ ಬಳಸಿ ಚಾಟ್ ಮಾಡಿ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ಹೊಸ ಸಂಭಾಷಣೆಗಳು ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಕೆಳಗಿನ ಮೂಲೆಯಲ್ಲಿ ಐಕಾನ್ಗಳಾಗಿ ಗೋಚರಿಸುತ್ತವೆ. ವಿಸ್ತರಿಸಲು ಅವುಗಳನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ ಅಥವಾ ವಜಾಗೊಳಿಸಲು ಡ್ರ್ಯಾಗ್ ಮಾಡಿ."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ಬಬಲ್ಸ್ ಅನ್ನು ನಿಯಂತ್ರಿಸಿ"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ಯಾವ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಸಂಭಾಷಣೆಗಳನ್ನು ಬಬಲ್ ಮಾಡಬಹುದು ಎಂಬುದನ್ನು ನಿರ್ವಹಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string> diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml index ffa77b0dc0e2..af08da9d6e65 100644 --- a/libs/WindowManager/Shell/res/values-ko/strings.xml +++ b/libs/WindowManager/Shell/res/values-ko/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"오른쪽 상단으로 이동"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"왼쪽 하단으로 이동"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"오른쪽 하단으로 이동"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> 펼치기"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> 접기"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> 설정"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"대화창 닫기"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"대화를 대화창으로 표시하지 않기"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"확인"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"최근 대화창 없음"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"최근 대화창과 내가 닫은 대화창이 여기에 표시됩니다."</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"대화창으로 채팅하기"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"새 대화는 화면 하단에 아이콘으로 표시됩니다. 아이콘을 탭하여 펼치거나 드래그하여 닫습니다."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"언제든지 대화창을 제어하세요"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"대화창을 만들 수 있는 앱과 대화를 관리하려면 여기를 탭하세요."</string> <string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string> diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml index b74875c5885a..be24cefbaf91 100644 --- a/libs/WindowManager/Shell/res/values-ky/strings.xml +++ b/libs/WindowManager/Shell/res/values-ky/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жогорку оң жакка жылдыруу"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төмөнкү сол жакка жылдыруу"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төмөнкү оң жакка жылдыруу"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> жайып көрсөтүү"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> жыйыштыруу"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлери"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Калкып чыкма билдирмени жабуу"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Жазышууда калкып чыкма билдирмелер көрүнбөсүн"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түшүндүм"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Азырынча эч нерсе жок"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Акыркы жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Калкып чыкма билдирмелер аркылуу маектешүү"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Жаңы сүйлөшүүлөр экраныңыздын төмөнкү бурчунда сүрөтчөлөр катары көрүнөт. Аларды жайып көрсөтүү үчүн таптап же четке кагуу үчүн сүйрөңүз."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Калкып чыкма билдирмелерди каалаган убакта көзөмөлдөңүз"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Калкып чыкма билдирме түрүндө көрүнө турган колдонмолор менен маектерди тандоо үчүн бул жерди таптаңыз"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string> diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml index 3e1ab6d28eeb..41219eec986c 100644 --- a/libs/WindowManager/Shell/res/values-lo/strings.xml +++ b/libs/WindowManager/Shell/res/values-lo/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ຍ້າຍຂວາເທິງ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ຍ້າຍຊ້າຍລຸ່ມ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ຍ້າຍຂວາລຸ່ມ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ຂະຫຍາຍ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ຫຍໍ້ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ລົງ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ການຕັ້ງຄ່າ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ປິດຟອງໄວ້"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ຢ່າໃຊ້ຟອງໃນການສົນທະນາ"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ເຂົ້າໃຈແລ້ວ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ບໍ່ມີຟອງຫຼ້າສຸດ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ຟອງຫຼ້າສຸດ ແລະ ຟອງທີ່ປິດໄປຈະປາກົດຢູ່ບ່ອນນີ້"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ສົນທະນາໂດຍໃຊ້ຟອງ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ການສົນທະນາໃໝ່ໆຈະປາກົດເປັນໄອຄອນຢູ່ມຸມລຸ່ມສຸດຂອງໜ້າຈໍຂອງທ່ານ. ແຕະເພື່ອຂະຫຍາຍ ຫຼື ລາກເພື່ອປິດໄວ້."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ຄວບຄຸມຟອງໄດ້ທຸກເວລາ"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ແຕະບ່ອນນີ້ເພື່ອຈັດການແອັບ ແລະ ການສົນທະນາທີ່ສາມາດສະແດງເປັນແບບຟອງໄດ້"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string> diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml index f4751aae323d..b98fce8f8a3f 100644 --- a/libs/WindowManager/Shell/res/values-lt/strings.xml +++ b/libs/WindowManager/Shell/res/values-lt/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Perkelti į viršų dešinėje"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Perkelti į apačią kairėje"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Perkelti į apačią dešinėje"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"išskleisti „<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>“"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sutraukti „<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>“"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ nustatymai"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Atsisakyti burbulo"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerodyti pokalbio burbule"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Supratau"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nėra naujausių burbulų"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Naujausi ir atsisakyti burbulai bus rodomi čia"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Pokalbis naudojant burbulus"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nauji pokalbiai rodomi kaip piktogramos apatiniame ekrano kampe. Palieskite piktogramą, jei norite išplėsti pokalbį, arba nuvilkite, kad atsisakytumėte."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Bet kada valdyti burbulus"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Palietę čia valdykite, kurie pokalbiai ir programos gali būti rodomi burbuluose"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string> diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml index 5fab5778d3d5..11b645ef80b7 100644 --- a/libs/WindowManager/Shell/res/values-lv/strings.xml +++ b/libs/WindowManager/Shell/res/values-lv/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pārvietot augšpusē pa labi"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pārvietot apakšpusē pa kreisi"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pārvietot apakšpusē pa labi"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Izvērst “<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Sakļaut “<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Lietotnes <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iestatījumi"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Nerādīt burbuli"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerādīt sarunu burbuļos"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Labi"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nav nesen aizvērtu burbuļu"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Šeit būs redzami nesen rādītie burbuļi un aizvērtie burbuļi"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Tērzēšana, izmantojot burbuļus"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Jaunas sarunas tiek parādītas kā ikonas jūsu ekrāna apakšējā stūrī. Varat pieskarties, lai tās izvērstu, vai vilkt, lai tās noraidītu."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Pārvaldīt burbuļus jebkurā laikā"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Pieskarieties šeit, lai pārvaldītu, kuras lietotnes un sarunas var rādīt burbulī"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string> diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml index 906fc094ea5a..ba2c97a4f29f 100644 --- a/libs/WindowManager/Shell/res/values-mk/strings.xml +++ b/libs/WindowManager/Shell/res/values-mk/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести долу лево"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести долу десно"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"прошири <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"собери <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Поставки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Отфрли балонче"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не прикажувај го разговорот во балончиња"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Сфатив"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нема неодамнешни балончиња"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Неодамнешните и отфрлените балончиња ќе се појавуваат тука"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Разговор со балончиња"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Новите разговори се појавуваат како икони во долниот агол од екранот. Допрете за да ги проширите или повлечете за да ги отфрлите."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Контролирајте ги балончињата во секое време"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Допрете тука за да одредите на кои апл. и разговори може да се појават балончиња"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string> diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml index 65e6d2c4c8e7..8a95d7ef157c 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"മുകളിൽ വലതുഭാഗത്തേക്ക് നീക്കുക"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ചുവടെ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ചുവടെ വലതുഭാഗത്തേക്ക് നീക്കുക"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> വികസിപ്പിക്കുക"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ചുരുക്കുക"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ക്രമീകരണം"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ബബിൾ ഡിസ്മിസ് ചെയ്യൂ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"സംഭാഷണം ബബിൾ ചെയ്യരുത്"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"മനസ്സിലായി"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"അടുത്തിടെയുള്ള ബബിളുകൾ ഒന്നുമില്ല"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"അടുത്തിടെയുള്ള ബബിളുകൾ, ഡിസ്മിസ് ചെയ്ത ബബിളുകൾ എന്നിവ ഇവിടെ ദൃശ്യമാവും"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ബബിളുകൾ ഉപയോഗിച്ച് ചാറ്റ് ചെയ്യുക"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"പുതിയ സംഭാഷണങ്ങൾ, നിങ്ങളുടെ സ്ക്രീനിന്റെ താഴെ മൂലയിൽ ഐക്കണുകളായി ദൃശ്യമാകും. വികസിപ്പിക്കാൻ അവയിൽ ടാപ്പ് ചെയ്യുക അല്ലെങ്കിൽ ഡിസ്മിസ് ചെയ്യാൻ വലിച്ചിടുക."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ബബിളുകൾ ഏതുസമയത്തും നിയന്ത്രിക്കുക"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ഏതൊക്കെ ആപ്പുകളും സംഭാഷണങ്ങളും ബബിൾ ചെയ്യാനാകുമെന്നത് മാനേജ് ചെയ്യാൻ ഇവിടെ ടാപ്പ് ചെയ്യുക"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ബബിൾ"</string> diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml index 44c594691e36..bead7fe0d1c7 100644 --- a/libs/WindowManager/Shell/res/values-mn/strings.xml +++ b/libs/WindowManager/Shell/res/values-mn/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Баруун дээш зөөх"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Зүүн доош зөөх"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Баруун доош зөөх"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-г дэлгэх"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-г хураах"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-н тохиргоо"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Бөмбөлгийг хаах"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Харилцан яриаг бүү бөмбөлөг болго"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ойлголоо"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Саяхны бөмбөлөг алга байна"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Саяхны бөмбөлгүүд болон үл хэрэгссэн бөмбөлгүүд энд харагдана"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Бөмбөлгүүд ашиглан чатлах"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Шинэ харилцан ярианууд таны дэлгэцийн доод буланд дүрс тэмдэг байдлаар харагдана. Тэдгээрийг дэлгэхийн тулд товших эсвэл хаахын тулд чирнэ үү."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Бөмбөлгүүдийг хүссэн үедээ хянах"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ямар апп болон харилцан ярианууд бөмбөлгөөр харагдахыг энд удирдана уу"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string> diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml index bd898c4395ee..ca0e15a0241b 100644 --- a/libs/WindowManager/Shell/res/values-mr/strings.xml +++ b/libs/WindowManager/Shell/res/values-mr/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"वर उजवीकडे हलवा"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"तळाशी डावीकडे हलवा"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"तळाशी उजवीकडे हलवा"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> विस्तार करा"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> कोलॅप्स करा"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> सेटिंग्ज"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल डिसमिस करा"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"संभाषणाला बबल करू नका"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"समजले"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"अलीकडील कोणतेही बबल नाहीत"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"अलीकडील बबल आणि डिसमिस केलेले बबल येथे दिसतील"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"बबल वापरून चॅट करा"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"नवीन संभाषणे ही तुमच्या स्क्रीनच्या तळाशी असलेल्या कोपऱ्यात आयकनच्या स्वरूपात दिसतात. त्यांचा विस्तार करण्यासाठी टॅप करा किंवा ती डिसमिस करण्यासाठी ड्रॅग करा."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"बबल कधीही नियंत्रित करा"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"कोणती ॲप्स आणि संभाषणे बबल होऊ शकतात हे व्यवस्थापित करण्यासाठी येथे टॅप करा"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string> diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml index 86a7025f5e39..fd9fb87b918c 100644 --- a/libs/WindowManager/Shell/res/values-ms/strings.xml +++ b/libs/WindowManager/Shell/res/values-ms/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Alihkan ke atas sebelah kanan"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Alihkan ke bawah sebelah kiri"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Alihkan ke bawah sebelah kanan"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"kembangkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"kuncupkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Tetapan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ketepikan gelembung"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan jadikan perbualan dalam bentuk gelembung"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Tiada gelembung terbaharu"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Gelembung baharu dan gelembung yang diketepikan akan dipaparkan di sini"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bersembang menggunakan gelembung"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Perbualan baharu dipaparkan sebagai ikon pada penjuru sebelah bawah skrin anda. Ketik untuk mengembangkan perbualan atau seret untuk mengetepikan perbualan."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kawal gelembung pada bila-bila masa"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ketik di sini untuk mengurus apl dan perbualan yang boleh menggunakan gelembung"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string> diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml index 4c494eb586a2..42bce8104a5c 100644 --- a/libs/WindowManager/Shell/res/values-my/strings.xml +++ b/libs/WindowManager/Shell/res/values-my/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ညာဘက်ထိပ်သို့ ရွှေ့ပါ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ဘယ်အောက်ခြေသို့ ရွှေ့ရန်"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ညာအောက်ခြေသို့ ရွှေ့ပါ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ကို ချဲ့ရန်"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ကို ချုံ့ရန်"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ဆက်တင်များ"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ပူဖောင်းကွက် ပယ်ရန်"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"စကားဝိုင်းကို ပူဖောင်းကွက် မပြုလုပ်ပါနှင့်"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"နားလည်ပြီ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"လတ်တလော ပူဖောင်းကွက်များ မရှိပါ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"လတ်တလော ပူဖောင်းကွက်များနှင့် ပိတ်လိုက်သော ပူဖောင်းကွက်များကို ဤနေရာတွင် မြင်ရပါမည်"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ပူဖောင်းကွက်သုံး၍ ချတ်လုပ်ခြင်း"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"စကားဝိုင်းအသစ်များကို သင့်ဖန်သားပြင် အောက်ခြေထောင့်တွင် သင်္ကေတများအဖြစ် မြင်ရပါမည်။ ၎င်းတို့ကို ဖြန့်ရန်တို့ပါ (သို့) ပယ်ရန်ဖိဆွဲပါ။"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ပူဖောင်းကွက်ကို အချိန်မရွေး ထိန်းချုပ်ရန်"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ပူဖောင်းကွက်သုံးနိုင်သည့် အက်ပ်နှင့် စကားဝိုင်းများ စီမံရန် ဤနေရာကို တို့ပါ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index e9f90c0cb0ea..cd656b846ccf 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytt til øverst til høyre"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytt til nederst til venstre"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytt til nederst til høyre"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"vis <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skjul <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-innstillinger"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Lukk boblen"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ikke vis samtaler i bobler"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Greit"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen nylige bobler"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nylige bobler og avviste bobler vises her"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat med bobler"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nye samtaler vises som ikoner i et hjørne nede på skjermen. Trykk for å åpne dem, eller dra for å lukke dem."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontroller bobler når som helst"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Trykk her for å administrere hvilke apper og samtaler som kan vises i bobler"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string> diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml index dcfff7c30a1a..3300bc68c084 100644 --- a/libs/WindowManager/Shell/res/values-ne/strings.xml +++ b/libs/WindowManager/Shell/res/values-ne/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सिरानमा दायाँतिर सार्नुहोस्"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"पुछारमा बायाँतिर सार्नुहोस्"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"पुछारमा दायाँतिर सार्नुहोस्"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> एक्स्पान्ड गर्नुहोस्"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> कोल्याप्स गर्नुहोस्"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> का सेटिङहरू"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारेज गर्नुहोस्"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"वार्तालाप बबलको रूपमा नदेखाइयोस्"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"बुझेँ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हालैका बबलहरू छैनन्"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हालैका बबल र खारेज गरिएका बबलहरू यहाँ देखिने छन्"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"बबल प्रयोग गरी कुराकानी गर्नुहोस्"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"नयाँ वार्तालापहरू तपाईंको डिभाइसको स्क्रिनको पुछारको कुनामा आइकनका रूपमा देखिन्छन्। ती आइकन ठुलो बनाउन ट्याप गर्नुहोस् वा ती आइकन हटाउन ड्र्याग गर्नुहोस्।"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"जुनसुकै बेला बबलसम्बन्धी सुविधा नियन्त्रण गर्नुहोस्"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"कुन एप र कुराकानी बबल प्रयोग गर्न सक्छन् भन्ने कुराको व्यवस्थापन गर्न यहाँ ट्याप गर्नुहोस्"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string> diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml index 2f560f04205c..0d174049dc15 100644 --- a/libs/WindowManager/Shell/res/values-nl/strings.xml +++ b/libs/WindowManager/Shell/res/values-nl/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Naar rechtsboven verplaatsen"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Naar linksonder verplaatsen"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Naar rechtsonder verplaatsen"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> uitvouwen"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> samenvouwen"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubbel sluiten"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels tonen"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen recente bubbels"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels zie je hier"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatten met bubbels"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nieuwe gesprekken verschijnen als iconen in een benedenhoek van je scherm. Tik om ze uit te vouwen of sleep om ze te sluiten."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Bubbels beheren wanneer je wilt"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tik hier om te beheren welke apps en gesprekken als bubbel kunnen worden getoond"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string> diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml index ad25de51a3e7..916d8638436c 100644 --- a/libs/WindowManager/Shell/res/values-or/strings.xml +++ b/libs/WindowManager/Shell/res/values-or/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ଉପର-ଡାହାଣକୁ ନିଅନ୍ତୁ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ତଳ ବାମକୁ ନିଅନ୍ତୁ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ତଳ ଡାହାଣକୁ ନିଅନ୍ତୁ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ବିସ୍ତାର କରନ୍ତୁ"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ସଙ୍କୁଚିତ କରନ୍ତୁ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ସେଟିଂସ୍"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ବବଲ୍ ଖାରଜ କରନ୍ତୁ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ବାର୍ତ୍ତାଳାପକୁ ବବଲ୍ କରନ୍ତୁ ନାହିଁ"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ବୁଝିଗଲି"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ବର୍ତ୍ତମାନ କୌଣସି ବବଲ୍ ନାହିଁ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ବର୍ତ୍ତମାନର ଏବଂ ଖାରଜ କରାଯାଇଥିବା ବବଲଗୁଡ଼ିକ ଏଠାରେ ଦେଖାଯିବ"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ବବଲଗୁଡ଼ିକୁ ବ୍ୟବହାର କରି ଚାଟ୍ କରନ୍ତୁ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ନୂଆ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଆପଣଙ୍କ ସ୍କ୍ରିନର ଏକ ନିମ୍ନ କୋଣରେ ଆଇକନଗୁଡ଼ିକ ଭାବେ ଦେଖାଯାଏ। ସେଗୁଡ଼ିକୁ ବିସ୍ତାର କରିବା ପାଇଁ ଟାପ କରନ୍ତୁ କିମ୍ବା ସେଗୁଡ଼ିକୁ ଖାରଜ କରିବା ପାଇଁ ଡ୍ରାଗ କରନ୍ତୁ।"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ଯେ କୌଣସି ସମୟରେ ବବଲଗୁଡ଼ିକ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"କେଉଁ ଆପ୍ସ ଓ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ବବଲ ହୋଇପାରିବ ତାହା ପରିଚାଳନା କରିବାକୁ ଏଠାରେ ଟାପ କରନ୍ତୁ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string> diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml index 4bd9d6b9d214..5c936e069f9e 100644 --- a/libs/WindowManager/Shell/res/values-pa/strings.xml +++ b/libs/WindowManager/Shell/res/values-pa/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ਉੱਪਰ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ਹੇਠਾਂ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ਹੇਠਾਂ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ਨੂੰ ਸਮੇਟੋ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ਸੈਟਿੰਗਾਂ"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕਰੋ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ਗੱਲਬਾਤ \'ਤੇ ਬਬਲ ਨਾ ਲਾਓ"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ਸਮਝ ਲਿਆ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ਕੋਈ ਹਾਲੀਆ ਬਬਲ ਨਹੀਂ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ਹਾਲੀਆ ਬਬਲ ਅਤੇ ਖਾਰਜ ਕੀਤੇ ਬਬਲ ਇੱਥੇ ਦਿਸਣਗੇ"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ਬਬਲ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਚੈਟ ਕਰੋ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ਨਵੀਂਆਂ ਗੱਲਾਂਬਾਤਾਂ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਕੋਨੇ ਵਿੱਚ ਪ੍ਰਤੀਕਾਂ ਦੇ ਰੂਪ ਵਿੱਚ ਦਿਖਦੀਆਂ ਹਨ। ਉਨ੍ਹਾਂ ਦਾ ਵਿਸਤਾਰ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਜਾਂ ਉਨ੍ਹਾਂ ਨੂੰ ਖਾਰਜ ਕਰਨ ਲਈ ਘਸੀਟੋ।"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ਬਬਲ ਦੀ ਸੁਵਿਧਾ ਨੂੰ ਕਿਸੇ ਵੀ ਵੇਲੇ ਕੰਟਰੋਲ ਕਰੋ"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ਇਹ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਇੱਥੇ ਟੈਪ ਕਰੋ ਕਿ ਕਿਹੜੀਆਂ ਐਪਾਂ ਅਤੇ ਗੱਲਾਂਬਾਤਾਂ ਬਬਲ ਹੋ ਸਕਦੀਆਂ ਹਨ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string> diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml index d98be758c97e..abdb5f7eb7ac 100644 --- a/libs/WindowManager/Shell/res/values-pl/strings.xml +++ b/libs/WindowManager/Shell/res/values-pl/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Przenieś w prawy górny róg"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Przenieś w lewy dolny róg"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Przenieś w prawy dolny róg"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozwiń dymek <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"zwiń dymek <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – ustawienia"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zamknij dymek"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nie wyświetlaj rozmowy jako dymka"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Brak ostatnich dymków"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tutaj będą pojawiać się ostatnie i odrzucone dymki"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Czatuj, korzystając z dymków"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nowe rozmowy pojawiają się jako ikony w dolnym rogu ekranu. Kliknij, aby je rozwinąć, lub przeciągnij, aby je zamknąć."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Zarządzaj dymkami, kiedy chcesz"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Kliknij tutaj, aby zarządzać wyświetlaniem aplikacji i rozmów jako dymków"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml index 81d325a7ec58..bb68b28075bb 100644 --- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"abrir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"fechar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balão"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não criar balões de conversa"</string> @@ -76,17 +78,15 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ok"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e dispensados aparecerão aqui"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Converse usando balões"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novas conversas aparecem como ícones no canto inferior da tela. Toque neles para abrir ou arraste para dispensar."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controle os balões a qualquer momento"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toque aqui para gerenciar quais apps e conversas podem aparecer em balões"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string> <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar o app e atualizar a visualização"</string> - <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude a proporção deste app nas Configurações"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude o tamanho da janela deste app nas Configurações"</string> <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mudar a proporção"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml index 7fa592afbbe3..2c03c543d1dc 100644 --- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover parte superior direita"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover p/ parte infer. esquerda"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover parte inferior direita"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expandir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"reduzir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Definições de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar balão"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não apresentar a conversa em balões"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e ignorados vão aparecer aqui."</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Converse no chat através de balões"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"As novas conversas aparecem como ícones no canto inferior do ecrã. Toque para as expandir ou arraste para as ignorar."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controle os balões em qualquer altura"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toque aqui para gerir que apps e conversas podem aparecer em balões"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string> diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml index 81d325a7ec58..bb68b28075bb 100644 --- a/libs/WindowManager/Shell/res/values-pt/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"abrir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"fechar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balão"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não criar balões de conversa"</string> @@ -76,17 +78,15 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ok"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e dispensados aparecerão aqui"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Converse usando balões"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novas conversas aparecem como ícones no canto inferior da tela. Toque neles para abrir ou arraste para dispensar."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controle os balões a qualquer momento"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toque aqui para gerenciar quais apps e conversas podem aparecer em balões"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string> <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar o app e atualizar a visualização"</string> - <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude a proporção deste app nas Configurações"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude o tamanho da janela deste app nas Configurações"</string> <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mudar a proporção"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string> diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml index 0341667be3e3..579046b3a45f 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mută în dreapta sus"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mută în stânga jos"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mută în dreapta jos"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"extinde <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"restrânge <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Setări <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Închide balonul"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nu afișa conversația în balon"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nu există baloane recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Baloanele recente și baloanele respinse vor apărea aici"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat cu baloane"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Conversațiile noi apar ca pictograme în colțul de jos al ecranului. Atinge pentru a le extinde sau trage pentru a le închide."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controlează baloanele oricând"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Atinge aici pentru a gestiona aplicațiile și conversațiile care pot apărea în balon"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string> diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml index da234c71a009..a8a914cbf9fc 100644 --- a/libs/WindowManager/Shell/res/values-ru/strings.xml +++ b/libs/WindowManager/Shell/res/values-ru/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перенести в правый верхний угол"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перенести в левый нижний угол"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перенести в правый нижний угол"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Развернуть <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Свернуть <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: настройки"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Скрыть всплывающий чат"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не показывать всплывающий чат для разговора"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ОК"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нет недавних всплывающих чатов"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Здесь будут появляться недавние и скрытые всплывающие чаты."</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Всплывающие чаты"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Новые чаты появляются в виде значков в нижней части экрана. Коснитесь их, чтобы развернуть. Перетащите их, если хотите закрыть."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Всплывающие чаты"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Укажите приложения и разговоры, для которых разрешены всплывающие чаты."</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string> diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml index 236da5d67fc9..968bc2c3b862 100644 --- a/libs/WindowManager/Shell/res/values-si/strings.xml +++ b/libs/WindowManager/Shell/res/values-si/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ඉහළ දකුණට ගෙන යන්න"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"පහළ වමට ගෙන යන්න"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"පහළ දකුණට ගෙන යන්න"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> දිග හරින්න"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> හකුළන්න"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> සැකසීම්"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"බුබුලු ඉවත ලන්න"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"සංවාදය බුබුලු නොදමන්න"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"තේරුණා"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"මෑත බුබුලු නැත"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"මෑත බුබුලු සහ ඉවත ලූ බුබුලු මෙහි දිස් වනු ඇත"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"බුබුලු භාවිතයෙන් කතාබහ කරන්න"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"නව සංවාද ඔබේ තිරයෙහි පහළ කෙළවරේ නිරූපක ලෙස දිස් වේ. ඒවා පුළුල් කිරීමට තට්ටු කරන්න හෝ ඒවා ඉවත දැමීමට අදින්න."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ඕනෑම වේලාවක බුබුලු පාලනය කරන්න"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"බුබුලු කළ හැකි යෙදුම් සහ සංවාද කළමනාකරණය කිරීමට මෙහි තට්ටු කරන්න"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string> diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml index eaabdabb0285..303d81b17568 100644 --- a/libs/WindowManager/Shell/res/values-sk/strings.xml +++ b/libs/WindowManager/Shell/res/values-sk/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Presunúť doprava nahor"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Presunúť doľava nadol"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Presunúť doprava nadol"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozbaliť <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"zbaliť <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavenia aplikácie <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zavrieť bublinu"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovať konverzáciu ako bublinu"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Dobre"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Žiadne nedávne bubliny"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tu sa budú zobrazovať nedávne a zavreté bubliny"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Čet pomocou bublín"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nové konverzácie sa zobrazujú ako ikony v dolnom rohu obrazovky. Klepnutím ich rozbalíte a presunutím zavriete."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Ovládajte bubliny kedykoľvek"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Klepnite tu a spravujte, ktoré aplikácie a konverzácie môžu ovládať bubliny"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string> diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml index 514a0b3548db..6178a1a9a489 100644 --- a/libs/WindowManager/Shell/res/values-sl/strings.xml +++ b/libs/WindowManager/Shell/res/values-sl/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premakni zgoraj desno"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premakni spodaj levo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premakni spodaj desno"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"razširitev oblačka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"strnitev oblačka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavitve za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Opusti oblaček"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Pogovora ne prikaži v oblačku"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"V redu"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ni nedavnih oblačkov"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tukaj bodo prikazani tako nedavni kot tudi opuščeni oblački"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Klepet z oblački"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novi pogovori so prikazani kot ikone v enem od spodnjih kotov zaslona. Z dotikom pogovore razširite, z vlečenjem jih opustite."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Upravljanje oblačkov"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dotaknite se tukaj za upravljanje aplikacij in pogovorov, ki so lahko prikazani v oblačkih"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string> diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml index 790119b8e9cc..e155c72bf297 100644 --- a/libs/WindowManager/Shell/res/values-sq/strings.xml +++ b/libs/WindowManager/Shell/res/values-sq/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Lëviz lart djathtas"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Zhvendos poshtë majtas"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Lëvize poshtë djathtas"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"zgjero <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"palos <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Cilësimet e <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Hiqe flluskën"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Mos e vendos bisedën në flluskë"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"E kuptova"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nuk ka flluska të fundit"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Flluskat e fundit dhe flluskat e hequra do të shfaqen këtu"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bisedo duke përdorur flluskat"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Bisedat e reja shfaqen si ikona në këndin e poshtëm të ekranit tënd. Trokit për t\'i zgjeruar ose zvarrit për t\'i hequr ato."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrollo flluskat në çdo moment"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Trokit këtu për të menaxhuar aplikacionet e bisedat që do të shfaqen në flluska"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string> diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml index 9fd9f3ed18a9..0e70a47f3fcb 100644 --- a/libs/WindowManager/Shell/res/values-sr/strings.xml +++ b/libs/WindowManager/Shell/res/values-sr/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести доле лево"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести доле десно"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"проширите облачић <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"скупите облачић <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Подешавања за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Одбаци облачић"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не користи облачиће за конверзацију"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Важи"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нема недавних облачића"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Овде се приказују недавни и одбачени облачићи"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Ћаскајте у облачићима"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Нове конверзације се појављују као иконе у доњем углу екрана. Додирните да бисте их проширили или превуците да бисте их одбацили."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Контролишите облачиће у сваком тренутку"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Додирните овде и одредите које апликације и конверзације могу да имају облачић"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string> diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml index f7f218e4ded7..4c22964e125f 100644 --- a/libs/WindowManager/Shell/res/values-sv/strings.xml +++ b/libs/WindowManager/Shell/res/values-sv/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytta högst upp till höger"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytta längst ned till vänster"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytta längst ned till höger"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"utöka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"komprimera <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Inställningar för <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Stäng bubbla"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Visa inte konversationen i bubblor"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Inga nya bubblor"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"De senaste bubblorna och ignorerade bubblor visas här"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatta med bubblor"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nya konversationer visas som ikoner nere i hörnet på skärmen. Tryck för att utöka eller dra för att stänga dem."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Styr bubblor när som helst"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tryck här för att hantera vilka appar och konversationer som får visas i bubblor"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string> diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml index 83173f3649c0..71aeb61538cd 100644 --- a/libs/WindowManager/Shell/res/values-sw/strings.xml +++ b/libs/WindowManager/Shell/res/values-sw/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sogeza juu kulia"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sogeza chini kushoto"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sogeza chini kulia"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"panua <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"kunja <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Mipangilio ya <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ondoa kiputo"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Usiweke viputo kwenye mazungumzo"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Nimeelewa"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hakuna viputo vya hivi majuzi"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Viputo vya hivi karibuni na vile vilivyoondolewa vitaonekana hapa"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Piga gumzo ukitumia viputo"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Mazungumzo mapya huonekana kama aikoni katika kona ya chini ya skrini yako. Gusa ili uyapanue au buruta ili uyaondoe."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Dhibiti viputo wakati wowote"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Gusa hapa ili udhibiti programu na mazungumzo yanayoweza kutumia viputo"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string> diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml index ea2ee9c22c08..aab05daca0ae 100644 --- a/libs/WindowManager/Shell/res/values-ta/strings.xml +++ b/libs/WindowManager/Shell/res/values-ta/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"மேலே வலப்புறமாக நகர்த்து"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"கீழே இடப்புறமாக நகர்த்து"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"கீழே வலதுபுறமாக நகர்த்து"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ஐ விரிவாக்கும்"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ஐச் சுருக்கும்"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> அமைப்புகள்"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"குமிழை அகற்று"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"உரையாடலைக் குமிழாக்காதே"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"சரி"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"சமீபத்திய குமிழ்கள் இல்லை"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"சமீபத்திய குமிழ்களும் நிராகரிக்கப்பட்ட குமிழ்களும் இங்கே தோன்றும்"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"குமிழ்களைப் பயன்படுத்தி உரையாடுங்கள்"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"புதிய உரையாடல்கள் உங்கள் திரையின் கீழ் மூலையில் ஐகான்களாகத் தோன்றும். அவற்றை விரிவாக்க தட்டவும் அல்லது நிராகரிக்க இழுக்கவும்."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"எப்போது வேண்டுமானாலும் குமிழ்களைக் கட்டுப்படுத்துங்கள்"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"எந்தெந்த ஆப்ஸும் உரையாடல்களும் குமிழியாகலாம் என்பதை நிர்வகிக்க இங்கே தட்டுங்கள்"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string> diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index e2772bf7311e..1e407bf255de 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ఎగువ కుడివైపునకు జరుపు"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"దిగువ ఎడమవైపునకు తరలించు"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"దిగవు కుడివైపునకు జరుపు"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> విస్తరించండి"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ను కుదించండి"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> సెట్టింగ్లు"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"బబుల్ను విస్మరించు"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"సంభాషణను బబుల్ చేయవద్దు"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"అర్థమైంది"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ఇటీవలి బబుల్స్ ఏవీ లేవు"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ఇటీవలి బబుల్స్ మరియు తీసివేసిన బబుల్స్ ఇక్కడ కనిపిస్తాయి"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"బబుల్స్ను ఉపయోగించి చాట్ చేయండి"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"కొత్త సంభాషణలు మీ స్క్రీన్ కింద మూలన చిహ్నాలుగా కనిపిస్తాయి. ట్యాప్ చేసి వాటిని విస్తరించండి లేదా లాగి విస్మరించండి."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"బబుల్స్ను ఎప్పుడైనా కంట్రోల్ చేయండి"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ఏ యాప్లు, సంభాషణలను బబుల్ చేయాలో మేనేజ్ చేయడానికి ఇక్కడ ట్యాప్ చేయండి"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string> diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml index 14bdc4bb040f..77a9f4b3ec76 100644 --- a/libs/WindowManager/Shell/res/values-th/strings.xml +++ b/libs/WindowManager/Shell/res/values-th/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ย้ายไปด้านขวาบน"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ย้ายไปด้านซ้ายล่าง"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ย้ายไปด้านขาวล่าง"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ขยาย <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ยุบ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"การตั้งค่า <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ปิดบับเบิล"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ไม่ต้องแสดงการสนทนาเป็นบับเบิล"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"รับทราบ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ไม่มีบับเบิลเมื่อเร็วๆ นี้"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"บับเบิลที่แสดงและที่ปิดไปเมื่อเร็วๆ นี้จะปรากฏที่นี่"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"แชทโดยใช้บับเบิล"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"การสนทนาครั้งใหม่ๆ จะปรากฏเป็นไอคอนที่มุมล่างของหน้าจอ โดยสามารถแตะเพื่อขยายหรือลากเพื่อปิด"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ควบคุมบับเบิลได้ทุกเมื่อ"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"แตะที่นี่เพื่อจัดการแอปและการสนทนาที่แสดงเป็นบับเบิลได้"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string> diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml index 208e8cbc2277..757da92ef72c 100644 --- a/libs/WindowManager/Shell/res/values-tl/strings.xml +++ b/libs/WindowManager/Shell/res/values-tl/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ilipat sa kanan sa itaas"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ilipat sa kaliwa sa ibaba"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ilipat sa kanan sa ibaba"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"I-expand ang <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"i-collapse ang <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Mga setting ng <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"I-dismiss ang bubble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Huwag ipakita sa bubble ang mga pag-uusap"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Walang kamakailang bubble"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Lalabas dito ang mga kamakailang bubble at na-dismiss na bubble"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Mag-chat gamit ang mga bubble"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Lalabas ang mga bagong pag-uusap bilang mga icon sa sulok sa ibaba ng iyong screen. I-tap para i-expand ang mga ito o i-drag para i-dismiss ang mga ito."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrolin ang mga bubble anumang oras"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Mag-tap dito para pamahalaan ang mga app at conversion na puwedeng mag-bubble"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml index b6c0d6864d1c..9c4bf8df9308 100644 --- a/libs/WindowManager/Shell/res/values-tr/strings.xml +++ b/libs/WindowManager/Shell/res/values-tr/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sağ üste taşı"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sol alta taşı"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sağ alta taşı"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"genişlet: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"daralt: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Baloncuğu kapat"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Görüşmeyi baloncuk olarak görüntüleme"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Anladım"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Son kapatılan baloncuk yok"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Son baloncuklar ve kapattığınız baloncuklar burada görünür"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Baloncukları kullanarak sohbet edin"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Yeni görüşmeler, ekranınızın alt köşesinde simge olarak görünür. Bunları dokunarak genişletebilir veya sürükleyerek kapatabilirsiniz."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Baloncukları istediğiniz zaman kontrol edin"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Buraya dokunarak baloncuk olarak gösterilecek uygulama ve görüşmeleri yönetin"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string> diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml index 6a119881518a..753fe29ebd41 100644 --- a/libs/WindowManager/Shell/res/values-uk/strings.xml +++ b/libs/WindowManager/Shell/res/values-uk/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перемістити праворуч угору"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перемістити ліворуч униз"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перемістити праворуч униз"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"розгорнути \"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>\""</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"згорнути \"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>\""</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Налаштування параметра \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Закрити підказку"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не показувати спливаючі чати для розмов"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Зрозуміло"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Немає нещодавніх спливаючих чатів"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Тут з\'являтимуться нещодавні й закриті спливаючі чати"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Спливаючий чат"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Нові розмови відображаються у вигляді значків у нижньому куті екрана. Торкніться, щоб розгорнути їх, або перетягніть, щоб закрити."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Контроль спливаючих чатів"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Натисніть тут, щоб вибрати, для яких додатків і розмов дозволити спливаючі чати"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string> diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml index 292cabae3cdb..fb0137658560 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"اوپر دائیں جانب لے جائيں"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"نیچے بائیں جانب لے جائیں"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"نیچے دائیں جانب لے جائیں"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> کو پھیلائیں"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> کو سکیڑیں"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ترتیبات"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"بلبلہ برخاست کریں"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"گفتگو بلبلہ نہ کریں"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"سمجھ آ گئی"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"کوئی حالیہ بلبلہ نہیں"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حالیہ بلبلے اور برخاست شدہ بلبلے یہاں ظاہر ہوں گے"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"بلبلے کے ذریعے چیٹ کریں"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"نئی گفتگوئیں آپ کی اسکرین کے نیچے کونے میں آئیکنز کے طور پر ظاہر ہوتی ہیں۔ انہیں پھیلانے کے لیے تھپتھپائیں یا انہیں برخاست کرنے کے لیے گھسیٹیں۔"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"کسی بھی وقت بلبلے کو کنٹرول کریں"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"یہ نظم کرنے کے لیے یہاں تھپتھپائیں کہ کون سی ایپس اور گفتگوئیں بلبلہ سکتی ہیں"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string> diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml index 5f33fe941040..81e63de9767c 100644 --- a/libs/WindowManager/Shell/res/values-uz/strings.xml +++ b/libs/WindowManager/Shell/res/values-uz/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuqori oʻngga surish"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Quyi chapga surish"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Quyi oʻngga surish"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ni yoyish"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ni yopish"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> sozlamalari"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bulutchani yopish"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Suhbatlar bulutchalar shaklida chiqmasin"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hech qanday bulutcha topilmadi"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Eng oxirgi va yopilgan bulutchali chatlar shu yerda chiqadi"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bulutchalar yordamida suhbatlashish"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Yangi suhbatlar ekraningizning pastki burchagida belgilar shaklida koʻrinadi. Ochish uchun bosing yoki yopish uchun torting"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Bulutchalardagi bildirishnomalar"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Bulutchalarda bildirishnomalar chiqishiga ruxsat beruvchi ilova va suhbatlarni tanlang."</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string> diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml index 29b3b854e3c3..16bbd5eda23e 100644 --- a/libs/WindowManager/Shell/res/values-vi/strings.xml +++ b/libs/WindowManager/Shell/res/values-vi/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Chuyển lên trên cùng bên phải"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Chuyển tới dưới cùng bên trái"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Chuyển tới dưới cùng bên phải"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"mở rộng <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"thu gọn <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Cài đặt <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Đóng bong bóng"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Dừng sử dụng bong bóng cho cuộc trò chuyện"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Đã hiểu"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Không có bong bóng trò chuyện nào gần đây"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Bong bóng trò chuyện đã đóng và bong bóng trò chuyện gần đây sẽ xuất hiện ở đây"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Trò chuyện bằng bong bóng trò chuyện"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Các cuộc trò chuyện mới sẽ xuất hiện dưới dạng biểu tượng ở góc dưới màn hình. Hãy nhấn vào các cuộc trò chuyện đó để mở rộng hoặc kéo để bỏ qua."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kiểm soát bong bóng bất cứ lúc nào"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Nhấn vào đây để quản lý việc dùng bong bóng cho các ứng dụng và cuộc trò chuyện"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml index 7820965a81c1..c12ec8423858 100644 --- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移至右上角"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移至左下角"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移至右下角"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"展开“<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收起“<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>设置"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"关闭对话泡"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不以对话泡形式显示对话"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"知道了"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近没有对话泡"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"此处会显示最近的对话泡和已关闭的对话泡"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"使用对话泡聊天"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"新对话会以图标形式显示在屏幕底部的角落中。点按图标即可展开对话,拖动图标即可关闭对话。"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"随时控制对话泡"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"点按此处即可管理哪些应用和对话可以显示对话泡"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index f0df04a1ea53..c9543488688b 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移去右上角"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移去左下角"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移去右下角"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"打開<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收埋<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」設定"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"關閉小視窗氣泡"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不要透過小視窗顯示對話"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"知道了"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"沒有最近曾使用的小視窗"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近使用和關閉的小視窗會在這裡顯示"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"使用對話氣泡進行即時通訊"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"畫面底部的角落會顯示新對話圖示。輕按即可展開圖示;拖曳即可關閉。"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"隨時控制對話氣泡"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"輕按這裡即可管理哪些應用程式和對話可以使用對話氣泡"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml index a9773634ea3d..d25bfd74ca04 100644 --- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移至右上方"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移至左下方"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移至右下方"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"展開「<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>」"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收合「<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>」"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」設定"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"關閉對話框"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不要以對話框形式顯示對話"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"我知道了"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近沒有任何對話框"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近的對話框和已關閉的對話框會顯示在這裡"</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"透過對話框進行即時通訊"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"畫面底部的角落會顯示新對話圖示。輕觸可展開圖示,拖曳即可關閉。"</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"你隨時可以控管對話框的各項設定"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"輕觸這裡即可管理哪些應用程式和對話可顯示對話框"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string> diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml index a6903a38ecf4..bd62b65ccdf2 100644 --- a/libs/WindowManager/Shell/res/values-zu/strings.xml +++ b/libs/WindowManager/Shell/res/values-zu/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Hambisa phezulu ngakwesokudla"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Hambisa inkinobho ngakwesokunxele"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Hambisa inkinobho ngakwesokudla"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"nweba <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"goqa <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> izilungiselelo"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Cashisa ibhamuza"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ungayibhamuzi ingxoxo"</string> @@ -76,10 +78,8 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ngiyezwa"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Awekho amabhamuza akamuva"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Amabhamuza akamuva namabhamuza asusiwe azobonakala lapha."</string> - <!-- no translation found for bubble_bar_education_stack_title (2486903590422497245) --> - <skip /> - <!-- no translation found for bubble_bar_education_stack_text (2446934610817409820) --> - <skip /> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Xoxa usebenzisa amabhamuza"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Izingxoxo ezintsha zivela njengezithonjana ekhoneni eliphansi lesikrini sakho. Thepha ukuze uzikhulise noma uhudule ukuze uzichithe."</string> <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Lawula amabhamuza noma nini"</string> <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Thepha lapha ukuze ulawule ukuthi yimaphi ama-app kanye nezingxoxo ezingenza amabhamuza"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java index 5eeb3b650074..b141bebbe8b1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java @@ -18,12 +18,16 @@ package com.android.wm.shell.compatui; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.annotation.IdRes; import android.annotation.NonNull; import android.content.Context; import android.util.AttributeSet; import android.view.View; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; +import android.view.animation.PathInterpolator; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; @@ -36,14 +40,29 @@ import com.android.wm.shell.R; */ public class UserAspectRatioSettingsLayout extends LinearLayout { + private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); + + private static final Interpolator PATH_INTERPOLATOR = + new PathInterpolator(0.2f, 0f, 0f, 1f); + private static final float ALPHA_FULL_TRANSPARENT = 0f; private static final float ALPHA_FULL_OPAQUE = 1f; - private static final long VISIBILITY_ANIMATION_DURATION_MS = 50; + private static final float SCALE_START = 0.8f; + + private static final float SCALE_END = 1f; + + private static final long FADE_ANIMATION_DURATION_MS = 167; + + private static final long SCALE_ANIMATION_DURATION_MS = 300; private static final String ALPHA_PROPERTY_NAME = "alpha"; + private static final String SCALE_X_PROPERTY_NAME = "scaleX"; + + private static final String SCALE_Y_PROPERTY_NAME = "scaleY"; + private UserAspectRatioSettingsWindowManager mWindowManager; public UserAspectRatioSettingsLayout(Context context) { @@ -88,7 +107,7 @@ public class UserAspectRatioSettingsLayout extends LinearLayout { if (show) { showItem(view); } else { - view.setVisibility(visibility); + hideItem(view); } } @@ -121,16 +140,40 @@ public class UserAspectRatioSettingsLayout extends LinearLayout { } private void showItem(@NonNull View view) { - view.setVisibility(View.VISIBLE); + final AnimatorSet animatorSet = new AnimatorSet(); final ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME, ALPHA_FULL_TRANSPARENT, ALPHA_FULL_OPAQUE); - fadeIn.setDuration(VISIBILITY_ANIMATION_DURATION_MS); - fadeIn.addListener(new AnimatorListenerAdapter() { + fadeIn.setDuration(FADE_ANIMATION_DURATION_MS); + fadeIn.setInterpolator(LINEAR_INTERPOLATOR); + final ObjectAnimator scaleY = + ObjectAnimator.ofFloat(view, SCALE_Y_PROPERTY_NAME, SCALE_START, SCALE_END); + final ObjectAnimator scaleX = + ObjectAnimator.ofFloat(view, SCALE_X_PROPERTY_NAME, SCALE_START, SCALE_END); + scaleX.setDuration(SCALE_ANIMATION_DURATION_MS); + scaleX.setInterpolator(PATH_INTERPOLATOR); + scaleY.setDuration(SCALE_ANIMATION_DURATION_MS); + scaleY.setInterpolator(PATH_INTERPOLATOR); + animatorSet.addListener(new AnimatorListenerAdapter() { @Override - public void onAnimationEnd(Animator animation) { + public void onAnimationStart(Animator animation) { view.setVisibility(View.VISIBLE); } }); - fadeIn.start(); + animatorSet.playTogether(fadeIn, scaleY, scaleX); + animatorSet.start(); + } + + private void hideItem(@NonNull View view) { + final ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME, + ALPHA_FULL_OPAQUE, ALPHA_FULL_TRANSPARENT); + fadeOut.setDuration(FADE_ANIMATION_DURATION_MS); + fadeOut.setInterpolator(LINEAR_INTERPOLATOR); + fadeOut.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setVisibility(View.GONE); + } + }); + fadeOut.start(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index f8dd208f96db..09ba4f79326e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -37,6 +37,7 @@ import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_NONE import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_TO_FRONT +import android.window.RemoteTransition import android.window.TransitionInfo import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction @@ -65,7 +66,9 @@ import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.sysui.ShellSharedConstants +import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.transition.Transitions.TransitionHandler import com.android.wm.shell.util.KtProtoLog import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration import com.android.wm.shell.windowdecor.MoveToDesktopAnimator @@ -139,20 +142,22 @@ class DesktopTasksController( } /** Show all tasks, that are part of the desktop, on top of launcher */ - fun showDesktopApps(displayId: Int) { + fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition? = null) { KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps") val wct = WindowContainerTransaction() - // TODO(b/278084491): pass in display id bringDesktopAppsToFront(displayId, wct) - // Execute transaction if there are pending operations - if (!wct.isEmpty) { - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - // TODO(b/268662477): add animation for the transition - transitions.startTransition(TRANSIT_NONE, wct, null /* handler */) - } else { - shellTaskOrganizer.applyTransaction(wct) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + // TODO(b/255649902): ensure remote transition is supplied once state is introduced + val transitionType = if (remoteTransition == null) TRANSIT_NONE else TRANSIT_TO_FRONT + val handler = remoteTransition?.let { + OneShotRemoteHandler(transitions.mainExecutor, remoteTransition) + } + transitions.startTransition(transitionType, wct, handler).also { t -> + handler?.setTransition(t) } + } else { + shellTaskOrganizer.applyTransaction(wct) } } @@ -1093,11 +1098,11 @@ class DesktopTasksController( controller = null } - override fun showDesktopApps(displayId: Int) { + override fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition?) { ExecutorUtils.executeRemoteCallWithTaskPermission( controller, "showDesktopApps" - ) { c -> c.showDesktopApps(displayId) } + ) { c -> c.showDesktopApps(displayId, remoteTransition) } } override fun stashDesktopApps(displayId: Int) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl index 47edfd455f5a..6bdaf1eadb8a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl @@ -17,6 +17,7 @@ package com.android.wm.shell.desktopmode; import android.app.ActivityManager.RunningTaskInfo; +import android.window.RemoteTransition; import com.android.wm.shell.desktopmode.IDesktopTaskListener; /** @@ -25,7 +26,7 @@ import com.android.wm.shell.desktopmode.IDesktopTaskListener; interface IDesktopMode { /** Show apps on the desktop on the given display */ - void showDesktopApps(int displayId); + void showDesktopApps(int displayId, in RemoteTransition remoteTransition); /** Stash apps on the desktop to allow launching another app from home screen */ void stashDesktopApps(int displayId); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java index 13c0ac4bbaa7..b71c48e16acb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java @@ -76,6 +76,10 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler private IRemoteTransition mOccludeByDreamTransition = null; private IRemoteTransition mUnoccludeTransition = null; + // While set true, Keyguard has created a remote animation runner to handle the open app + // transition. + private boolean mIsLaunchingActivityOverLockscreen; + private final class StartedTransition { final TransitionInfo mInfo; final SurfaceControl.Transaction mFinishT; @@ -120,7 +124,7 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback) { - if (!handles(info)) { + if (!handles(info) || mIsLaunchingActivityOverLockscreen) { return false; } @@ -313,5 +317,11 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler mUnoccludeTransition = unoccludeTransition; }); } + + @Override + public void setLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) { + mMainExecutor.execute(() -> + mIsLaunchingActivityOverLockscreen = isLaunchingActivityOverLockscreen); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java index b4b327f0eff2..33c299f0b161 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java @@ -38,4 +38,9 @@ public interface KeyguardTransitions { @NonNull IRemoteTransition occludeTransition, @NonNull IRemoteTransition occludeByDreamTransition, @NonNull IRemoteTransition unoccludeTransition) {} + + /** + * Notify whether keyguard has created a remote animation runner for next app launch. + */ + default void setLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) {} } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java index 0d77a2e4610c..ef8393c3b5b1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java @@ -29,12 +29,16 @@ import android.content.pm.ShortcutInfo; import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; +import android.os.Handler; +import android.os.Looper; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewTreeObserver; +import com.android.internal.annotations.VisibleForTesting; + import java.util.concurrent.Executor; /** @@ -74,6 +78,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private final TaskViewTaskController mTaskViewTaskController; private Region mObscuredTouchRegion; private Insets mCaptionInsets; + private Handler mHandler; public TaskView(Context context, TaskViewTaskController taskViewTaskController) { super(context, null, 0, 0, true /* disableBackgroundLayer */); @@ -81,6 +86,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, // TODO(b/266736992): Think about a better way to set the TaskViewBase on the // TaskViewTaskController and vice-versa mTaskViewTaskController.setTaskViewBase(this); + mHandler = Handler.getMain(); getHolder().addCallback(this); } @@ -117,14 +123,16 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { onLocationChanged(); if (taskInfo.taskDescription != null) { - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + final int bgColor = taskInfo.taskDescription.getBackgroundColor(); + runOnViewThread(() -> setResizeBackgroundColor(bgColor)); } } @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { if (taskInfo.taskDescription != null) { - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + final int bgColor = taskInfo.taskDescription.getBackgroundColor(); + runOnViewThread(() -> setResizeBackgroundColor(bgColor)); } } @@ -143,7 +151,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, @Override public void setResizeBgColor(SurfaceControl.Transaction t, int bgColor) { - setResizeBackgroundColor(t, bgColor); + runOnViewThread(() -> setResizeBackgroundColor(t, bgColor)); } /** @@ -272,12 +280,14 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnComputeInternalInsetsListener(this); + mHandler = getHandler(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeOnComputeInternalInsetsListener(this); + mHandler = Handler.getMain(); } /** Returns the task info for the task in the TaskView. */ @@ -285,4 +295,24 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, public ActivityManager.RunningTaskInfo getTaskInfo() { return mTaskViewTaskController.getTaskInfo(); } + + /** + * Sets the handler, only for testing. + */ + @VisibleForTesting + void setHandler(Handler viewHandler) { + mHandler = viewHandler; + } + + /** + * Ensures that the given runnable runs on the view's thread. + */ + private void runOnViewThread(Runnable r) { + if (mHandler.getLooper().isCurrentThread()) { + r.run(); + } else { + // If this call is not from the same thread as the view, then post it + mHandler.post(r); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 00f6a1cc1167..83dc7fa5e869 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -111,6 +111,14 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, WindowContainerTransaction mFinishWCT = null; /** + * Whether the transition has request for remote transition while mLeftoversHandler + * isn't remote transition handler. + * If true and the mLeftoversHandler can handle the transition, need to notify remote + * transition handler to consume the transition. + */ + boolean mHasRequestToRemote; + + /** * Mixed transitions are made up of multiple "parts". This keeps track of how many * parts are currently animating. */ @@ -200,6 +208,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE, transition); mixed.mLeftoversHandler = handler.first; mActiveTransitions.add(mixed); + if (mixed.mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { + mixed.mHasRequestToRemote = true; + mPlayer.getRemoteTransitionHandler().handleRequest(transition, request); + } return handler.second; } else if (mSplitHandler.isSplitScreenVisible() && isOpeningType(request.getType()) @@ -316,12 +328,22 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, // the time of handleRequest, but we need more information than is available at that time. if (KeyguardTransitionHandler.handles(info)) { if (mixed != null && mixed.mType != MixedTransition.TYPE_KEYGUARD) { - ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, - "Converting mixed transition into a keyguard transition"); - onTransitionConsumed(transition, false, null); + final MixedTransition keyguardMixed = + new MixedTransition(MixedTransition.TYPE_KEYGUARD, transition); + mActiveTransitions.add(keyguardMixed); + final boolean hasAnimateKeyguard = animateKeyguard(keyguardMixed, info, + startTransaction, finishTransaction, finishCallback); + if (hasAnimateKeyguard) { + ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "Converting mixed transition into a keyguard transition"); + // Consume the original mixed transition + onTransitionConsumed(transition, false, null); + return true; + } else { + // Keyguard handler cannot handle it, process through original mixed + mActiveTransitions.remove(keyguardMixed); + } } - mixed = new MixedTransition(MixedTransition.TYPE_KEYGUARD, transition); - mActiveTransitions.add(mixed); } if (mixed == null) return false; @@ -332,8 +354,17 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) { return false; } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - return animateOpenIntentWithRemoteAndPip(mixed, info, startTransaction, - finishTransaction, finishCallback); + final boolean handledToPip = animateOpenIntentWithRemoteAndPip(mixed, info, + startTransaction, finishTransaction, finishCallback); + // Consume the transition on remote handler if the leftover handler already handle this + // transition. And if it cannot, the transition will be handled by remote handler, so + // don't consume here. + // Need to check leftOverHandler as it may change in #animateOpenIntentWithRemoteAndPip + if (handledToPip && mixed.mHasRequestToRemote + && mixed.mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { + mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, false, null); + } + return handledToPip; } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); @@ -799,5 +830,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } else if (mixed.mType == MixedTransition.TYPE_UNFOLD) { mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT); } + if (mixed.mHasRequestToRemote) { + mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, aborted, finishT); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 7df658e6c9db..de03f5826925 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -99,7 +99,6 @@ import android.window.WindowContainerTransaction; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.policy.AttributeCache; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.common.ProtoLog; @@ -182,7 +181,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { /* broadcastPermission = */ null, mMainHandler); - AttributeCache.init(mContext); + TransitionAnimation.initAttributeCache(mContext, mMainHandler); } private void updateEnterpriseThumbnailDrawable() { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt index 77f14f1b66a3..adf92d8854ff 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt @@ -50,7 +50,7 @@ abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) { } @Before - fun before() { + fun setUp() { Assume.assumeTrue(tapl.isTablet && letterboxRule.isIgnoreOrientationRequest) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt new file mode 100644 index 000000000000..ba2b3e7e2781 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt @@ -0,0 +1,199 @@ +/* + * 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.flicker.appcompat + +import android.os.Build +import android.tools.common.datatypes.Rect +import android.platform.test.annotations.Postsubmit +import android.system.helpers.CommandsHelper +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.common.flicker.assertions.FlickerTest +import android.tools.common.traces.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import android.tools.device.helpers.FIND_TIMEOUT +import android.tools.device.traces.parsers.toFlickerComponent +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.Until +import com.android.server.wm.flicker.helpers.LetterboxAppHelper +import com.android.server.wm.flicker.testapp.ActivityOptions +import org.junit.Assume +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +/** + * Test rotating an immersive app in fullscreen. + * + * To run this test: `atest WMShellFlickerTestsOther:RotateImmersiveAppInFullscreenTest` + * + * Actions: + * ``` + * Rotate the device by 90 degrees to trigger a rotation through sensors + * Verify that the button exists + * ``` + * + * Notes: + * ``` + * Some default assertions that are inherited from + * the `BaseTest` are ignored due to the nature of the immersive apps. + * + * This test only works with Cuttlefish devices. + * ``` + */ +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) { + + private val immersiveApp = LetterboxAppHelper(instrumentation, + launcherName = ActivityOptions.PortraitImmersiveActivity.LABEL, + component = + ActivityOptions.PortraitImmersiveActivity.COMPONENT.toFlickerComponent()) + + private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation) + private val execAdb: (String) -> String = { cmd -> cmdHelper.executeShellCommand(cmd) } + + protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) + + private val isCuttlefishDevice: Boolean = Build.MODEL.contains("Cuttlefish") + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit + get() = { + setup { + setStartRotation() + immersiveApp.launchViaIntent(wmHelper) + startDisplayBounds = + wmHelper.currentState.layerState.physicalDisplayBounds + ?: error("Display not found") + } + transitions { + if (isCuttlefishDevice) { + // Simulates a device rotation through sensors because the rotation button + // only appears in a rotation event through sensors + execAdb("/vendor/bin/cuttlefish_sensor_injection rotate 0") + // verify rotation button existence + val rotationButtonSelector = By.res(LAUNCHER_PACKAGE, "rotate_suggestion") + uiDevice.wait(Until.hasObject(rotationButtonSelector), FIND_TIMEOUT) + uiDevice.findObject(rotationButtonSelector) + ?: error("rotation button not found") + } + } + teardown { + immersiveApp.exit(wmHelper) + } + } + + @Before + fun setUpForImmersiveAppTests() { + Assume.assumeTrue(isCuttlefishDevice) + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun taskBarLayerIsVisibleAtStartAndEnd() { + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun navBarLayerIsVisibleAtStartAndEnd() { + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun statusBarLayerIsVisibleAtStartAndEnd() { + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun taskBarWindowIsAlwaysVisible() { + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun navBarWindowIsAlwaysVisible() { + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun statusBarWindowIsAlwaysVisible() { + } + + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun statusBarLayerPositionAtStartAndEnd() { + } + + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + } + + /** Test that app is fullscreen by checking status bar and task bar visibility. */ + @Postsubmit + @Test + fun appWindowFullScreen() { + flicker.assertWmEnd { + this.isAppWindowInvisible(ComponentNameMatcher.STATUS_BAR) + .isAppWindowInvisible(ComponentNameMatcher.TASK_BAR) + .visibleRegion(immersiveApp).coversExactly(startDisplayBounds) + } + } + + /** Test that app is in the original rotation we have set up. */ + @Postsubmit + @Test + fun appInOriginalRotation() { + flicker.assertWmEnd { + this.hasRotation(Rotation.ROTATION_90) + } + } + + companion object { + private var startDisplayBounds = Rect.EMPTY + const val LAUNCHER_PACKAGE = "com.google.android.apps.nexuslauncher" + + /** + * Creates the test configurations. + * + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTest> { + return LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_90), + // TODO(b/292403378): 3 button mode not added as rotation button is hidden in taskbar + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java new file mode 100644 index 000000000000..0df42b3cc5e4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java @@ -0,0 +1,59 @@ +/* + * 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; + +import android.os.IBinder; +import android.os.RemoteException; +import android.view.SurfaceControl; +import android.window.IRemoteTransition; +import android.window.IRemoteTransitionFinishedCallback; +import android.window.TransitionInfo; +import android.window.WindowContainerTransaction; + +/** + * {@link IRemoteTransition} for testing purposes. + * Stores info about + * {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction, + * IRemoteTransitionFinishedCallback)} being called. + */ +public class TestRemoteTransition extends IRemoteTransition.Stub { + private boolean mCalled = false; + final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction(); + + @Override + public void startAnimation(IBinder transition, TransitionInfo info, + SurfaceControl.Transaction startTransaction, + IRemoteTransitionFinishedCallback finishCallback) + throws RemoteException { + mCalled = true; + finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */); + } + + @Override + public void mergeAnimation(IBinder transition, TransitionInfo info, + SurfaceControl.Transaction t, IBinder mergeTarget, + IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { + } + + /** + * Check whether this remote transition + * {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction, + * IRemoteTransitionFinishedCallback)} is called + */ + public boolean isCalled() { + return mCalled; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index c6cccc059e89..664fbb22b13b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -28,10 +28,10 @@ import android.testing.AndroidTestingRunner import android.view.Display.DEFAULT_DISPLAY import android.view.WindowManager import android.view.WindowManager.TRANSIT_CHANGE -import android.view.WindowManager.TRANSIT_NONE import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_TO_FRONT import android.window.DisplayAreaInfo +import android.window.RemoteTransition import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER @@ -43,6 +43,7 @@ import com.android.wm.shell.MockToken import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.TestRemoteTransition import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.TestShellExecutor import com.android.wm.shell.common.DisplayController @@ -57,8 +58,10 @@ import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS +import com.android.wm.shell.transition.Transitions.TransitionHandler import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage @@ -69,6 +72,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.eq +import org.mockito.ArgumentMatchers.isA import org.mockito.ArgumentMatchers.isNull import org.mockito.Mock import org.mockito.Mockito @@ -174,9 +178,9 @@ class DesktopTasksControllerTest : ShellTestCase() { markTaskHidden(task1) markTaskHidden(task2) - controller.showDesktopApps(DEFAULT_DISPLAY) + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) - val wct = getLatestWct(expectTransition = TRANSIT_NONE) + val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java) assertThat(wct.hierarchyOps).hasSize(3) // Expect order to be from bottom: home, task1, task2 wct.assertReorderAt(index = 0, homeTask) @@ -192,9 +196,9 @@ class DesktopTasksControllerTest : ShellTestCase() { markTaskVisible(task1) markTaskVisible(task2) - controller.showDesktopApps(DEFAULT_DISPLAY) + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) - val wct = getLatestWct(expectTransition = TRANSIT_NONE) + val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java) assertThat(wct.hierarchyOps).hasSize(3) // Expect order to be from bottom: home, task1, task2 wct.assertReorderAt(index = 0, homeTask) @@ -210,9 +214,9 @@ class DesktopTasksControllerTest : ShellTestCase() { markTaskHidden(task1) markTaskVisible(task2) - controller.showDesktopApps(DEFAULT_DISPLAY) + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) - val wct = getLatestWct(expectTransition = TRANSIT_NONE) + val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java) assertThat(wct.hierarchyOps).hasSize(3) // Expect order to be from bottom: home, task1, task2 wct.assertReorderAt(index = 0, homeTask) @@ -224,9 +228,9 @@ class DesktopTasksControllerTest : ShellTestCase() { fun showDesktopApps_noActiveTasks_reorderHomeToTop() { val homeTask = setUpHomeTask() - controller.showDesktopApps(DEFAULT_DISPLAY) + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) - val wct = getLatestWct(expectTransition = TRANSIT_NONE) + val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java) assertThat(wct.hierarchyOps).hasSize(1) wct.assertReorderAt(index = 0, homeTask) } @@ -240,9 +244,9 @@ class DesktopTasksControllerTest : ShellTestCase() { markTaskHidden(taskDefaultDisplay) markTaskHidden(taskSecondDisplay) - controller.showDesktopApps(DEFAULT_DISPLAY) + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) - val wct = getLatestWct(expectTransition = TRANSIT_NONE) + val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java) assertThat(wct.hierarchyOps).hasSize(2) // Expect order to be from bottom: home, task wct.assertReorderAt(index = 0, homeTaskDefaultDisplay) @@ -373,7 +377,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask() task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN controller.moveToFullscreen(task) - val wct = getLatestWct(expectTransition = TRANSIT_CHANGE) + val wct = getLatestWct(type = TRANSIT_CHANGE) assertThat(wct.changes[task.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) } @@ -383,7 +387,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask() task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM controller.moveToFullscreen(task) - val wct = getLatestWct(expectTransition = TRANSIT_CHANGE) + val wct = getLatestWct(type = TRANSIT_CHANGE) assertThat(wct.changes[task.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FULLSCREEN) } @@ -401,7 +405,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.moveToFullscreen(taskDefaultDisplay) - with(getLatestWct(expectTransition = TRANSIT_CHANGE)) { + with(getLatestWct(type = TRANSIT_CHANGE)) { assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder()) assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder()) } @@ -414,7 +418,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.moveTaskToFront(task1) - val wct = getLatestWct(expectTransition = TRANSIT_TO_FRONT) + val wct = getLatestWct(type = TRANSIT_TO_FRONT) assertThat(wct.hierarchyOps).hasSize(1) wct.assertReorderAt(index = 0, task1) } @@ -439,7 +443,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY) controller.moveToNextDisplay(task.taskId) - with(getLatestWct(expectTransition = TRANSIT_CHANGE)) { + with(getLatestWct(type = TRANSIT_CHANGE)) { assertThat(hierarchyOps).hasSize(1) assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder()) assertThat(hierarchyOps[0].isReparent).isTrue() @@ -461,7 +465,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(displayId = SECOND_DISPLAY) controller.moveToNextDisplay(task.taskId) - with(getLatestWct(expectTransition = TRANSIT_CHANGE)) { + with(getLatestWct(type = TRANSIT_CHANGE)) { assertThat(hierarchyOps).hasSize(1) assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder()) assertThat(hierarchyOps[0].isReparent).isTrue() @@ -747,11 +751,16 @@ class DesktopTasksControllerTest : ShellTestCase() { } private fun getLatestWct( - @WindowManager.TransitionType expectTransition: Int = TRANSIT_OPEN + @WindowManager.TransitionType type: Int = TRANSIT_OPEN, + handlerClass: Class<out TransitionHandler>? = null ): WindowContainerTransaction { val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) if (ENABLE_SHELL_TRANSITIONS) { - verify(transitions).startTransition(eq(expectTransition), arg.capture(), isNull()) + if (handlerClass == null) { + verify(transitions).startTransition(eq(type), arg.capture(), isNull()) + } else { + verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass)) + } } else { verify(shellTaskOrganizer).applyTransaction(arg.capture()) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index 5efd9ad97a3e..d542139ff9fd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -47,11 +47,8 @@ import static org.mockito.Mockito.spy; import android.annotation.NonNull; import android.app.ActivityManager; import android.os.IBinder; -import android.os.RemoteException; import android.view.SurfaceControl; import android.view.SurfaceSession; -import android.window.IRemoteTransition; -import android.window.IRemoteTransitionFinishedCallback; import android.window.RemoteTransition; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; @@ -65,6 +62,7 @@ import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.TestRemoteTransition; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.TransitionInfoBuilder; import com.android.wm.shell.common.DisplayController; @@ -205,7 +203,7 @@ public class SplitTransitionTests extends ShellTestCase { // Make sure split-screen is now visible assertTrue(mStageCoordinator.isSplitScreenVisible()); - assertTrue(testRemote.mCalled); + assertTrue(testRemote.isCalled()); } @Test @@ -468,24 +466,4 @@ public class SplitTransitionTests extends ShellTestCase { return out; } - class TestRemoteTransition extends IRemoteTransition.Stub { - boolean mCalled = false; - final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction(); - - @Override - public void startAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction startTransaction, - IRemoteTransitionFinishedCallback finishCallback) - throws RemoteException { - mCalled = true; - finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */); - } - - @Override - public void mergeAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction t, IBinder mergeTarget, - IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { - } - } - } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java index 0088051928fb..4afb29ecd98c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java @@ -43,6 +43,8 @@ import android.content.Context; import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; +import android.os.Handler; +import android.os.Looper; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.SurfaceControl; @@ -88,6 +90,10 @@ public class TaskViewTest extends ShellTestCase { SyncTransactionQueue mSyncQueue; @Mock Transitions mTransitions; + @Mock + Looper mViewLooper; + @Mock + Handler mViewHandler; SurfaceSession mSession; SurfaceControl mLeash; @@ -105,6 +111,8 @@ public class TaskViewTest extends ShellTestCase { .build(); mContext = getContext(); + doReturn(true).when(mViewLooper).isCurrentThread(); + doReturn(mViewLooper).when(mViewHandler).getLooper(); mTaskInfo = new ActivityManager.RunningTaskInfo(); mTaskInfo.token = mToken; @@ -132,6 +140,7 @@ public class TaskViewTest extends ShellTestCase { mTaskViewTaskController = spy(new TaskViewTaskController(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue)); mTaskView = new TaskView(mContext, mTaskViewTaskController); + mTaskView.setHandler(mViewHandler); mTaskView.setListener(mExecutor, mViewListener); } @@ -646,4 +655,17 @@ public class TaskViewTest extends ShellTestCase { assertThat(mTaskViewTaskController.getTaskInfo()).isNull(); } + + @Test + public void testOnTaskInfoChangedOnSameUiThread() { + mTaskViewTaskController.onTaskInfoChanged(mTaskInfo); + verify(mViewHandler, never()).post(any()); + } + + @Test + public void testOnTaskInfoChangedOnDifferentUiThread() { + doReturn(false).when(mViewLooper).isCurrentThread(); + mTaskViewTaskController.onTaskInfoChanged(mTaskInfo); + verify(mViewHandler).post(any()); + } } diff --git a/libs/hwui/Mesh.h b/libs/hwui/Mesh.h index 764d1efcc8f4..69fda34afc78 100644 --- a/libs/hwui/Mesh.h +++ b/libs/hwui/Mesh.h @@ -166,11 +166,12 @@ public: #endif mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset, ib, mIndexCount, mIndexOffset, mBuilder->fUniforms, - mBounds) + SkSpan<SkRuntimeEffect::ChildPtr>(), mBounds) .mesh; } else { mMesh = SkMesh::Make(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset, - mBuilder->fUniforms, mBounds) + mBuilder->fUniforms, SkSpan<SkRuntimeEffect::ChildPtr>(), + mBounds) .mesh; } mIsDirty = false; diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 71f47e92e055..ff0d8d74831c 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -539,7 +539,7 @@ struct DrawSkMesh final : Op { if (!cpuMesh.indexBuffer()) { gpuMesh = SkMesh::Make(cpuMesh.refSpec(), cpuMesh.mode(), vb, cpuMesh.vertexCount(), cpuMesh.vertexOffset(), cpuMesh.refUniforms(), - cpuMesh.bounds()) + SkSpan<SkRuntimeEffect::ChildPtr>(), cpuMesh.bounds()) .mesh; } else { sk_sp<SkMesh::IndexBuffer> ib = @@ -547,7 +547,8 @@ struct DrawSkMesh final : Op { gpuMesh = SkMesh::MakeIndexed(cpuMesh.refSpec(), cpuMesh.mode(), vb, cpuMesh.vertexCount(), cpuMesh.vertexOffset(), ib, cpuMesh.indexCount(), cpuMesh.indexOffset(), - cpuMesh.refUniforms(), cpuMesh.bounds()) + cpuMesh.refUniforms(), + SkSpan<SkRuntimeEffect::ChildPtr>(), cpuMesh.bounds()) .mesh; } diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp index d1ebe6d9f576..1c3399a6650d 100644 --- a/libs/hwui/renderthread/HintSessionWrapper.cpp +++ b/libs/hwui/renderthread/HintSessionWrapper.cpp @@ -72,6 +72,7 @@ void HintSessionWrapper::destroy() { mSessionValid = true; mHintSession = nullptr; } + mResetsSinceLastReport = 0; } bool HintSessionWrapper::init() { @@ -109,12 +110,13 @@ bool HintSessionWrapper::init() { tids.push_back(mUiThreadId); tids.push_back(mRenderThreadId); - // Use a placeholder target value to initialize, - // this will always be replaced elsewhere before it gets used - int64_t defaultTargetDurationNanos = 16666667; + // Use the cached target value if there is one, otherwise use a default. This is to ensure + // the cached target and target in PowerHAL are consistent, and that it updates correctly + // whenever there is a change. + int64_t targetDurationNanos = + mLastTargetWorkDuration == 0 ? kDefaultTargetDuration : mLastTargetWorkDuration; mHintSessionFuture = CommonPool::async([=, this, tids = std::move(tids)] { - return mBinding->createSession(manager, tids.data(), tids.size(), - defaultTargetDurationNanos); + return mBinding->createSession(manager, tids.data(), tids.size(), targetDurationNanos); }); return false; } diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h index 36e91ea33c75..41891cd80a42 100644 --- a/libs/hwui/renderthread/HintSessionWrapper.h +++ b/libs/hwui/renderthread/HintSessionWrapper.h @@ -65,6 +65,7 @@ private: static constexpr nsecs_t kResetHintTimeout = 100_ms; static constexpr int64_t kSanityCheckLowerBound = 100_us; static constexpr int64_t kSanityCheckUpperBound = 10_s; + static constexpr int64_t kDefaultTargetDuration = 16666667; // Allows easier stub when testing class HintSessionBinding { diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 9924fae26194..09f09b94ac0d 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -2409,13 +2409,16 @@ public class Tuner implements AutoCloseable { @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) @Nullable public Descrambler openDescrambler() { + acquireTRMSLock("openDescrambler()"); mDemuxLock.lock(); try { - if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) { + // no need to unlock mDemuxLock (so pass null instead) as TRMS lock is already acquired + if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, null)) { return null; } return requestDescrambler(); } finally { + releaseTRMSLock(); mDemuxLock.unlock(); } } diff --git a/packages/BackupRestoreConfirmation/res/values-el/strings.xml b/packages/BackupRestoreConfirmation/res/values-el/strings.xml index cd325ef88aee..02234990606c 100644 --- a/packages/BackupRestoreConfirmation/res/values-el/strings.xml +++ b/packages/BackupRestoreConfirmation/res/values-el/strings.xml @@ -21,7 +21,7 @@ <string name="backup_confirm_text" msgid="1878021282758896593">"Έχει ζητηθεί ένα πλήρες αντίγραφο ασφαλείας όλων των δεδομένων σε έναν συνδεδεμένο επιτραπέζιο υπολογιστή. Θέλετε να επιτραπεί αυτή η ενέργεια;\n\nΑν δεν έχετε ζητήσει οι ίδιοι αυτό το αντίγραφο ασφαλείας, μην επιτρέψετε την ενέργεια."</string> <string name="allow_backup_button_label" msgid="4217228747769644068">"Δημιουργία αντιγράφων ασφαλείας για τα δεδομένα μου"</string> <string name="deny_backup_button_label" msgid="6009119115581097708">"Να μην δημιουργείται αντίγραφο ασφαλείας"</string> - <string name="restore_confirm_text" msgid="7499866728030461776">"Έχει ζητηθεί πλήρης επαναφορά όλων των δεδομένων από έναν συνδεδεμένο επιτραπέζιο υπολογιστή. Θέλετε να επιτρέψετε αυτήν την ενέργεια;\n\nΑν δεν έχετε ζητήσει οι ίδιοι αυτήν την επαναφορά, μην επιτρέψετε την ενέργεια. Θα γίνει αντικατάσταση τυχόν δεδομένων τα οποία υπάρχουν αυτήν τη στιγμή στη συσκευή σας!"</string> + <string name="restore_confirm_text" msgid="7499866728030461776">"Έχει ζητηθεί πλήρης επαναφορά όλων των δεδομένων από έναν συνδεδεμένο επιτραπέζιο υπολογιστή. Θέλετε να επιτρέψετε αυτή την ενέργεια;\n\nΑν δεν έχετε ζητήσει οι ίδιοι αυτή την επαναφορά, μην επιτρέψετε την ενέργεια. Θα γίνει αντικατάσταση τυχόν δεδομένων τα οποία υπάρχουν αυτήν τη στιγμή στη συσκευή σας!"</string> <string name="allow_restore_button_label" msgid="3081286752277127827">"Αποκατάσταση των δεδομένων μου"</string> <string name="deny_restore_button_label" msgid="1724367334453104378">"Να μην γίνει επαναφορά"</string> <string name="current_password_text" msgid="8268189555578298067">"Εισαγάγετε τον τρέχοντα κωδικό πρόσβασής αντιγράφων ασφαλείας παρακάτω:"</string> diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml index 1c8ecb710a26..201a392b2fbd 100644 --- a/packages/CompanionDeviceManager/res/values-el/strings.xml +++ b/packages/CompanionDeviceManager/res/values-el/strings.xml @@ -34,7 +34,7 @@ <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Υπηρεσίες Google Play"</string> <string name="helper_summary_computer" msgid="8774832742608187072">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά εκ μέρους της συσκευής σας <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> άδεια για πρόσβαση στις φωτογραφίες, τα αρχεία μέσων και τις ειδοποιήσεις του τηλεφώνου σας"</string> - <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Να επιτρέπεται στη συσκευή <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> να εκτελεί αυτήν την ενέργεια;"</string> + <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Να επιτρέπεται στη συσκευή <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> να εκτελεί αυτή την ενέργεια;"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά άδεια εκ μέρους της συσκευής σας <xliff:g id="DEVICE_NAME">%2$s</xliff:g> για ροή εφαρμογών και άλλων λειτουργιών του συστήματος σε συσκευές σε κοντινή απόσταση"</string> <string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string> <string name="summary_generic" msgid="1761976003668044801">"Αυτή η εφαρμογή θα μπορεί να συγχρονίζει πληροφορίες μεταξύ του τηλεφώνου και της επιλεγμένης συσκευής σας, όπως το όνομα ενός ατόμου που σας καλεί."</string> diff --git a/packages/CredentialManager/res/values-af/strings.xml b/packages/CredentialManager/res/values-af/strings.xml index 0c13cb245cac..a2d2a96fe591 100644 --- a/packages/CredentialManager/res/values-af/strings.xml +++ b/packages/CredentialManager/res/values-af/strings.xml @@ -24,42 +24,42 @@ <string name="string_learn_more" msgid="4541600451688392447">"Kom meer te wete"</string> <string name="content_description_show_password" msgid="3283502010388521607">"Wys wagwoord"</string> <string name="content_description_hide_password" msgid="6841375971631767996">"Versteek wagwoord"</string> - <string name="passkey_creation_intro_title" msgid="4251037543787718844">"Veiliger met wagwoordsleutels"</string> - <string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Met wagwoordsleutels hoef jy nie komplekse wagwoorde te skep of te onthou nie"</string> + <string name="passkey_creation_intro_title" msgid="4251037543787718844">"Veiliger met toegangsleutels"</string> + <string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Met toegangsleutels hoef jy nie komplekse wagwoorde te skep of te onthou nie"</string> <string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Wagwoordsleutels is geënkripteerde digitale sleutels wat jy met jou vingerafdruk, gesig of skermslot skep"</string> <string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Hulle word in ’n wagwoordbestuurder gestoor sodat jy op ander toestelle kan aanmeld"</string> - <string name="more_about_passkeys_title" msgid="7797903098728837795">"Meer oor wagwoordsleutels"</string> + <string name="more_about_passkeys_title" msgid="7797903098728837795">"Meer oor toegangsleutels"</string> <string name="passwordless_technology_title" msgid="2497513482056606668">"Wagwoordlose tegnologie"</string> - <string name="passwordless_technology_detail" msgid="6853928846532955882">"Met wagwoordsleutels kan jy aanmeld sonder om op wagwoorde staat te maak. Jy moet net jou vingerafdruk, gesigherkenning, PIN of swieppatroon gebruik om jou identiteit te verifieer en ’n wagwoordsleutel te skep."</string> + <string name="passwordless_technology_detail" msgid="6853928846532955882">"Met toegangsleutels kan jy aanmeld sonder om op wagwoorde staat te maak. Jy moet net jou vingerafdruk, gesigherkenning, PIN of swieppatroon gebruik om jou identiteit te verifieer en ’n toegangsleutel te skep."</string> <string name="public_key_cryptography_title" msgid="6751970819265298039">"Publiekesleutelkriptografie"</string> - <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Gegrond op FIDO Alliance (wat Google, Apple, Microsoft en meer insluit) en W3C-standaarde, gebruik wagwoordsleutels kriptografiese sleutelpare. Anders as die gebruikernaam en string karakters wat ons vir wagwoorde gebruik, word ’n private-publieke-sleutelpaar vir ’n app of webwerf geskep. Die private sleutel word veilig op jou toestel of wagwoordbestuurder geberg en bevestig jou identiteit. Die publieke sleutel word met die app of webwerfbediener gedeel. Met passende sleutels kan jy dadelik registreer en aanmeld."</string> + <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Gegrond op FIDO Alliance (wat Google, Apple, Microsoft en meer insluit) en W3C-standaarde, gebruik toegangsleutels kriptografiese sleutelpare. Anders as die gebruikernaam en string karakters wat ons vir wagwoorde gebruik, word ’n private-publieke-sleutelpaar vir ’n app of webwerf geskep. Die private sleutel word veilig op jou toestel of wagwoordbestuurder geberg en bevestig jou identiteit. Die publieke sleutel word met die app of webwerfbediener gedeel. Met passende sleutels kan jy dadelik registreer en aanmeld."</string> <string name="improved_account_security_title" msgid="1069841917893513424">"Verbeterde rekeningsekuriteit"</string> <string name="improved_account_security_detail" msgid="9123750251551844860">"Elke sleutel is uitsluitlik gekoppel aan die app of webwerf waarvoor dit geskep is, en daarom kan jy nooit per ongeluk by ’n bedrieglike app of webwerf aanmeld nie. En omdat bedieners net publieke sleutels hou, is kuberkrakery baie moeiliker."</string> <string name="seamless_transition_title" msgid="5335622196351371961">"Moeitevrye oorgang"</string> - <string name="seamless_transition_detail" msgid="4475509237171739843">"Wagwoorde sal steeds saam met wagwoordsleutels beskikbaar wees terwyl ons na ’n wagwoordlose toekoms beweeg."</string> + <string name="seamless_transition_detail" msgid="4475509237171739843">"Wagwoorde sal steeds saam met toegangsleutels beskikbaar wees terwyl ons na ’n wagwoordlose toekoms beweeg."</string> <string name="choose_provider_title" msgid="8870795677024868108">"Kies waar om jou <xliff:g id="CREATETYPES">%1$s</xliff:g> te stoor"</string> <string name="choose_provider_body" msgid="4967074531845147434">"Kies ’n wagwoordbestuurder om jou inligting te stoor en volgende keer vinniger aan te meld"</string> - <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Skep wagwoordsleutel vir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string> + <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Skep toegangsleutel vir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="7097275038523578687">"Stoor wagwoord vir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Stoor aanmeldinligting vir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string> - <string name="passkey" msgid="632353688396759522">"wagwoordsleutel"</string> + <string name="passkey" msgid="632353688396759522">"toegangsleutel"</string> <string name="password" msgid="6738570945182936667">"wagwoord"</string> - <string name="passkeys" msgid="5733880786866559847">"wagwoordsleutels"</string> + <string name="passkeys" msgid="5733880786866559847">"toegangsleutels"</string> <string name="passwords" msgid="5419394230391253816">"wagwoorde"</string> <string name="sign_ins" msgid="4710739369149469208">"aanmeldings"</string> <string name="sign_in_info" msgid="2627704710674232328">"aanmeldinligting"</string> <string name="save_credential_to_title" msgid="3172811692275634301">"Stoor <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> in"</string> - <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Skep wagwoordsleutel op ’n ander toestel?"</string> + <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Skep toegangsleutel op ’n ander toestel?"</string> <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Stoor wagwoord op ’n ander toestel?"</string> <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Stoor aanmelding op ’n ander toestel?"</string> <string name="use_provider_for_all_title" msgid="4201020195058980757">"Gebruik <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> vir al jou aanmeldings?"</string> - <string name="use_provider_for_all_description" msgid="1998772715863958997">"Hierdie wagwoordbestuurder vir <xliff:g id="USERNAME">%1$s</xliff:g> sal jou wagwoorde en wagwoordsleutels berg om jou te help om maklik aan te meld"</string> + <string name="use_provider_for_all_description" msgid="1998772715863958997">"Hierdie wagwoordbestuurder vir <xliff:g id="USERNAME">%1$s</xliff:g> sal jou wagwoorde en toegangsleutels berg om jou te help om maklik aan te meld"</string> <string name="set_as_default" msgid="4415328591568654603">"Stel as verstek"</string> <string name="settings" msgid="6536394145760913145">"Instellings"</string> <string name="use_once" msgid="9027366575315399714">"Gebruik een keer"</string> - <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> wagwoorde • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> wagwoordsleutels"</string> + <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> wagwoorde • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> toegangsleutels"</string> <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> wagwoorde"</string> - <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> wagwoordsleutels"</string> + <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> toegangsleutels"</string> <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g>-eiebewyse"</string> <string name="passkey_before_subtitle" msgid="2448119456208647444">"Wagwoordsleutel"</string> <string name="another_device" msgid="5147276802037801217">"’n Ander toestel"</string> @@ -68,11 +68,11 @@ <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> <string name="accessibility_snackbar_dismiss" msgid="3456598374801836120">"Maak toe"</string> - <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_passkey_for" msgid="6236608872708021767">"Gebruik jou gestoorde toegangsleutel vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_password_for" msgid="625828023234318484">"Gebruik jou gestoorde wagwoord vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"Gebruik jou aanmelding vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"Ontsluit aanmeldingopsies vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"Kies ’n gestoorde wagwoordsleutel vir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"Kies ’n gestoorde toegangsleutel vir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"Kies ’n gestoorde wagwoord vir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"Kies ’n gestoorde aanmelding vir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"Kies ’n aanmelding vir <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 bf6bc8bbbac9..391c51191daa 100644 --- a/packages/CredentialManager/res/values-sq/strings.xml +++ b/packages/CredentialManager/res/values-sq/strings.xml @@ -32,7 +32,7 @@ <string name="passwordless_technology_title" msgid="2497513482056606668">"Teknologji pa fjalëkalime"</string> <string name="passwordless_technology_detail" msgid="6853928846532955882">"Çelësat e kalimit të lejojnë të identifikohesh pa u mbështetur te fjalëkalimet. Të duhet vetëm të përdorësh gjurmën e gishtit, njohjen e fytyrës, PIN-in ose të rrëshqasësh motivin për të verifikuar identitetin dhe për të krijuar një çelës kalimi."</string> <string name="public_key_cryptography_title" msgid="6751970819265298039">"Kriptografia e çelësit publik"</string> - <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Bazuar në aleancën FIDO (e cila përfshin Google, Apple, Microsoft e të tjera) dhe standardet W3C, çelësat e kalimit përdorin çifte çelësash kriptografikë. Ndryshe nga emri i përdoruesit dhe vargu i karaktereve që përdorim për fjalëkalime, një çift çelësash privat-publik krijohet për aplikacion ose sajtin e uebit. Çelësi privat ruhet i sigurt në pajisjen tënde ose në menaxherin e fjalëkalimeve dhe konfirmon identitetin tënd. Çelësi publik ndahet me aplikacionin ose serverin e sajtit të uebit. Me çelësat përkatës, mund të regjistrohesh dhe të identifikohesh në çast."</string> + <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Bazuar në aleancën FIDO (e cila përfshin Google, Apple, Microsoft e të tjera) dhe standardet W3C, çelësat e kalimit përdorin çifte çelësash kriptografikë. Ndryshe nga emri i përdoruesit dhe vargu i karaktereve që përdorim për fjalëkalime, një çift çelësash privat-publik krijohet për një aplikacion ose uebsajt. Çelësi privat ruhet i sigurt në pajisjen tënde ose në menaxherin e fjalëkalimeve dhe konfirmon identitetin tënd. Çelësi publik ndahet me aplikacionin ose serverin e uebsajtit. Me çelësat përkatës, mund të regjistrohesh dhe të identifikohesh në çast."</string> <string name="improved_account_security_title" msgid="1069841917893513424">"Siguri e përmirësuar e llogarisë"</string> <string name="improved_account_security_detail" msgid="9123750251551844860">"Secili çelës është i lidhur ekskluzivisht me aplikacionin ose uebsajtin për të cilin është krijuar, kështu që nuk do të identifikohesh asnjëherë gabimisht në një aplikacion ose uebsajt mashtrues. Gjithashtu, me serverët që mbajnë vetëm çelësa publikë, pirateria informatike është shumë më e vështirë."</string> <string name="seamless_transition_title" msgid="5335622196351371961">"Kalim i thjeshtuar"</string> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index 943c2b4b3a5b..ba88484518aa 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -16,23 +16,183 @@ package com.android.credentialmanager.autofill +import android.app.assist.AssistStructure +import android.content.Context +import android.credentials.GetCredentialRequest +import android.credentials.CredentialManager +import android.credentials.GetCandidateCredentialsResponse +import android.credentials.CredentialOption +import android.credentials.GetCandidateCredentialsException +import android.os.Bundle import android.os.CancellationSignal +import android.os.OutcomeReceiver +import android.service.autofill.FillRequest import android.service.autofill.AutofillService +import android.service.autofill.FillResponse import android.service.autofill.FillCallback -import android.service.autofill.FillRequest import android.service.autofill.SaveRequest import android.service.autofill.SaveCallback +import android.util.Log +import org.json.JSONObject +import java.util.concurrent.Executors class CredentialAutofillService : AutofillService() { + + companion object { + private const val TAG = "CredAutofill" + + private const val CRED_HINT_PREFIX = "credential=" + private const val REQUEST_DATA_KEY = "requestData" + private const val CANDIDATE_DATA_KEY = "candidateQueryData" + private const val SYS_PROVIDER_REQ_KEY = "isSystemProviderRequired" + private const val CRED_OPTIONS_KEY = "credentialOptions" + private const val TYPE_KEY = "type" + } + + private val credentialManager: CredentialManager = + getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager + override fun onFillRequest( request: FillRequest, cancellationSignal: CancellationSignal, callback: FillCallback ) { + val context = request.fillContexts + val structure = context[context.size - 1].structure + val callingPackage = structure.activityComponent.packageName + Log.i(TAG, "onFillRequest called for $callingPackage") + + val getCredRequest: GetCredentialRequest? = getCredManRequest(structure) + if (getCredRequest == null) { + callback.onFailure("No credential manager request found") + return + } + + val outcome = object : OutcomeReceiver<GetCandidateCredentialsResponse, + GetCandidateCredentialsException> { + override fun onResult(result: GetCandidateCredentialsResponse) { + Log.i(TAG, "getCandidateCredentials onResponse") + val fillResponse: FillResponse? = convertToFillResponse(result, request) + callback.onSuccess(fillResponse) + } + + override fun onError(error: GetCandidateCredentialsException) { + Log.i(TAG, "getCandidateCredentials onError") + callback.onFailure("error received from credential manager ${error.message}") + } + } + + credentialManager.getCandidateCredentials( + getCredRequest, + callingPackage, + CancellationSignal(), + Executors.newSingleThreadExecutor(), + outcome + ) + } + + private fun convertToFillResponse( + getCredResponse: GetCandidateCredentialsResponse, + filLRequest: FillRequest + ): FillResponse? { TODO("Not yet implemented") } override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) { TODO("Not yet implemented") } + + private fun getCredManRequest(structure: AssistStructure): GetCredentialRequest? { + val credentialOptions: MutableList<CredentialOption> = mutableListOf() + traverseStructure(structure, credentialOptions) + + if (credentialOptions.isNotEmpty()) { + return GetCredentialRequest.Builder(Bundle.EMPTY) + .setCredentialOptions(credentialOptions) + .build() + } + return null + } + + private fun traverseStructure( + structure: AssistStructure, + cmRequests: MutableList<CredentialOption> + ) { + val windowNodes: List<AssistStructure.WindowNode> = + structure.run { + (0 until windowNodeCount).map { getWindowNodeAt(it) } + } + + windowNodes.forEach { windowNode: AssistStructure.WindowNode -> + traverseNode(windowNode.rootViewNode, cmRequests) + } + } + + private fun traverseNode( + viewNode: AssistStructure.ViewNode?, + cmRequests: MutableList<CredentialOption> + ) { + val options = getCredentialOptionsFromViewNode(viewNode) + cmRequests.addAll(options) + + val children: List<AssistStructure.ViewNode>? = + viewNode?.run { + (0 until childCount).map { getChildAt(it) } + } + + children?.forEach { childNode: AssistStructure.ViewNode -> + traverseNode(childNode, cmRequests) + } + } + + private fun getCredentialOptionsFromViewNode(viewNode: AssistStructure.ViewNode?): + List<CredentialOption> { + // TODO(b/293945193) Replace with isCredential check from viewNode + val credentialHints: MutableList<String> = mutableListOf() + if (viewNode != null && viewNode.autofillHints != null) { + for (hint in viewNode.autofillHints!!) { + if (hint.startsWith(CRED_HINT_PREFIX)) { + credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX)) + } + } + } + + val credentialOptions: MutableList<CredentialOption> = mutableListOf() + for (credentialHint in credentialHints) { + convertJsonToCredentialOption(credentialHint).let { credentialOptions.addAll(it) } + } + return credentialOptions + } + + private fun convertJsonToCredentialOption(jsonString: String): List<CredentialOption> { + // TODO(b/302000646) Move this logic to jetpack so that is consistent + // with building the json + val credentialOptions: MutableList<CredentialOption> = mutableListOf() + + val json = JSONObject(jsonString) + val options = json.getJSONArray(CRED_OPTIONS_KEY) + for (i in 0 until options.length()) { + val option = options.getJSONObject(i) + + credentialOptions.add(CredentialOption( + option.getString(TYPE_KEY), + convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)), + convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)), + option.getBoolean(SYS_PROVIDER_REQ_KEY), + )) + } + return credentialOptions + } + + private fun convertJsonToBundle(json: JSONObject): Bundle { + val result = Bundle() + json.keys().forEach { + val v = json.get(it) + when (v) { + is String -> result.putString(it, v) + is Boolean -> result.putBoolean(it, v) + } + } + return result + } }
\ No newline at end of file diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml index 8a5738e6fac2..38a781ac4ec0 100644 --- a/packages/PackageInstaller/res/values-af/strings.xml +++ b/packages/PackageInstaller/res/values-af/strings.xml @@ -76,8 +76,8 @@ <string name="uninstall_failed" msgid="1847750968168364332">"Deïnstallering onsuksesvol."</string> <string name="uninstall_failed_app" msgid="5506028705017601412">"Kon nie <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> deïnstalleer nie."</string> <string name="uninstalling_cloned_app" msgid="1826380164974984870">"Vee tans <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-kloon uit …"</string> - <string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"Kan nie aktiewe toesteladministrasieprogram deïnstalleer nie"</string> - <string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"Kan nie aktiewe toesteladministrasieprogram vir <xliff:g id="USERNAME">%1$s</xliff:g> deïnstalleer nie"</string> + <string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"Kan nie aktiewe toesteladministrasie-app deïnstalleer nie"</string> + <string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"Kan nie aktiewe toesteladministrasie-app vir <xliff:g id="USERNAME">%1$s</xliff:g> deïnstalleer nie"</string> <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"Dié program word vir sommige gebruikers of profiele vereis en is vir ander gedeïnstalleer"</string> <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"Hierdie program is nodig vir jou profiel en kan nie gedeïnstalleer word nie."</string> <string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"Jou toesteladministrateur vereis die program; kan nie gedeïnstalleer word nie."</string> diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml index 43c9b1e9e9bd..96b582bf60c4 100644 --- a/packages/PackageInstaller/res/values-el/strings.xml +++ b/packages/PackageInstaller/res/values-el/strings.xml @@ -24,11 +24,11 @@ <string name="installing" msgid="4921993079741206516">"Εγκατάσταση…"</string> <string name="installing_app" msgid="1165095864863849422">"Εγκατάσταση <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string> <string name="install_done" msgid="5987363587661783896">"Η εφαρμογή εγκαταστάθηκε."</string> - <string name="install_confirm_question" msgid="7663733664476363311">"Θέλετε να εγκαταστήσετε αυτήν την εφαρμογή;"</string> - <string name="install_confirm_question_update" msgid="3348888852318388584">"Θέλετε να ενημερώσετε αυτήν την εφαρμογή;"</string> - <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"<p>Ενημερώστε αυτήν την εφαρμογή από <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Η συγκεκριμένη εφαρμογή λαμβάνει συνήθως ενημερώσεις από <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Αν κάνετε την ενημέρωση από διαφορετική πηγή, μπορεί να λαμβάνετε μελλοντικές ενημερώσεις από οποιαδήποτε πηγή στο tablet σας. Η λειτουργικότητα της εφαρμογής μπορεί να αλλάξει.</p>"</string> - <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"<p>Ενημερώστε αυτήν την εφαρμογή από <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Η συγκεκριμένη εφαρμογή λαμβάνει συνήθως ενημερώσεις από <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Αν κάνετε την ενημέρωση από διαφορετική πηγή, μπορεί να λαμβάνετε μελλοντικές ενημερώσεις από οποιαδήποτε πηγή στην τηλεόρασή σας. Η λειτουργικότητα της εφαρμογής μπορεί να αλλάξει.</p>"</string> - <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"<p>Ενημερώστε αυτήν την εφαρμογή από <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Η συγκεκριμένη εφαρμογή λαμβάνει συνήθως ενημερώσεις από <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Αν κάνετε την ενημέρωση από διαφορετική πηγή, μπορεί να λαμβάνετε μελλοντικές ενημερώσεις από οποιαδήποτε πηγή στο τηλέφωνό σας. Η λειτουργικότητα της εφαρμογής μπορεί να αλλάξει.</p>"</string> + <string name="install_confirm_question" msgid="7663733664476363311">"Θέλετε να εγκαταστήσετε αυτή την εφαρμογή;"</string> + <string name="install_confirm_question_update" msgid="3348888852318388584">"Θέλετε να ενημερώσετε αυτή την εφαρμογή;"</string> + <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"<p>Ενημερώστε αυτή την εφαρμογή από <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Η συγκεκριμένη εφαρμογή λαμβάνει συνήθως ενημερώσεις από <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Αν κάνετε την ενημέρωση από διαφορετική πηγή, μπορεί να λαμβάνετε μελλοντικές ενημερώσεις από οποιαδήποτε πηγή στο tablet σας. Η λειτουργικότητα της εφαρμογής μπορεί να αλλάξει.</p>"</string> + <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"<p>Ενημερώστε αυτή την εφαρμογή από <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Η συγκεκριμένη εφαρμογή λαμβάνει συνήθως ενημερώσεις από <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Αν κάνετε την ενημέρωση από διαφορετική πηγή, μπορεί να λαμβάνετε μελλοντικές ενημερώσεις από οποιαδήποτε πηγή στην τηλεόρασή σας. Η λειτουργικότητα της εφαρμογής μπορεί να αλλάξει.</p>"</string> + <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"<p>Ενημερώστε αυτή την εφαρμογή από <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Η συγκεκριμένη εφαρμογή λαμβάνει συνήθως ενημερώσεις από <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Αν κάνετε την ενημέρωση από διαφορετική πηγή, μπορεί να λαμβάνετε μελλοντικές ενημερώσεις από οποιαδήποτε πηγή στο τηλέφωνό σας. Η λειτουργικότητα της εφαρμογής μπορεί να αλλάξει.</p>"</string> <string name="install_failed" msgid="5777824004474125469">"Η εφαρμογή δεν εγκαταστάθηκε."</string> <string name="install_failed_blocked" msgid="8512284352994752094">"Η εγκατάσταση του πακέτου αποκλείστηκε."</string> <string name="install_failed_conflict" msgid="3493184212162521426">"Η εφαρμογή δεν εγκαταστάθηκε, επειδή το πακέτο είναι σε διένεξη με κάποιο υπάρχον πακέτο."</string> @@ -57,15 +57,15 @@ <string name="uninstall_application_title" msgid="4045420072401428123">"Απεγκατάσταση εφαρμογής"</string> <string name="uninstall_update_title" msgid="824411791011583031">"Απεγκατάσταση ενημέρωσης"</string> <string name="uninstall_activity_text" msgid="1928194674397770771">"Η δραστηριότητα <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> αποτελεί τμήμα της ακόλουθης εφαρμογής:"</string> - <string name="uninstall_application_text" msgid="3816830743706143980">"Θέλετε να απεγκαταστήσετε αυτήν την εφαρμογή;"</string> - <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Θέλετε να απεγκαταστήσετε αυτήν την εφαρμογή για "<b>"όλους"</b>" τους χρήστες; Η εφαρμογή και τα δεδομένα της θα καταργηθούν από "<b>"όλους"</b>" τους χρήστες στη συσκευή."</string> - <string name="uninstall_application_text_user" msgid="498072714173920526">"Θέλετε να απεγκαταστήσετε αυτήν την εφαρμογή για τον χρήστη <xliff:g id="USERNAME">%1$s</xliff:g>;"</string> + <string name="uninstall_application_text" msgid="3816830743706143980">"Θέλετε να απεγκαταστήσετε αυτή την εφαρμογή;"</string> + <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Θέλετε να απεγκαταστήσετε αυτή την εφαρμογή για "<b>"όλους"</b>" τους χρήστες; Η εφαρμογή και τα δεδομένα της θα καταργηθούν από "<b>"όλους"</b>" τους χρήστες στη συσκευή."</string> + <string name="uninstall_application_text_user" msgid="498072714173920526">"Θέλετε να απεγκαταστήσετε αυτή την εφαρμογή για τον χρήστη <xliff:g id="USERNAME">%1$s</xliff:g>;"</string> <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Θέλετε να καταργήσετε την εγκατάσταση αυτής της εφαρμογής από το προφίλ εργασίας σας;"</string> <string name="uninstall_update_text" msgid="863648314632448705">"Να αντικατασταθεί αυτή η εφαρμογή με την εργοστασιακή έκδοση; Όλα τα δεδομένα θα καταργηθούν."</string> <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Να αντικατασταθεί αυτή η εφαρμογή με την εργοστασιακή έκδοση; Όλα τα δεδομένα θα καταργηθούν. Αυτό επηρεάζει όλους τους χρήστες της συσκευής, συμπεριλαμβανομένων και των κατόχων προφίλ εργασίας."</string> <string name="uninstall_keep_data" msgid="7002379587465487550">"Διατήρηση <xliff:g id="SIZE">%1$s</xliff:g> δεδομένων εφαρμογών."</string> - <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Θέλετε να διαγράψετε αυτήν την εφαρμογή;"</string> - <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Θέλετε να απεγκαταστήσετε αυτήν την εφαρμογή; Θα διαγραφεί επίσης το διπλότυπο της εφαρμογής <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> + <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Θέλετε να διαγράψετε αυτή την εφαρμογή;"</string> + <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Θέλετε να απεγκαταστήσετε αυτή την εφαρμογή; Θα διαγραφεί επίσης το διπλότυπο της εφαρμογής <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Απεγκαταστάσεις σε εξέλιξη"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Αποτυχημένες απεγκαταστάσεις"</string> <string name="uninstalling" msgid="8709566347688966845">"Απεγκατάσταση…"</string> @@ -89,10 +89,10 @@ <string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"Οι ενέργειες εγκατάστασης/απεγκατάστασης δεν υποστηρίζονται στο Wear."</string> <string name="message_staging" msgid="8032722385658438567">"Σταδιακή διάθεση εφαρμογής…"</string> <string name="app_name_unknown" msgid="6881210203354323926">"Άγνωστη"</string> - <string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"Για λόγους ασφαλείας, δεν επιτρέπεται προς το παρόν η εγκατάσταση άγνωστων εφαρμογών από αυτήν την πηγή στο tablet σας. Μπορείτε να αλλάξετε την επιλογή από τις Ρυθμίσεις."</string> - <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"Για λόγους ασφαλείας, δεν επιτρέπεται προς το παρόν η εγκατάσταση άγνωστων εφαρμογών από αυτήν την πηγή στην τηλεόρασή σας. Μπορείτε να αλλάξετε την επιλογή από τις Ρυθμίσεις."</string> - <string name="untrusted_external_source_warning" product="watch" msgid="7195163388090818636">"Για λόγους ασφαλείας, δεν επιτρέπεται προς το παρόν η εγκατάσταση άγνωστων εφαρμογών από αυτήν την πηγή στο ρολόι σας. Αυτό μπορείτε να το αλλάξετε από την ενότητα Ρυθμίσεις."</string> - <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"Για λόγους ασφαλείας, δεν επιτρέπεται προς το παρόν η εγκατάσταση άγνωστων εφαρμογών από αυτήν την πηγή στο τηλέφωνό σας. Μπορείτε να αλλάξετε την επιλογή από τις Ρυθμίσεις."</string> + <string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"Για λόγους ασφαλείας, δεν επιτρέπεται προς το παρόν η εγκατάσταση άγνωστων εφαρμογών από αυτή την πηγή στο tablet σας. Μπορείτε να αλλάξετε την επιλογή από τις Ρυθμίσεις."</string> + <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"Για λόγους ασφαλείας, δεν επιτρέπεται προς το παρόν η εγκατάσταση άγνωστων εφαρμογών από αυτή την πηγή στην τηλεόρασή σας. Μπορείτε να αλλάξετε την επιλογή από τις Ρυθμίσεις."</string> + <string name="untrusted_external_source_warning" product="watch" msgid="7195163388090818636">"Για λόγους ασφαλείας, δεν επιτρέπεται προς το παρόν η εγκατάσταση άγνωστων εφαρμογών από αυτή την πηγή στο ρολόι σας. Αυτό μπορείτε να το αλλάξετε από την ενότητα Ρυθμίσεις."</string> + <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"Για λόγους ασφαλείας, δεν επιτρέπεται προς το παρόν η εγκατάσταση άγνωστων εφαρμογών από αυτή την πηγή στο τηλέφωνό σας. Μπορείτε να αλλάξετε την επιλογή από τις Ρυθμίσεις."</string> <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Το τηλέφωνό σας και τα προσωπικά δεδομένα σας είναι πιο ευάλωτα σε επιθέσεις από άγνωστες εφαρμογές. Με την εγκατάσταση αυτής της εφαρμογής, συμφωνείτε ότι είστε υπεύθυνοι για τυχόν βλάβη που μπορεί να προκληθεί στο τηλέφωνο ή απώλεια δεδομένων που μπορεί να προκύψει από τη χρήση τους."</string> <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Το tablet σας και τα προσωπικά δεδομένα σας είναι πιο ευάλωτα σε επιθέσεις από άγνωστες εφαρμογές. Με την εγκατάσταση αυτής της εφαρμογής, συμφωνείτε ότι είστε υπεύθυνοι για τυχόν βλάβη που μπορεί να προκληθεί στο tablet ή απώλεια δεδομένων που μπορεί να προκύψει από τη χρήση τους."</string> <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Η τηλεόρασή σας και τα προσωπικά δεδομένα σας είναι πιο ευάλωτα σε επιθέσεις από άγνωστες εφαρμογές. Με την εγκατάσταση αυτής της εφαρμογής, συμφωνείτε ότι είστε υπεύθυνοι για τυχόν βλάβη που μπορεί να προκληθεί στην τηλεόρασή ή απώλεια δεδομένων που μπορεί να προκύψει από τη χρήση τους."</string> diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 5dcb9d2f6e77..2d231f2f55da 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -71,7 +71,6 @@ android_library { "SettingsLibMainSwitchPreference", "SettingsLibProfileSelector", "SettingsLibProgressBar", - "SettingsLibRadioButtonPreference", "SettingsLibRestrictedLockUtils", "SettingsLibSelectorWithWidgetPreference", "SettingsLibSettingsSpinner", diff --git a/packages/SettingsLib/AppPreference/res/values-nl/strings.xml b/packages/SettingsLib/AppPreference/res/values-nl/strings.xml index c648449ed933..595fea362cb9 100644 --- a/packages/SettingsLib/AppPreference/res/values-nl/strings.xml +++ b/packages/SettingsLib/AppPreference/res/values-nl/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="install_type_instant" msgid="7217305006127216917">"Instant-app"</string> + <string name="install_type_instant" msgid="7217305006127216917">"Instant app"</string> </resources> diff --git a/packages/SettingsLib/RadioButtonPreference/Android.bp b/packages/SettingsLib/RadioButtonPreference/Android.bp deleted file mode 100644 index 505ba05c5f59..000000000000 --- a/packages/SettingsLib/RadioButtonPreference/Android.bp +++ /dev/null @@ -1,29 +0,0 @@ -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_library { - name: "SettingsLibRadioButtonPreference", - use_resource_processor: true, - - srcs: ["src/**/*.java"], - resource_dirs: ["res"], - - static_libs: [ - "androidx.preference_preference", - "SettingsLibSelectorWithWidgetPreference", - "SettingsLibSettingsTheme", - ], - - sdk_version: "system_current", - min_sdk_version: "21", - apex_available: [ - "//apex_available:platform", - "com.android.permission", - ], -} diff --git a/packages/SettingsLib/RadioButtonPreference/AndroidManifest.xml b/packages/SettingsLib/RadioButtonPreference/AndroidManifest.xml deleted file mode 100644 index 8b5c3b1b5652..000000000000 --- a/packages/SettingsLib/RadioButtonPreference/AndroidManifest.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2019 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.settingslib.widget.preference.radio"> - - <uses-sdk android:minSdkVersion="21" /> - -</manifest> diff --git a/packages/SettingsLib/RadioButtonPreference/res/drawable/ic_settings_accent.xml b/packages/SettingsLib/RadioButtonPreference/res/drawable/ic_settings_accent.xml deleted file mode 100644 index 6521bc9e0fb9..000000000000 --- a/packages/SettingsLib/RadioButtonPreference/res/drawable/ic_settings_accent.xml +++ /dev/null @@ -1,29 +0,0 @@ -<!-- - Copyright (C) 2021 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?android:attr/colorAccent"> - <path - android:fillColor="#FFFFFFFF" - android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46c0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19C1.98,9.9 1.83,9.09 2.2,8.47l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91C15.21,21.71 14.59,22.25 13.85,22.25zM13.32,20.72c0,0.01 0,0.01 0,0.02L13.32,20.72zM10.68,20.7l0,0.02C10.69,20.72 10.69,20.71 10.68,20.7zM10.62,20.25h2.76l0.37,-2.55l0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34l2.38,0.96l1.38,-2.4l-2.03,-1.58l0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78c0,-0.27 -0.03,-0.53 -0.06,-0.78l-0.07,-0.56l2.03,-1.58l-1.39,-2.4l-2.39,0.96l-0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77L13.75,6.3l-0.37,-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.7 8.84,6.95 8.38,7.3L7.93,7.63L5.55,6.68L4.16,9.07l2.03,1.58l-0.07,0.56C6.09,11.47 6.06,11.74 6.06,12c0,0.26 0.02,0.53 0.06,0.78l0.07,0.56l-2.03,1.58l1.38,2.4l2.39,-0.96l0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22L10.62,20.25zM18.22,17.72c0,0.01 -0.01,0.02 -0.01,0.03L18.22,17.72zM5.77,17.71l0.01,0.02C5.78,17.72 5.77,17.71 5.77,17.71zM3.93,9.47L3.93,9.47C3.93,9.47 3.93,9.47 3.93,9.47zM18.22,6.27c0,0.01 0.01,0.02 0.01,0.02L18.22,6.27zM5.79,6.25L5.78,6.27C5.78,6.27 5.79,6.26 5.79,6.25zM13.31,3.28c0,0.01 0,0.01 0,0.02L13.31,3.28zM10.69,3.26l0,0.02C10.69,3.27 10.69,3.27 10.69,3.26z"/> - <path - android:fillColor="#FFFFFFFF" - android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/> -</vector>
\ No newline at end of file diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml deleted file mode 100644 index bb8ac286e38d..000000000000 --- a/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml +++ /dev/null @@ -1,127 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. - --> - -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:settings="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="?android:attr/selectableItemBackground" - android:gravity="center_vertical" - android:minHeight="?android:attr/listPreferredItemHeightSmall" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> - - <LinearLayout - android:id="@android:id/widget_frame" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:paddingHorizontal="20dp" - android:gravity="center" - android:minWidth="56dp" - android:orientation="vertical"/> - - <LinearLayout - android:id="@+id/icon_frame" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:minWidth="32dp" - android:orientation="horizontal" - android:layout_marginEnd="16dp" - android:paddingTop="4dp" - android:paddingBottom="4dp"> - <androidx.preference.internal.PreferenceImageView - android:id="@android:id/icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - settings:maxWidth="@dimen/secondary_app_icon_size" - settings:maxHeight="@dimen/secondary_app_icon_size"/> - </LinearLayout> - - <LinearLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:orientation="vertical" - android:paddingTop="16dp" - android:paddingBottom="16dp" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> - - <TextView - android:id="@android:id/title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxLines="2" - android:hyphenationFrequency="normalFast" - android:lineBreakWordStyle="phrase" - android:textAppearance="?android:attr/textAppearanceListItem"/> - - <LinearLayout - android:id="@+id/summary_container" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:visibility="gone"> - <TextView - android:id="@android:id/summary" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textAlignment="viewStart" - android:hyphenationFrequency="normalFast" - android:lineBreakWordStyle="phrase" - android:textColor="?android:attr/textColorSecondary"/> - - <TextView - android:id="@+id/appendix" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textAlignment="viewEnd" - android:textColor="?android:attr/textColorSecondary" - android:maxLines="1" - android:visibility="gone" - android:ellipsize="end"/> - </LinearLayout> - </LinearLayout> - - <LinearLayout - android:id="@+id/radio_extra_widget_container" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:orientation="horizontal" - android:gravity="center_vertical"> - <View - android:layout_width=".75dp" - android:layout_height="32dp" - android:layout_marginTop="16dp" - android:layout_marginBottom="16dp" - android:background="?android:attr/dividerVertical" /> - <ImageView - android:id="@+id/radio_extra_widget" - android:layout_width="match_parent" - android:minWidth="@dimen/two_target_min_width" - android:layout_height="fill_parent" - android:src="@drawable/ic_settings_accent" - android:contentDescription="@string/settings_label" - android:paddingStart="24dp" - android:paddingEnd="24dp" - android:layout_gravity="center" - android:background="?android:attr/selectableItemBackground" /> - </LinearLayout> -</LinearLayout> diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml deleted file mode 100644 index 906ff2cc09d5..000000000000 --- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml +++ /dev/null @@ -1,123 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2019 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. - --> - -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:settings="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="?android:attr/selectableItemBackground" - android:gravity="center_vertical" - android:minHeight="?android:attr/listPreferredItemHeightSmall" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> - - <LinearLayout - android:id="@android:id/widget_frame" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:paddingHorizontal="20dp" - android:gravity="center" - android:minWidth="56dp" - android:orientation="vertical"/> - - <LinearLayout - android:id="@+id/icon_frame" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:minWidth="32dp" - android:orientation="horizontal" - android:layout_marginEnd="16dp" - android:paddingTop="4dp" - android:paddingBottom="4dp"> - <androidx.preference.internal.PreferenceImageView - android:id="@android:id/icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - settings:maxWidth="@dimen/secondary_app_icon_size" - settings:maxHeight="@dimen/secondary_app_icon_size"/> - </LinearLayout> - - <LinearLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:orientation="vertical" - android:paddingTop="16dp" - android:paddingBottom="16dp" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> - - <TextView - android:id="@android:id/title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxLines="2" - android:textAppearance="?android:attr/textAppearanceListItem"/> - - <LinearLayout - android:id="@+id/summary_container" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:visibility="gone"> - <TextView - android:id="@android:id/summary" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textAlignment="viewStart" - android:textColor="?android:attr/textColorSecondary"/> - - <TextView - android:id="@+id/appendix" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textAlignment="viewEnd" - android:textColor="?android:attr/textColorSecondary" - android:maxLines="1" - android:visibility="gone" - android:ellipsize="end"/> - </LinearLayout> - </LinearLayout> - - <LinearLayout - android:id="@+id/radio_extra_widget_container" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:orientation="horizontal" - android:gravity="center_vertical"> - <View - android:layout_width=".75dp" - android:layout_height="32dp" - android:layout_marginTop="16dp" - android:layout_marginBottom="16dp" - android:background="?android:attr/dividerVertical" /> - <ImageView - android:id="@+id/radio_extra_widget" - android:layout_width="match_parent" - android:minWidth="@dimen/two_target_min_width" - android:layout_height="fill_parent" - android:src="@drawable/ic_settings_accent" - android:contentDescription="@string/settings_label" - android:paddingStart="24dp" - android:paddingEnd="24dp" - android:layout_gravity="center" - android:background="?android:attr/selectableItemBackground" /> - </LinearLayout> -</LinearLayout> diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_widget_radiobutton.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_widget_radiobutton.xml deleted file mode 100644 index cb7b8eb289ea..000000000000 --- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_widget_radiobutton.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2019 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. - --> - -<RadioButton xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@android:id/checkbox" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:background="@null" - android:focusable="false" - android:clickable="false" /> diff --git a/packages/SettingsLib/RadioButtonPreference/res/values/strings.xml b/packages/SettingsLib/RadioButtonPreference/res/values/strings.xml deleted file mode 100644 index ff3f90cfffd3..000000000000 --- a/packages/SettingsLib/RadioButtonPreference/res/values/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2021 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - - <!-- Content description for RadioButton with extra gear icon [CHAR LIMIT=NONE] --> - <string name="settings_label">Settings</string> - -</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java b/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java deleted file mode 100644 index fc4b71430c5f..000000000000 --- a/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2019 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.settingslib.widget; - -import android.content.Context; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.view.View; -import android.widget.ImageView; - -import androidx.preference.CheckBoxPreference; -import androidx.preference.PreferenceViewHolder; - -import com.android.settingslib.widget.preference.radio.R; - -/** - * DEPRECATED. Please use SelectorWithWidgetPreference instead. - * - * This file has been moved there and will be removed once all callers are updated. - * - * Check box preference with check box replaced by radio button. - * - * Functionally speaking, it's actually a CheckBoxPreference. We only modified - * the widget to RadioButton to make it "look like" a RadioButtonPreference. - * - * In other words, there's no "RadioButtonPreferenceGroup" in this - * implementation. When you check one RadioButtonPreference, if you want to - * uncheck all the other preferences, you should do that by code yourself. - * - * RadioButtonPreference can assign a extraWidgetListener to show a gear icon - * on the right side that can open another page. - * - * @Deprecated - */ -public class RadioButtonPreference extends CheckBoxPreference { - - /** - * Interface definition for a callback to be invoked when the preference is clicked. - */ - public interface OnClickListener { - /** - * Called when a preference has been clicked. - * - * @param emiter The clicked preference - */ - void onRadioButtonClicked(RadioButtonPreference emiter); - } - - private OnClickListener mListener = null; - private View mAppendix; - private int mAppendixVisibility = -1; - - private View mExtraWidgetContainer; - private ImageView mExtraWidget; - - private View.OnClickListener mExtraWidgetOnClickListener; - - /** - * Perform inflation from XML and apply a class-specific base style. - * - * @param context The {@link Context} this is associated with, through which it can - * access the current theme, resources, {@link SharedPreferences}, etc. - * @param attrs The attributes of the XML tag that is inflating the preference - * @param defStyle An attribute in the current theme that contains a reference to a style - * resource that supplies default values for the view. Can be 0 to not - * look for defaults. - */ - public RadioButtonPreference(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - /** - * Perform inflation from XML and apply a class-specific base style. - * - * @param context The {@link Context} this is associated with, through which it can - * access the current theme, resources, {@link SharedPreferences}, etc. - * @param attrs The attributes of the XML tag that is inflating the preference - */ - public RadioButtonPreference(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** - * Constructor to create a preference. - * - * @param context The Context this is associated with. - */ - public RadioButtonPreference(Context context) { - this(context, null); - } - - /** - * Sets the callback to be invoked when this preference is clicked by the user. - * - * @param listener The callback to be invoked - */ - public void setOnClickListener(OnClickListener listener) { - mListener = listener; - } - - /** - * Processes a click on the preference. - */ - @Override - public void onClick() { - if (mListener != null) { - mListener.onRadioButtonClicked(this); - } - } - - /** - * Binds the created View to the data for this preference. - * - * <p>This is a good place to grab references to custom Views in the layout and set - * properties on them. - * - * <p>Make sure to call through to the superclass's implementation. - * - * @param holder The ViewHolder that provides references to the views to fill in. These views - * will be recycled, so you should not hold a reference to them after this method - * returns. - */ - @Override - public void onBindViewHolder(PreferenceViewHolder holder) { - super.onBindViewHolder(holder); - - View summaryContainer = holder.findViewById(R.id.summary_container); - if (summaryContainer != null) { - summaryContainer.setVisibility( - TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE); - mAppendix = holder.findViewById(R.id.appendix); - if (mAppendix != null && mAppendixVisibility != -1) { - mAppendix.setVisibility(mAppendixVisibility); - } - } - - mExtraWidget = (ImageView) holder.findViewById(R.id.radio_extra_widget); - mExtraWidgetContainer = holder.findViewById(R.id.radio_extra_widget_container); - - setExtraWidgetOnClickListener(mExtraWidgetOnClickListener); - } - - /** - * Set the visibility state of appendix view. - * - * @param visibility One of {@link View#VISIBLE}, {@link View#INVISIBLE}, or {@link View#GONE}. - */ - public void setAppendixVisibility(int visibility) { - if (mAppendix != null) { - mAppendix.setVisibility(visibility); - } - mAppendixVisibility = visibility; - } - - /** - * Sets the callback to be invoked when extra widget is clicked by the user. - * - * @param listener The callback to be invoked - */ - public void setExtraWidgetOnClickListener(View.OnClickListener listener) { - mExtraWidgetOnClickListener = listener; - - if (mExtraWidget == null || mExtraWidgetContainer == null) { - return; - } - - mExtraWidget.setOnClickListener(mExtraWidgetOnClickListener); - - mExtraWidgetContainer.setVisibility((mExtraWidgetOnClickListener != null) - ? View.VISIBLE : View.GONE); - } - - private void init() { - setWidgetLayoutResource(R.layout.preference_widget_radiobutton); - setLayoutResource(R.layout.preference_radio); - setIconSpaceReserved(false); - } -} diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java index 1a938d6ec37e..a4089c0d8697 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java @@ -22,6 +22,7 @@ import static com.android.settingslib.drawer.TileUtils.META_DATA_NEW_TASK; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT; +import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SEARCHABLE; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI; @@ -424,6 +425,13 @@ public abstract class Tile implements Parcelable { } /** + * Returns if this is searchable. + */ + public boolean isSearchable() { + return mMetaData == null || mMetaData.getBoolean(META_DATA_PREFERENCE_SEARCHABLE, true); + } + + /** * The type of the tile. */ public enum Type { diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java index 33907ece5a7c..d0929e19bc9b 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java @@ -250,6 +250,11 @@ public class TileUtils { public static final String META_DATA_NEW_TASK = "com.android.settings.new_task"; /** + * If the entry should be shown in settings search results. Defaults to true. + */ + public static final String META_DATA_PREFERENCE_SEARCHABLE = "com.android.settings.searchable"; + + /** * Build a list of DashboardCategory. */ public static List<DashboardCategory> getCategories(Context context, diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index ed15e7c7be9c..3d89679299c3 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Nie geregistreer nie"</string> <string name="status_unavailable" msgid="5279036186589861608">"Onbeskikbaar"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC word ewekansig gemaak"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 toestelle is gekoppel}=1{1 toestel is gekoppel}other{# toestelle is gekoppel}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Meer tyd."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Minder tyd."</string> <string name="cancel" msgid="5665114069455378395">"Kanselleer"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 4831a2734f6b..7f3aa6f3af36 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"አልተመዘገበም"</string> <string name="status_unavailable" msgid="5279036186589861608">"አይገኝም"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"ማክ በዘፈቀደ ይሰራል"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 መሣሪያ ተገናኝቷል}=1{1 መሣሪያ ተገናኝቷል}one{# መሣሪያዎች ተገናኝተዋል}other{# መሣሪያዎች ተገናኝተዋል}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"ተጨማሪ ጊዜ።"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"ያነሰ ጊዜ።"</string> <string name="cancel" msgid="5665114069455378395">"ይቅር"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 53fbdb1d9884..6a6dddd62ca4 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"غير مُسجَّل"</string> <string name="status_unavailable" msgid="5279036186589861608">"غير متاح"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"يتم اختيار عنوان MAC بشكل انتقائي."</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{لم يتم اتصال أي أجهزة.}=1{تم اتصال جهاز واحد.}two{تم اتصال جهازين.}few{تم اتصال # أجهزة.}many{تم اتصال # جهازًا.}other{تم اتصال # جهاز.}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"وقت أكثر."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"وقت أقل."</string> <string name="cancel" msgid="5665114069455378395">"إلغاء"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 20edf938a937..6d6800e0ab0a 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"পঞ্জীকৃত নহয়"</string> <string name="status_unavailable" msgid="5279036186589861608">"উপলব্ধ নহয়"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC ক্ৰমানুসৰি ছেট কৰা হোৱা নাই"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{০ টা ডিভাইচ সংযোগ কৰা হ’ল}=1{১ টা ডিভাইচ সংযোগ কৰা হ’ল}one{# টা ডিভাইচ সংযোগ কৰা হ’ল}other{# টা ডিভাইচ সংযোগ কৰা হ’ল}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"অধিক সময়।"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"কম সময়।"</string> <string name="cancel" msgid="5665114069455378395">"বাতিল কৰক"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index d7ca008b8abe..45420b7586ee 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Qeydiyyatsız"</string> <string name="status_unavailable" msgid="5279036186589861608">"Əlçatmazdır"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC ixtiyari olaraq seçildi"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 cihaz qoşulub}=1{1 cihaz qoşulub}other{# cihaz qoşulub}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Daha çox vaxt."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Daha az vaxt."</string> <string name="cancel" msgid="5665114069455378395">"Ləğv edin"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index a10f109ab418..bd644950f3bc 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Nije registrovan"</string> <string name="status_unavailable" msgid="5279036186589861608">"Nedostupno"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC adresa je nasumično izabrana"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 uređaja je povezano}=1{1 uređaj je povezan}one{# uređaj je povezan}few{# uređaja su povezana}other{# uređaja je povezano}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Više vremena."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Manje vremena."</string> <string name="cancel" msgid="5665114069455378395">"Otkaži"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 66ce12d5948c..4ed606c11e94 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Не зарэгістраваны"</string> <string name="status_unavailable" msgid="5279036186589861608">"Адсутнічае"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Выпадковы MAC-адрас"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Падключана 0 прылад}=1{Падключана 1 прылада}one{Падключана # прылада}few{Падключаны # прылады}many{Падключаны # прылад}other{Падключаны # прылады}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Больш часу."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Менш часу."</string> <string name="cancel" msgid="5665114069455378395">"Скасаваць"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 5166d876ec11..bc612479e769 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Не е регистрирано"</string> <string name="status_unavailable" msgid="5279036186589861608">"Няма данни"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC адресът е рандомизиран"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Няма свързани устройства}=1{1 устройството е свързано}other{# устройства са свързани}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Повече време."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"По-малко време."</string> <string name="cancel" msgid="5665114069455378395">"Отказ"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 7f285f6e76d2..52acfd633635 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"রেজিস্টার করা নয়"</string> <string name="status_unavailable" msgid="5279036186589861608">"অনুপলভ্য"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC র্যান্ডমাইজ করা হয়েছে"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{০টি ডিভাইস কানেক্ট করা হয়েছে}=1{১টি ডিভাইস কানেক্ট করা হয়েছে}one{#টি ডিভাইস কানেক্ট করা হয়েছে}other{#টি ডিভাইস কানেক্ট করা হয়েছে}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"আরও বেশি।"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"আরও কম।"</string> <string name="cancel" msgid="5665114069455378395">"বাতিল"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index d7daed5cf936..14d853ce0959 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Nije registrirano"</string> <string name="status_unavailable" msgid="5279036186589861608">"Nije dostupno"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC adresa je nasumično odabrana"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Povezano je 0 uređaja}=1{Povezan je 1 uređaj}one{Povezan je # uređaj}few{Povezana su # uređaja}other{Povezano je # uređaja}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Više vremena."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Manje vremena."</string> <string name="cancel" msgid="5665114069455378395">"Otkaži"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 89caaf94bdf7..c41b839bcb43 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Sense registrar"</string> <string name="status_unavailable" msgid="5279036186589861608">"No disponible"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"L\'adreça MAC és aleatòria"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Cap dispositiu connectat}=1{1 dispositiu connectat}other{# dispositius connectats}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Més temps"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Menys temps"</string> <string name="cancel" msgid="5665114069455378395">"Cancel·la"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index c4b49672372f..5724677a2bad 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Neregistrováno"</string> <string name="status_unavailable" msgid="5279036186589861608">"Není k dispozici"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Adresa MAC je vybrána náhodně"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 připojených zařízení}=1{1 připojené zařízení}few{# připojená zařízení}many{# připojeného zařízení}other{# připojených zařízení}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Delší doba"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Kratší doba"</string> <string name="cancel" msgid="5665114069455378395">"Zrušit"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 94852b4c983e..80c10880305a 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Ikke registreret"</string> <string name="status_unavailable" msgid="5279036186589861608">"Utilgængelig"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC-adressen er tilfældig"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 enheder er forbundet}=1{1 enhed er forbundet}one{# enhed er forbundet}other{# enheder er forbundet}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Mere tid."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Mindre tid."</string> <string name="cancel" msgid="5665114069455378395">"Annuller"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 9d9a82a53d5b..cf77e6ce5266 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Nicht registriert"</string> <string name="status_unavailable" msgid="5279036186589861608">"Nicht verfügbar"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC-Adresse wird zufällig festgelegt"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 Geräte verbunden}=1{1 Gerät verbunden}other{# Geräte verbunden}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Mehr Zeit."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Weniger Zeit."</string> <string name="cancel" msgid="5665114069455378395">"Abbrechen"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index d65f2562a445..d649786091c7 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Μη εγγεγραμμένη"</string> <string name="status_unavailable" msgid="5279036186589861608">"Μη διαθέσιμο"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Η διεύθυνση MAC είναι τυχαία"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 συνδεδεμένες συσκευές}=1{1 συνδεδεμένη συσκευή}other{# συνδεδεμένες συσκευές}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Περισσότερη ώρα."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Λιγότερη ώρα."</string> <string name="cancel" msgid="5665114069455378395">"Ακύρωση"</string> @@ -529,7 +530,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-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index e35bc1931691..5853d6fc33c6 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Not registered"</string> <string name="status_unavailable" msgid="5279036186589861608">"Unavailable"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC is randomised"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 device connected}=1{1 device connected}other{# devices connected}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"More time."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Less time."</string> <string name="cancel" msgid="5665114069455378395">"Cancel"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 08bcb77bfe68..c9037999e914 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Not registered"</string> <string name="status_unavailable" msgid="5279036186589861608">"Unavailable"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC is randomized"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 device connected}=1{1 device connected}other{# devices connected}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"More time."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Less time."</string> <string name="cancel" msgid="5665114069455378395">"Cancel"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index e35bc1931691..5853d6fc33c6 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Not registered"</string> <string name="status_unavailable" msgid="5279036186589861608">"Unavailable"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC is randomised"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 device connected}=1{1 device connected}other{# devices connected}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"More time."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Less time."</string> <string name="cancel" msgid="5665114069455378395">"Cancel"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index e35bc1931691..5853d6fc33c6 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Not registered"</string> <string name="status_unavailable" msgid="5279036186589861608">"Unavailable"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC is randomised"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 device connected}=1{1 device connected}other{# devices connected}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"More time."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Less time."</string> <string name="cancel" msgid="5665114069455378395">"Cancel"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index dba88d0fb167..1da7f124c12f 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Not registered"</string> <string name="status_unavailable" msgid="5279036186589861608">"Unavailable"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC is randomized"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 device connected}=1{1 device connected}other{# devices connected}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"More time."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Less time."</string> <string name="cancel" msgid="5665114069455378395">"Cancel"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 79d3822b2e19..40145c96c426 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Sin registrar"</string> <string name="status_unavailable" msgid="5279036186589861608">"No disponible"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"La dirección MAC es aleatoria"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Se conectaron 0 dispositivos}=1{Se conectó 1 dispositivo}other{Se conectaron # dispositivos}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Más tiempo"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Menos tiempo"</string> <string name="cancel" msgid="5665114069455378395">"Cancelar"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index af03e18143c9..15734d39f689 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"No registrado"</string> <string name="status_unavailable" msgid="5279036186589861608">"No disponible"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"La dirección MAC es aleatoria"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Ningún dispositivo conectado}=1{1 dispositivo conectado}other{# dispositivos conectados}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Más tiempo."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Menos tiempo."</string> <string name="cancel" msgid="5665114069455378395">"Cancelar"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 56e50f982a2a..2879f3aae142 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Ei ole registreeritud"</string> <string name="status_unavailable" msgid="5279036186589861608">"Pole saadaval"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC-aadress on juhuslikuks muudetud"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Ühendatud on 0 seadet}=1{Ühendatud on 1 seade}other{Ühendatud on # seadet}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Pikem aeg."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Lühem aeg."</string> <string name="cancel" msgid="5665114069455378395">"Tühista"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 41a365186108..d6bedc85c778 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Erregistratu gabe"</string> <string name="status_unavailable" msgid="5279036186589861608">"Ez dago erabilgarri"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Ausaz aukeratutako MAC helbidea"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 gailu daude konektatuta}=1{1 gailu dago konektatuta}other{# gailu daude konektatuta}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Denbora gehiago."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Denbora gutxiago."</string> <string name="cancel" msgid="5665114069455378395">"Utzi"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 838237863d7c..05620af219fc 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"ثبت نشده است"</string> <string name="status_unavailable" msgid="5279036186589861608">"در دسترس نیست"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"ویژگی MAC تصادفی است"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{هیچ دستگاهی متصل نیست}=1{یک دستگاه متصل است}one{# دستگاه متصل است}other{# دستگاه متصل است}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"زمان بیشتر."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"زمان کمتر."</string> <string name="cancel" msgid="5665114069455378395">"لغو"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 20ed75eb9dfa..efbe8a85a71c 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Ei rekisteröity"</string> <string name="status_unavailable" msgid="5279036186589861608">"Ei käytettävissä"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC-osoite satunnaistetaan"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 yhdistettyä laitetta}=1{1 yhdistetty laite}other{# yhdistettyä laitetta}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Enemmän aikaa"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Vähemmän aikaa"</string> <string name="cancel" msgid="5665114069455378395">"Peru"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 3fdf3042a1dc..34e870427b56 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Non enregistré"</string> <string name="status_unavailable" msgid="5279036186589861608">"Non disponible"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Les adresses MAC sont randomisées"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Aucun appareil connecté}=1{1 appareil connecté}one{# appareil connecté}other{# appareils connectés}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Plus longtemps."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Moins longtemps."</string> <string name="cancel" msgid="5665114069455378395">"Annuler"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 7e99fa7ac12e..0fea3e8338d8 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Non enregistré"</string> <string name="status_unavailable" msgid="5279036186589861608">"Non disponible"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"La sélection des adresses MAC est aléatoire"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 appareil connecté}=1{1 appareil connecté}one{# appareil connecté}other{# appareils connectés}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Plus longtemps."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Moins longtemps."</string> <string name="cancel" msgid="5665114069455378395">"Annuler"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 8132a87f39f4..19598fabefe0 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Non rexistrado"</string> <string name="status_unavailable" msgid="5279036186589861608">"Non dispoñible"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"O enderezo MAC é aleatorio"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 dispositivos conectados}=1{1 dispositivo conectado}other{# dispositivos conectados}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Máis tempo."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Menos tempo."</string> <string name="cancel" msgid="5665114069455378395">"Cancelar"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 239b04d3e944..9b397876dbe6 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"રજિસ્ટર કરેલ નથી"</string> <string name="status_unavailable" msgid="5279036186589861608">"અનુપલબ્ધ"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MACને રેન્ડમ કરેલ છે"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{કોઈ ડિવાઇસ કનેક્ટેડ નથી}=1{1 ડિવાઇસ કનેક્ટેડ છે}one{# ડિવાઇસ કનેક્ટેડ છે}other{# ડિવાઇસ કનેક્ટેડ છે}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"વધુ સમય."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"ઓછો સમય."</string> <string name="cancel" msgid="5665114069455378395">"રદ કરો"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 06ddcd9f4c72..520fcb4c2099 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -456,7 +456,7 @@ <string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"आपके इस्तेमाल के हिसाब से बैटरी करीब <xliff:g id="TIME">%1$s</xliff:g> तक चलेगी"</string> <string name="power_discharge_by" msgid="4113180890060388350">"बैटरी करीब <xliff:g id="TIME">%1$s</xliff:g> चलेगी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"बैटरी करीब <xliff:g id="TIME">%1$s</xliff:g> तक चलेगी"</string> - <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> तक"</string> + <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> तक चलेगी"</string> <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"बैटरी <xliff:g id="TIME">%1$s</xliff:g> तक खत्म हो जाएगी"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम बैटरी बची है"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम बैटरी बची है (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"रजिस्टर नहीं है"</string> <string name="status_unavailable" msgid="5279036186589861608">"मौजूद नहीं है"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"एमएसी पता रैंडम पर सेट है"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 डिवाइस कनेक्ट किया गया है}=1{1 डिवाइस कनेक्ट किया गया है}one{# डिवाइस कनेक्ट किया गया है}other{# डिवाइस कनेक्ट किए गए हैं}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"ज़्यादा समय."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"कम समय."</string> <string name="cancel" msgid="5665114069455378395">"रद्द करें"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index c5bea2761ed6..f787d47773dc 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Nije registrirano"</string> <string name="status_unavailable" msgid="5279036186589861608">"Nije dostupno"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC adresa određena je nasumično"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Povezano je 0 uređaja}=1{Povezan je jedan uređaj}one{Povezan je # uređaj}few{Povezana su # uređaja}other{Povezano je # uređaja}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Više vremena."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Manje vremena."</string> <string name="cancel" msgid="5665114069455378395">"Odustani"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index e43ed439a8d3..0dde2f834f98 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Nem regisztrált"</string> <string name="status_unavailable" msgid="5279036186589861608">"Nem érhető el"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"A MAC-cím generálása véletlenszerű."</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 csatlakoztatott eszköz}=1{1 csatlakoztatott eszköz}other{# csatlakoztatott eszköz}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Több idő."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Kevesebb idő."</string> <string name="cancel" msgid="5665114069455378395">"Mégse"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index b8f94f48285b..1de8bf0970d0 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Գրանցված չէ"</string> <string name="status_unavailable" msgid="5279036186589861608">"Անհասանելի"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC հասցեն պատահականորեն է փոխվում"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Թեժ կետին միացված սարքեր չկան}=1{Թեժ կետին 1 սարք է միացված}one{Թեժ կետին # սարք է միացված}other{Թեժ կետին # սարք է միացված}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Ավելացնել ժամանակը:"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Պակասեցնել ժամանակը:"</string> <string name="cancel" msgid="5665114069455378395">"Չեղարկել"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index f867ba58dcdc..ccca25a0369b 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Tidak terdaftar"</string> <string name="status_unavailable" msgid="5279036186589861608">"Tidak tersedia"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC diacak"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 perangkat terhubung}=1{1 perangkat terhubung}other{# perangkat terhubung}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Lebih lama."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Lebih cepat."</string> <string name="cancel" msgid="5665114069455378395">"Batal"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index aa9d8f82f43a..e671377a291d 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Ekki skráð"</string> <string name="status_unavailable" msgid="5279036186589861608">"Ekki tiltækt"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC-vistfang er valið af handahófi"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 tæki tengd}=1{1 tæki tengt}one{# tæki tengt}other{# tæki tengd}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Meiri tími."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Minni tími."</string> <string name="cancel" msgid="5665114069455378395">"Hætta við"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 1f35f74a9c41..fb753d017358 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Non registrato"</string> <string name="status_unavailable" msgid="5279036186589861608">"Non disponibile"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Selezione casuale dell\'indirizzo MAC"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 dispositivi connessi}=1{1 dispositivo connesso}other{# dispositivi connessi}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Più tempo."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Meno tempo."</string> <string name="cancel" msgid="5665114069455378395">"Annulla"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index f0a19d414f3e..25a46537706e 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"לא רשום"</string> <string name="status_unavailable" msgid="5279036186589861608">"לא זמין"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"כתובת ה-MAC אקראית"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{אין מכשירים מחוברים}=1{מכשיר אחד מחובר}one{# מכשירים מחוברים}two{# מכשירים מחוברים}other{# מכשירים מחוברים}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"יותר זמן."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"פחות זמן."</string> <string name="cancel" msgid="5665114069455378395">"ביטול"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 36c11cd8c431..b2bc21af8065 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"未登録"</string> <string name="status_unavailable" msgid="5279036186589861608">"不明"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC はランダムに設定されます"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{接続されているデバイスはありません}=1{1 台のデバイスが接続されています}other{# 台のデバイスが接続されています}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"長くします。"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"短くします。"</string> <string name="cancel" msgid="5665114069455378395">"キャンセル"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index f516cf29d222..67c0228f651e 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"არარეგისტრირებული"</string> <string name="status_unavailable" msgid="5279036186589861608">"მიუწვდომელია"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC-ის მიმდევრობა არეულია"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{დაკავშირებულია 0 მოწყობილობა}=1{დაკავშირებულია 1 მოწყობილობა}other{დაკავშირებულია # მოწყობილობა}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"მეტი დრო."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"ნაკლები დრო."</string> <string name="cancel" msgid="5665114069455378395">"გაუქმება"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 36ee23abb15d..946a8611ec57 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Тіркелмеген"</string> <string name="status_unavailable" msgid="5279036186589861608">"Қолжетімсіз"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC еркін таңдауға қойылды"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Ешқандай құрылғы жалғанбаған}=1{1 құрылғы жалғанған}other{# құрылғы жалғанған}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Көбірек уақыт."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Азырақ уақыт."</string> <string name="cancel" msgid="5665114069455378395">"Бас тарту"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 75c0f6a81c2f..62b1c8c01fd1 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"មិនបានចុះឈ្មោះ"</string> <string name="status_unavailable" msgid="5279036186589861608">"មិនមាន"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC ត្រូវបានជ្រើសរើសដោយចៃដន្យ"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{បានភ្ជាប់ឧបករណ៍ 0}=1{បានភ្ជាប់ឧបករណ៍ 1}other{បានភ្ជាប់ឧបករណ៍ #}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"រយៈពេលច្រើនជាង។"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"រយៈពេលតិចជាង។"</string> <string name="cancel" msgid="5665114069455378395">"បោះបង់"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 94df4192aa88..042effbc21b5 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"ನೋಂದಾಯಿಸಲಾಗಿಲ್ಲ"</string> <string name="status_unavailable" msgid="5279036186589861608">"ಲಭ್ಯವಿಲ್ಲ"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC ಯಾದೃಚ್ಛಿಕವಾಗಿದೆ"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 ಸಾಧನವನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ}=1{1 ಸಾಧನವನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ}one{# ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ}other{# ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"ಹೆಚ್ಚು ಸಮಯ."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"ಕಡಿಮೆ ಸಮಯ."</string> <string name="cancel" msgid="5665114069455378395">"ರದ್ದುಮಾಡಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index db215dda4245..ee1895d13464 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"등록되지 않음"</string> <string name="status_unavailable" msgid="5279036186589861608">"사용할 수 없음"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC가 임의 선택됨"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{기기 0대 연결됨}=1{기기 1대 연결됨}other{기기 #대 연결됨}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"시간 늘리기"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"시간 줄이기"</string> <string name="cancel" msgid="5665114069455378395">"취소"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 3f6874f1f00a..0883e187dcb8 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Катталган эмес"</string> <string name="status_unavailable" msgid="5279036186589861608">"Жеткиликсиз"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC дарегин кокустан тандоо иштетилген"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 түзмөк туташып турат}=1{1 түзмөк туташып турат}other{# түзмөк туташып турат}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Көбүрөөк убакыт."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Азыраак убакыт."</string> <string name="cancel" msgid="5665114069455378395">"Жок"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index fc82a9be57e0..558f7ed8cbc6 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"ບໍ່ໄດ້ລົງທະບຽນ"</string> <string name="status_unavailable" msgid="5279036186589861608">"ບໍ່ມີຂໍ້ມູນ"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC is randomized"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{ຍັງບໍ່ໄດ້ເຊື່ອມຕໍ່ອຸປະກອນເທື່ອ}=1{ເຊື່ອມຕໍ່ 1 ອຸປະກອນແລ້ວ}other{ເຊື່ອມຕໍ່ # ອຸປະກອນແລ້ວ}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"ເພີ່ມເວລາ."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"ຫຼຸດເວລາ."</string> <string name="cancel" msgid="5665114069455378395">"ຍົກເລີກ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index bc4630dc7f80..772b6e15004e 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Neužregistruota"</string> <string name="status_unavailable" msgid="5279036186589861608">"Užimta"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC parinktas atsitiktine tvarka"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Neprijungtas nė vienas įrenginys}=1{Prijungtas vienas įrenginys}one{Prijungtas # įrenginys}few{Prijungti # įrenginiai}many{Prijungta # įrenginio}other{Prijungta # įrenginių}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Daugiau laiko."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Mažiau laiko."</string> <string name="cancel" msgid="5665114069455378395">"Atšaukti"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 133520be1498..87d6c8caf615 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Nav reģistrēts"</string> <string name="status_unavailable" msgid="5279036186589861608">"Nepieejams"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC ir atlasīts nejaušā secībā"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Nav pievienota neviena ierīce}=1{Pievienota viena ierīce}zero{Pievienotas # ierīces}one{Pievienota # ierīce}other{Pievienotas # ierīces}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Vairāk laika."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Mazāk laika."</string> <string name="cancel" msgid="5665114069455378395">"Atcelt"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 2de999818a39..0bfa707f444b 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Не е регистриран"</string> <string name="status_unavailable" msgid="5279036186589861608">"Недостапно"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC-адресата е рандомизирана"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 поврзани уреди}=1{1 поврзан уред}one{# поврзан уред}other{# поврзани уреди}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Повеќе време."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Помалку време."</string> <string name="cancel" msgid="5665114069455378395">"Откажи"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 0aad465e1c9e..cd43d16bde79 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"രജിസ്റ്റർ ചെയ്തിട്ടില്ല"</string> <string name="status_unavailable" msgid="5279036186589861608">"ലഭ്യമല്ല"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC യാദൃച്ഛികമാക്കിയിരിക്കുന്നു"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 ഉപകരണം കണക്റ്റ് ചെയ്തു}=1{1 ഉപകരണം കണക്റ്റ് ചെയ്തു}other{# ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്തു}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"കൂടുതൽ സമയം."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"കുറഞ്ഞ സമയം."</string> <string name="cancel" msgid="5665114069455378395">"റദ്ദാക്കുക"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 94e6059bc270..3f9ed6800c13 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Бүртгээгүй"</string> <string name="status_unavailable" msgid="5279036186589861608">"Байхгүй"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC хаягийг үүсгэсэн"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 төхөөрөмж холбогдсон}=1{1 төхөөрөмж холбогдсон}other{# төхөөрөмж холбогдсон}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Их хугацаа."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Бага хугацаа."</string> <string name="cancel" msgid="5665114069455378395">"Цуцлах"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 30775199ed70..c6e8f6be177e 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"नोंदवलेले नाही"</string> <string name="status_unavailable" msgid="5279036186589861608">"उपलब्ध नाही"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC रँडमाइझ केला आहे"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 डिव्हाइस कनेक्ट केले}=1{एक डिव्हाइस कनेक्ट केले}other{# डिव्हाइस कनेक्ट केली}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"जास्त वेळ."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"कमी वेळ."</string> <string name="cancel" msgid="5665114069455378395">"रद्द करा"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 24c1733d02cd..c946ae556193 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Tidak didaftarkan"</string> <string name="status_unavailable" msgid="5279036186589861608">"Tidak tersedia"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC dirawakkan"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 peranti disambungkan}=1{1 peranti disambungkan}other{# peranti disambungkan}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Lagi masa."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Kurang masa."</string> <string name="cancel" msgid="5665114069455378395">"Batal"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index aa9989ba3bd2..b5e4aa5ec270 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"မှတ်ပုံတင်မထားပါ"</string> <string name="status_unavailable" msgid="5279036186589861608">"မရရှိနိုင်ပါ။"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC ကို ကျပန်းပေးထားသည်"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{စက်တစ်ခုမျှ ချိတ်ဆက်မထားပါ}=1{စက် 1 ခု ချိတ်ဆက်ထားသည်}other{စက် # ခု ချိတ်ဆက်ထားသည်}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"အချိန်တိုးရန်။"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"အချိန်လျှော့ရန်။"</string> <string name="cancel" msgid="5665114069455378395">"မလုပ်တော့"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 9126f2904620..3551e755071e 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Ikke registrert"</string> <string name="status_unavailable" msgid="5279036186589861608">"Ikke tilgjengelig"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC velges tilfeldig"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 enheter er tilkoblet}=1{1 enhet er tilkoblet}other{# enheter er tilkoblet}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Mer tid."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Mindre tid."</string> <string name="cancel" msgid="5665114069455378395">"Avbryt"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index f3e2cdd45e9e..a9bb2b11f0bc 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"दर्ता नगरिएको"</string> <string name="status_unavailable" msgid="5279036186589861608">"अनुपलब्ध"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC क्रमरहित छ"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{कुनै पनि डिभाइस कनेक्ट गरिएको छैन}=1{एउटा डिभाइस कनेक्ट गरिएको छ}other{# वटा डिभाइस कनेक्ट गरिएका छन्}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"थप समय।"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"कम समय।"</string> <string name="cancel" msgid="5665114069455378395">"रद्द गर्नुहोस्"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index c8581994b75f..c8cb2b637a18 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Niet geregistreerd"</string> <string name="status_unavailable" msgid="5279036186589861608">"Niet beschikbaar"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC-adres is willekeurig"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 verbonden apparaten}=1{1 verbonden apparaat}other{# verbonden apparaten}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Meer tijd."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Minder tijd."</string> <string name="cancel" msgid="5665114069455378395">"Annuleren"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index ec72ba9fc567..ad2578065276 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"ପଞ୍ଜିକୃତ ହୋଇନାହିଁ"</string> <string name="status_unavailable" msgid="5279036186589861608">"ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MACର ଠିକଣା ରାଣ୍ଡମ୍ ଭାବେ ସେଟ୍ କରାଯାଇଛି"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0ଟି ଡିଭାଇସ ସଂଯୁକ୍ତ ହୋଇଛି}=1{1ଟି ଡିଭାଇସ ସଂଯୁକ୍ତ ହୋଇଛି}other{#ଟି ଡିଭାଇସ ସଂଯୁକ୍ତ ହୋଇଛି}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"ଅଧିକ ସମୟ।"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"କମ୍ ସମୟ।"</string> <string name="cancel" msgid="5665114069455378395">"ବାତିଲ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 34194589e3a6..3b3d4887b9ba 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"ਰਜਿਸਟਰ ਨਹੀਂ ਕੀਤੀ ਗਈ"</string> <string name="status_unavailable" msgid="5279036186589861608">"ਅਣਉਪਲਬਧ"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC ਬੇਤਰਤੀਬਾ ਹੈ"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 ਡੀਵਾਈਸ ਕਨੈਕਟ ਹੋ ਗਿਆ}=1{1 ਡੀਵਾਈਸ ਕਨੈਕਟ ਹੋ ਗਿਆ}other{# ਡੀਵਾਈਸ ਕਨੈਕਟ ਹੋ ਗਏ}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"ਹੋਰ ਸਮਾਂ।"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"ਘੱਟ ਸਮਾਂ।"</string> <string name="cancel" msgid="5665114069455378395">"ਰੱਦ ਕਰੋ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index addad23c5255..4470bd9d6f74 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Niezarejestrowane"</string> <string name="status_unavailable" msgid="5279036186589861608">"Niedostępny"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Adres MAC jest randomizowany"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 połączonych urządzeń}=1{1 połączone urządzenie}few{# połączone urządzenia}many{# połączonych urządzeń}other{# połączonego urządzenia}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Więcej czasu."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Mniej czasu."</string> <string name="cancel" msgid="5665114069455378395">"Anuluj"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 6f3f644ccfd1..b3137f17bee2 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Não registrado"</string> <string name="status_unavailable" msgid="5279036186589861608">"Não disponível"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"O MAC é randomizado"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 dispositivo conectado}=1{1 dispositivo conectado}one{# dispositivo conectado}other{# dispositivos conectados}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Mais tempo."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Menos tempo."</string> <string name="cancel" msgid="5665114069455378395">"Cancelar"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 566c5d318a5c..12f6097f6350 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Não registado"</string> <string name="status_unavailable" msgid="5279036186589861608">"Indisponível"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"O MAC é aleatório."</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 dispositivo ligado}=1{1 dispositivo ligado}other{# dispositivos ligados}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Mais tempo."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Menos tempo."</string> <string name="cancel" msgid="5665114069455378395">"Cancelar"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 6f3f644ccfd1..b3137f17bee2 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Não registrado"</string> <string name="status_unavailable" msgid="5279036186589861608">"Não disponível"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"O MAC é randomizado"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 dispositivo conectado}=1{1 dispositivo conectado}one{# dispositivo conectado}other{# dispositivos conectados}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Mais tempo."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Menos tempo."</string> <string name="cancel" msgid="5665114069455378395">"Cancelar"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 77b79c9aafd7..a6e0e3ae8c64 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Neînregistrat"</string> <string name="status_unavailable" msgid="5279036186589861608">"Indisponibilă"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC este aleatoriu"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Niciun dispozitiv conectat}=1{Un dispozitiv conectat}few{# dispozitive conectate}other{# de dispozitive conectate}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Mai mult timp."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Mai puțin timp."</string> <string name="cancel" msgid="5665114069455378395">"Anulează"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index c368913c2a44..a0b82697fc9a 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Не зарегистрирован"</string> <string name="status_unavailable" msgid="5279036186589861608">"Недоступно"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Случайный MAC-адрес"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Устройства не подключены}=1{Подключено 1 устройство}one{Подключено # устройство}few{Подключено # устройства}many{Подключено # устройств}other{Подключено # устройства}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Увеличить продолжительность"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Уменьшить продолжительность"</string> <string name="cancel" msgid="5665114069455378395">"Отмена"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 17090a75f132..b308c99863a0 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"ලියාපදිංචි වී නැත"</string> <string name="status_unavailable" msgid="5279036186589861608">"ලබාගත නොහැක"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC සසම්භාවී වේ"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{උපාංග 0ක් සම්බන්ධිතයි}=1{උපාංග 1ක් සම්බන්ධිතයි}one{උපාංග #ක් සම්බන්ධිතයි}other{උපාංග #ක් සම්බන්ධිතයි}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"වේලාව වැඩියෙන්."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"වේලාව අඩුවෙන්."</string> <string name="cancel" msgid="5665114069455378395">"අවලංගු කරන්න"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 06367677c365..bb7c32ec7949 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Neregistrované"</string> <string name="status_unavailable" msgid="5279036186589861608">"Nie je k dispozícii"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Adresa MAC je náhodná"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Je pripojených 0 zariadení}=1{Je pripojené 1 zariadenie}few{Sú pripojené # zariadenia}many{# devices connected}other{Je pripojených # zariadení}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Dlhší čas."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Kratší čas."</string> <string name="cancel" msgid="5665114069455378395">"Zrušiť"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 949ed422ea57..eced5d901052 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Ni registrirana"</string> <string name="status_unavailable" msgid="5279036186589861608">"Ni na voljo"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Naslov MAC je naključno izbran"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 naprav ni povezanih.}=1{1 naprava je povezana.}one{# naprava je povezana.}two{# napravi sta povezani.}few{# naprave so povezane.}other{# naprav je povezanih.}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Daljši čas."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Krajši čas."</string> <string name="cancel" msgid="5665114069455378395">"Prekliči"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 0eff3ab8dc82..81fed3476e0b 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Paregjistruar"</string> <string name="status_unavailable" msgid="5279036186589861608">"Nuk ofrohet"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Adresa MAC është e rastësishme"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 pajisje të lidhura}=1{1 pajisje e lidhur}other{# pajisje të lidhura}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Më shumë kohë."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Më pak kohë."</string> <string name="cancel" msgid="5665114069455378395">"Anulo"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 2ae0fe2e3c10..5eba95928b52 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Није регистрован"</string> <string name="status_unavailable" msgid="5279036186589861608">"Недоступно"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC адреса је насумично изабрана"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 уређаја је повезано}=1{1 уређај је повезан}one{# уређај је повезан}few{# уређаја су повезана}other{# уређаја је повезано}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Више времена."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Мање времена."</string> <string name="cancel" msgid="5665114069455378395">"Откажи"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index e8f045d06c2a..e6bc3a6ab506 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Ej registrerad"</string> <string name="status_unavailable" msgid="5279036186589861608">"Inte tillgängligt"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC-adressen slumpgenereras"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Ingen enhet är ansluten}=1{1 enhet är ansluten}other{# enheter är anslutna}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Längre tid."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Kortare tid."</string> <string name="cancel" msgid="5665114069455378395">"Avbryt"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 64119140bb65..9b74ee294a33 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Haijasajiliwa"</string> <string name="status_unavailable" msgid="5279036186589861608">"Hamna"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Imechagua anwani ya MAC kwa nasibu"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Hakuna kifaa kimeunganishwa}=1{Kifaa 1 kimeunganishwa}other{Vifaa # vimeunganishwa}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Muda zaidi."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Muda kidogo."</string> <string name="cancel" msgid="5665114069455378395">"Ghairi"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 608638483190..130a45e32d6a 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"பதிவு செய்யப்படவில்லை"</string> <string name="status_unavailable" msgid="5279036186589861608">"கிடைக்கவில்லை"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC முகவரி சீரற்றுள்ளது"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 சாதனம் இணைக்கப்பட்டது}=1{1 சாதனம் இணைக்கப்பட்டது}other{# சாதனங்கள் இணைக்கப்பட்டன}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"நேரத்தை அதிகரிக்கும்."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"நேரத்தைக் குறைக்கும்."</string> <string name="cancel" msgid="5665114069455378395">"ரத்துசெய்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index f43b56a36acc..b7cade9a4608 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"నమోదు కాలేదు"</string> <string name="status_unavailable" msgid="5279036186589861608">"అందుబాటులో లేదు"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC యాదృచ్ఛికంగా ఉంది"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 పరికరం కనెక్ట్ చేయబడింది}=1{1 పరికరం కనెక్ట్ చేయబడింది}other{# పరికరాలు కనెక్ట్ చేయబడ్డాయి}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"ఎక్కువ సమయం."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"తక్కువ సమయం."</string> <string name="cancel" msgid="5665114069455378395">"రద్దు చేయండి"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index c7b10fba15e2..1516ad9b4bc2 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"ไม่ได้ลงทะเบียน"</string> <string name="status_unavailable" msgid="5279036186589861608">"ไม่มี"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC เป็นแบบสุ่ม"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{มีอุปกรณ์ที่เชื่อมต่ออยู่ 0 เครื่อง}=1{มีอุปกรณ์ที่เชื่อมต่ออยู่ 1 เครื่อง}other{มีอุปกรณ์ที่เชื่อมต่ออยู่ # เครื่อง}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"เวลามากขึ้น"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"เวลาน้อยลง"</string> <string name="cancel" msgid="5665114069455378395">"ยกเลิก"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 8222c426541c..477782edd5f0 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Hindi nakarehistro"</string> <string name="status_unavailable" msgid="5279036186589861608">"Hindi available"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Naka-randomize ang MAC"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 device ang nakakonekta}=1{1 device ang nakakonekta}one{# device ang nakakonekta}other{# na device ang nakakonekta}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Dagdagan ang oras."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Bawasan ang oras."</string> <string name="cancel" msgid="5665114069455378395">"Kanselahin"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 991c9182ce3c..e562685c6b2e 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Kaydettirilmedi"</string> <string name="status_unavailable" msgid="5279036186589861608">"Kullanılamıyor"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC rastgele yapıldı"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 cihaz bağlandı}=1{1 cihaz bağlandı}other{# cihaz bağlandı}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Daha uzun süre."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Daha kısa süre."</string> <string name="cancel" msgid="5665114069455378395">"İptal"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 1cb31853a267..40e12f090ab8 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Не зареєстровано"</string> <string name="status_unavailable" msgid="5279036186589861608">"Недоступно"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Для MAC-адреси вибрано функцію довільного вибору"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Підключено 0 пристроїв}=1{Підключено 1 пристрій}one{Підключено # пристрій}few{Підключено # пристрої}many{Підключено # пристроїв}other{Підключено # пристрою}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Більше часу."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Менше часу."</string> <string name="cancel" msgid="5665114069455378395">"Скасувати"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 7e3a164b6c1a..e00f02ba0bd9 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"رجسٹر نہیں ہے"</string> <string name="status_unavailable" msgid="5279036186589861608">"غیر دستیاب"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC پتہ رینڈم ہے"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 آلہ منسلک ہے}=1{1 آلہ منسلک ہے}other{# آلات منسلک ہیں}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"زیادہ وقت۔"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"کم وقت۔"</string> <string name="cancel" msgid="5665114069455378395">"منسوخ کریں"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 845b83554e12..1bfdf9eccd56 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Registratsiya qilinmagan"</string> <string name="status_unavailable" msgid="5279036186589861608">"Mavjud emas"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Tasodifiy MAC manzil"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0 ta qurilma ulangan}=1{1 ta qurilma ulangan}other{# ta qurilma ulangan}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Ko‘proq vaqt."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Kamroq vaqt."</string> <string name="cancel" msgid="5665114069455378395">"Bekor qilish"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 18090352f0a0..71b7fb1a0d0f 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Chưa được đăng ký"</string> <string name="status_unavailable" msgid="5279036186589861608">"Không có"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Địa chỉ MAC được gán ngẫu nhiên"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Đã kết nối 0 thiết bị}=1{Đã kết nối 1 thiết bị}other{Đã kết nối # thiết bị}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Nhiều thời gian hơn."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Ít thời gian hơn."</string> <string name="cancel" msgid="5665114069455378395">"Hủy"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 7d027512c8ff..0dc214e5c9ad 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"未注册"</string> <string name="status_unavailable" msgid="5279036186589861608">"无法获取"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC 已随机化"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{已连接到 0 台设备}=1{已连接到 1 台设备}other{已连接到 # 台设备}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"增加时间。"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"减少时间。"</string> <string name="cancel" msgid="5665114069455378395">"取消"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index c563c86512be..d18a07ec7048 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"未註冊"</string> <string name="status_unavailable" msgid="5279036186589861608">"無法使用"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC 位址已隨機產生"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{未連接任何裝置}=1{已連接 1 部裝置}other{已連接 # 部裝置}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"增加時間。"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"減少時間。"</string> <string name="cancel" msgid="5665114069455378395">"取消"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index eb3843ea1f1a..95f266f2c254 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"未註冊"</string> <string name="status_unavailable" msgid="5279036186589861608">"無法取得"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC 位址已隨機化"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{未與任何裝置連線}=1{已與 1 部裝置連線}other{已與 # 部裝置連線}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"增加時間。"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"減少時間。"</string> <string name="cancel" msgid="5665114069455378395">"取消"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index f4fd35205f1a..9c789c42ce95 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -517,7 +517,8 @@ <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Akubhalisiwe"</string> <string name="status_unavailable" msgid="5279036186589861608">"Ayitholakali"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"I-MAC ayihleliwe"</string> - <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Idivayisi engu-0 ixhunyiwe}=1{Idivayisi e-1 ixhunyiwe}one{Amadivayisi angu-# axhunyiwe}other{Amadivayisi angu-# axhunyiwe}}"</string> + <!-- no translation found for wifi_tether_connected_summary (5100712926640492336) --> + <skip /> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Isikhathi esiningi."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Isikhathi esincane."</string> <string name="cancel" msgid="5665114069455378395">"Khansela"</string> diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java index 4d2b1ae2ade0..21cdc492e4ea 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java @@ -20,6 +20,7 @@ import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI; +import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SEARCHABLE; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI; import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL; import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY; @@ -256,4 +257,26 @@ public class ActivityTileTest { assertThat(tile.getType()).isEqualTo(Tile.Type.SWITCH_WITH_ACTION); } + + @Test + public void isSearchable_noMetadata_isTrue() { + final Tile tile = new ActivityTile(null, "category"); + + assertThat(tile.isSearchable()).isTrue(); + } + + @Test + public void isSearchable_notSet_isTrue() { + final Tile tile = new ActivityTile(mActivityInfo, "category"); + + assertThat(tile.isSearchable()).isTrue(); + } + + @Test + public void isSearchable_isSet_false() { + mActivityInfo.metaData.putBoolean(META_DATA_PREFERENCE_SEARCHABLE, false); + final Tile tile = new ActivityTile(mActivityInfo, "category"); + + assertThat(tile.isSearchable()).isFalse(); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java index 80f9efb8b5ac..faccf2f15f49 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java @@ -20,6 +20,7 @@ import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT; +import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SEARCHABLE; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE; import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL; @@ -257,6 +258,28 @@ public class ProviderTileTest { assertThat(tile.getType()).isEqualTo(Tile.Type.GROUP); } + @Test + public void isSearchable_noMetadata_isTrue() { + final Tile tile = new ProviderTile(mProviderInfo, "category", null); + + assertThat(tile.isSearchable()).isTrue(); + } + + @Test + public void isSearchable_notSet_isTrue() { + final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData); + + assertThat(tile.isSearchable()).isTrue(); + } + + @Test + public void isSearchable_isSet_false() { + mMetaData.putBoolean(META_DATA_PREFERENCE_SEARCHABLE, false); + final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData); + + assertThat(tile.isSearchable()).isFalse(); + } + @Implements(TileUtils.class) private static class ShadowTileUtils { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java deleted file mode 100644 index 0f708cdb9992..000000000000 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2019 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.settingslib.widget; - -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.app.Application; -import android.view.LayoutInflater; -import android.view.View; - -import androidx.preference.PreferenceViewHolder; - -import com.android.settingslib.widget.preference.radio.R; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -@RunWith(RobolectricTestRunner.class) -public class RadioButtonPreferenceTest { - - private Application mContext; - private RadioButtonPreference mPreference; - - private View mExtraWidgetContainer; - private View mExtraWidget; - - private boolean mIsClickListenerCalled; - private View.OnClickListener mClickListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - mIsClickListenerCalled = true; - } - }; - - @Before - public void setUp() { - mContext = RuntimeEnvironment.application; - mPreference = new RadioButtonPreference(mContext); - - View view = LayoutInflater.from(mContext) - .inflate(R.layout.preference_radio, null /* root */); - PreferenceViewHolder preferenceViewHolder = - PreferenceViewHolder.createInstanceForTests(view); - mPreference.onBindViewHolder(preferenceViewHolder); - - mExtraWidgetContainer = view.findViewById(R.id.radio_extra_widget_container); - mExtraWidget = view.findViewById(R.id.radio_extra_widget); - } - - @Test - public void shouldHaveRadioPreferenceLayout() { - assertThat(mPreference.getLayoutResource()).isEqualTo(R.layout.preference_radio); - } - - @Test - public void iconSpaceReservedShouldBeFalse() { - assertThat(mPreference.isIconSpaceReserved()).isFalse(); - } - - @Test - public void onBindViewHolder_withSummary_containerShouldBeVisible() { - mPreference.setSummary("some summary"); - View summaryContainer = new View(mContext); - View view = mock(View.class); - when(view.findViewById(R.id.summary_container)).thenReturn(summaryContainer); - PreferenceViewHolder preferenceViewHolder = - PreferenceViewHolder.createInstanceForTests(view); - - mPreference.onBindViewHolder(preferenceViewHolder); - - assertEquals(View.VISIBLE, summaryContainer.getVisibility()); - } - - @Test - public void onBindViewHolder_emptySummary_containerShouldBeGone() { - mPreference.setSummary(""); - View summaryContainer = new View(mContext); - View view = mock(View.class); - when(view.findViewById(R.id.summary_container)).thenReturn(summaryContainer); - PreferenceViewHolder preferenceViewHolder = - PreferenceViewHolder.createInstanceForTests(view); - - mPreference.onBindViewHolder(preferenceViewHolder); - - assertEquals(View.GONE, summaryContainer.getVisibility()); - } - - @Test - public void nullSummary_containerShouldBeGone() { - mPreference.setSummary(null); - View summaryContainer = new View(mContext); - View view = mock(View.class); - when(view.findViewById(R.id.summary_container)).thenReturn(summaryContainer); - PreferenceViewHolder preferenceViewHolder = - PreferenceViewHolder.createInstanceForTests(view); - mPreference.onBindViewHolder(preferenceViewHolder); - assertEquals(View.GONE, summaryContainer.getVisibility()); - } - - @Test - public void setAppendixVisibility_setGone_shouldBeGone() { - mPreference.setAppendixVisibility(View.GONE); - - View view = LayoutInflater.from(mContext) - .inflate(R.layout.preference_radio, null /* root */); - PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view); - mPreference.onBindViewHolder(holder); - assertThat(holder.findViewById(R.id.appendix).getVisibility()).isEqualTo(View.GONE); - } - - @Test - public void setExtraWidgetListener_setNull_extraWidgetShouldInvisible() { - mPreference.setExtraWidgetOnClickListener(null); - - assertEquals(View.GONE, mExtraWidgetContainer.getVisibility()); - } - - @Test - public void setExtraWidgetListener_extraWidgetShouldVisible() { - mPreference.setExtraWidgetOnClickListener(mClickListener); - - assertEquals(View.VISIBLE, mExtraWidgetContainer.getVisibility()); - } - - @Test - public void onClickListener_setExtraWidgetOnClickListener_ShouldCalled() { - mPreference.setExtraWidgetOnClickListener(mClickListener); - - assertThat(mIsClickListenerCalled).isFalse(); - mExtraWidget.callOnClick(); - assertThat(mIsClickListenerCalled).isTrue(); - } -} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java index b57f6ca26f39..969f1fde604e 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java @@ -19,6 +19,7 @@ package com.android.providers.settings; import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE; import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT; import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT; + import static com.android.providers.settings.Flags.supportOverrides; import android.annotation.SuppressLint; @@ -180,6 +181,7 @@ public final class DeviceConfigService extends Binder { DELETE, LIST, LIST_NAMESPACES, + LIST_LOCAL_OVERRIDES, RESET, SET_SYNC_DISABLED_FOR_TESTS, GET_SYNC_DISABLED_FOR_TESTS, @@ -266,6 +268,11 @@ public final class DeviceConfigService extends Binder { if (peekNextArg() == null) { isValid = true; } + } else if (supportOverrides() && "list_local_overrides".equalsIgnoreCase(cmd)) { + verb = CommandVerb.LIST_LOCAL_OVERRIDES; + if (peekNextArg() == null) { + isValid = true; + } } else if ("reset".equalsIgnoreCase(cmd)) { verb = CommandVerb.RESET; } else if ("set_sync_disabled_for_tests".equalsIgnoreCase(cmd)) { @@ -418,62 +425,29 @@ public final class DeviceConfigService extends Binder { : "Failed to delete " + key + " from " + namespace); break; case LIST: - if (supportOverrides()) { - pout.println("Server overrides:"); - - Map<String, Map<String, String>> underlyingValues = - DeviceConfig.getUnderlyingValuesForOverriddenFlags(); - - if (namespace != null) { - DeviceConfig.Properties properties = - DeviceConfig.getProperties(namespace); - List<String> keys = new ArrayList<>(properties.getKeyset()); - Collections.sort(keys); - for (String name : keys) { - String valueReadFromDeviceConfig = properties.getString(name, null); - String underlyingValue = underlyingValues.get(namespace).get(name); - String printValue = underlyingValue != null - ? underlyingValue - : valueReadFromDeviceConfig; - pout.println(name + "=" + printValue); - } - } else { - for (String line : listAll(iprovider)) { - boolean isPrivateNamespace = false; + if (namespace != null) { + DeviceConfig.Properties properties = + DeviceConfig.getProperties(namespace); + List<String> keys = new ArrayList<>(properties.getKeyset()); + Collections.sort(keys); + for (String name : keys) { + pout.println(name + "=" + properties.getString(name, null)); + } + } else { + for (String line : listAll(iprovider)) { + if (supportOverrides()) { + boolean isPrivate = false; for (String privateNamespace : PRIVATE_NAMESPACES) { if (line.startsWith(privateNamespace)) { - isPrivateNamespace = true; + isPrivate = true; + break; } } - if (!isPrivateNamespace) { + + if (!isPrivate) { pout.println(line); } - } - } - - pout.println(""); - pout.println("Local overrides (these take precedence):"); - for (String overrideNamespace : underlyingValues.keySet()) { - Map<String, String> flagToValue = - underlyingValues.get(overrideNamespace); - for (String flag : flagToValue.keySet()) { - String flagText = overrideNamespace + "/" + flag; - String valueText = - DeviceConfig.getProperty(overrideNamespace, flag); - pout.println(flagText + "=" + valueText); - } - } - } else { - if (namespace != null) { - DeviceConfig.Properties properties = - DeviceConfig.getProperties(namespace); - List<String> keys = new ArrayList<>(properties.getKeyset()); - Collections.sort(keys); - for (String name : keys) { - pout.println(name + "=" + properties.getString(name, null)); - } - } else { - for (String line : listAll(iprovider)) { + } else { pout.println(line); } } @@ -503,6 +477,22 @@ public final class DeviceConfigService extends Binder { pout.println(namespaces.get(i)); } break; + case LIST_LOCAL_OVERRIDES: + if (supportOverrides()) { + Map<String, Map<String, String>> underlyingValues = + DeviceConfig.getUnderlyingValuesForOverriddenFlags(); + for (String overrideNamespace : underlyingValues.keySet()) { + Map<String, String> flagToValue = + underlyingValues.get(overrideNamespace); + for (String flag : flagToValue.keySet()) { + String flagText = overrideNamespace + "/" + flag; + String valueText = + DeviceConfig.getProperty(overrideNamespace, flag); + pout.println(flagText + "=" + valueText); + } + } + } + break; case RESET: DeviceConfig.resetToDefaults(resetMode, namespace); break; diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING index af6fa86bacf4..5c2f979ac639 100644 --- a/packages/SystemUI/TEST_MAPPING +++ b/packages/SystemUI/TEST_MAPPING @@ -131,5 +131,22 @@ } ] } + ], + // v2/sysui/suite/test-mapping-sysui-screenshot-test + "sysui-screenshot-test": [ + { + "name": "SystemUIGoogleScreenshotTests", + "options": [ + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "android.platform.test.annotations.Postsubmit" + } + ] + } ] } diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-my/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-my/strings.xml index 1097f87c0be9..783b37516822 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-my/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-my/strings.xml @@ -8,7 +8,7 @@ <string name="a11y_settings_label" msgid="3977714687248445050">"အများသုံးနိုင်မှု ဆက်တင်များ"</string> <string name="power_label" msgid="7699720321491287839">"ပါဝါခလုတ်"</string> <string name="power_utterance" msgid="7444296686402104807">"ပါဝါ ရွေးစရာများ"</string> - <string name="recent_apps_label" msgid="6583276995616385847">"လတ်တလောသုံး အက်ပ်များ"</string> + <string name="recent_apps_label" msgid="6583276995616385847">"မကြာသေးမီက အက်ပ်များ"</string> <string name="lockscreen_label" msgid="648347953557887087">"လော့ခ်မျက်နှာပြင်"</string> <string name="quick_settings_label" msgid="2999117381487601865">"အမြန် ဆက်တင်များ"</string> <string name="notifications_label" msgid="6829741046963013567">"အကြောင်းကြားချက်များ"</string> diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt index 46a9d42d0606..d0f2ce84655d 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt @@ -48,13 +48,13 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.SceneScope -import com.android.systemui.res.R import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.res.R import com.android.systemui.scene.shared.model.Direction import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel @@ -104,7 +104,8 @@ private fun SceneScope.BouncerScene( modifier: Modifier = Modifier, ) { val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState() - val authMethodViewModel: AuthMethodBouncerViewModel? by viewModel.authMethod.collectAsState() + val authMethodViewModel: AuthMethodBouncerViewModel? by + viewModel.authMethodViewModel.collectAsState() val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState() var dialog: Dialog? by remember { mutableStateOf(null) } val backgroundColor = MaterialTheme.colorScheme.surface diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt index 8e3400872a9a..519c0a9c4c7c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt @@ -39,7 +39,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.android.compose.animation.scene.ElementKey @@ -166,11 +168,18 @@ fun SceneScope.ExpandedShadeHeader( modifier = Modifier.align(Alignment.CenterVertically) // use graphicsLayer instead of Modifier.scale to anchor transform to - // top left corner + // the (start, top) corner .graphicsLayer( scaleX = 2.57f, scaleY = 2.57f, - transformOrigin = TransformOrigin(0f, 0.5f) + transformOrigin = + TransformOrigin( + when (LocalLayoutDirection.current) { + LayoutDirection.Ltr -> 0f + LayoutDirection.Rtl -> 1f + }, + 0.5f + ) ), ) Spacer(modifier = Modifier.weight(1f)) diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml index 9ad9d560394f..1e03c03b1ae4 100644 --- a/packages/SystemUI/res-keyguard/values-ky/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml @@ -72,7 +72,7 @@ <string name="kg_unlock_with_pin_or_fp" msgid="5635161174698729890">"PIN кд же мнжа изи мнен клпусн ачңыз"</string> <string name="kg_unlock_with_password_or_fp" msgid="2251295907826814237">"Срсөз же мнжа изи мнен клпусн ачңз"</string> <string name="kg_unlock_with_pattern_or_fp" msgid="2391870539909135046">"Грфиклык ачкч же мнжа изи менн клпусн ачңз"</string> - <string name="kg_prompt_after_dpm_lock" msgid="6002804765868345917">"Кошумча коопсуздук үчүн түзмөк жумуш саясатына ылайык кулпуланган"</string> + <string name="kg_prompt_after_dpm_lock" msgid="6002804765868345917">"Кошумча коопсуздук үчүн түзмөк жумуш эрежеси боюнча кулпуланган"</string> <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Бекем кулпулангандан кийин PIN код талап кылынат"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Бекем кулпулангандан кийин сырсөз талап кылынат"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Бекем кулпулангандан кийн грфикалык ачкыч талп клынт"</string> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 8c817330953c..d1067a9960bf 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -105,6 +105,13 @@ screen. --> <item name="half_opened_bouncer_height_ratio" type="dimen" format="float">0.0</item> + <!-- Proportion of the screen height to use to set the maximum height of the bouncer to when + the device is in the DEVICE_POSTURE_HALF_OPENED posture. + + This value is only used when motion layout bouncer is used - when flag + landscape.enable_lockscreen (b/293252410) is on --> + <item name="motion_layout_half_fold_bouncer_height_ratio" type="dimen" format="float">0.55</item> + <!-- The actual amount of translation that is applied to the security when it animates from one side of the screen to the other in one-handed or user switcher mode. Note that it will always translate from the side of the screen to the other (it will "jump" closer to the diff --git a/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml b/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml index 6112411402c4..751d6d8d04e7 100644 --- a/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml +++ b/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml @@ -10,9 +10,34 @@ motion:duration="0" motion:autoTransition="none"/> + <Transition + motion:constraintSetStart="@id/single_constraints" + motion:constraintSetEnd="@+id/half_folded_single_constraints" + motion:duration="@integer/material_motion_duration_short_1" + motion:autoTransition="none"/> + <!-- No changes to default layout --> <ConstraintSet android:id="@+id/single_constraints"/> + <ConstraintSet android:id="@+id/half_folded_single_constraints"> + + <Constraint + android:id="@+id/pattern_top_guideline" + androidprv:layout_constraintGuide_percent= + "@dimen/motion_layout_half_fold_bouncer_height_ratio"/> + + <Constraint + android:id="@+id/keyguard_selector_fade_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="0dp" + android:layout_marginTop="@dimen/keyguard_eca_top_margin" + android:orientation="vertical" + androidprv:layout_constraintBottom_toBottomOf="parent" + androidprv:layout_constraintTop_toBottomOf="@+id/flow1"/> + + </ConstraintSet> + <ConstraintSet android:id="@+id/split_constraints"> <Constraint diff --git a/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml b/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml index 2a1270c80b75..cc498f4a7ab3 100644 --- a/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml +++ b/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml @@ -26,11 +26,35 @@ motion:constraintSetStart="@id/single_constraints" motion:constraintSetEnd="@+id/split_constraints" motion:duration="0" - motion:autoTransition="none"/> + motion:autoTransition="none" /> + + <Transition + motion:constraintSetStart="@id/single_constraints" + motion:constraintSetEnd="@+id/half_folded_single_constraints" + motion:duration="@integer/material_motion_duration_short_1" /> <!-- No changes to default layout --> <ConstraintSet android:id="@+id/single_constraints"/> + <ConstraintSet android:id="@+id/half_folded_single_constraints"> + + <Constraint + android:id="@+id/pin_pad_top_guideline" + androidprv:layout_constraintGuide_percent= + "@dimen/motion_layout_half_fold_bouncer_height_ratio"/> + + <Constraint + android:id="@+id/keyguard_selector_fade_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="0dp" + android:layout_marginTop="@dimen/keyguard_eca_top_margin" + android:orientation="vertical" + androidprv:layout_constraintBottom_toBottomOf="parent" + androidprv:layout_constraintTop_toBottomOf="@+id/flow1"/> + + </ConstraintSet> + <ConstraintSet android:id="@+id/split_constraints"> <Constraint @@ -68,4 +92,5 @@ android:layout_marginTop="@dimen/keyguard_eca_top_margin" /> </ConstraintSet> + </MotionScene>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar.xml b/packages/SystemUI/res/drawable/volume_row_seekbar.xml index 7ce1ba3be7f6..d7d75d4304d0 100644 --- a/packages/SystemUI/res/drawable/volume_row_seekbar.xml +++ b/packages/SystemUI/res/drawable/volume_row_seekbar.xml @@ -20,17 +20,14 @@ <layer-list xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:paddingMode="stack" > + <!-- The groove used for indicating max volume !--> <item android:id="@android:id/background" android:gravity="center_vertical|fill_horizontal"> - <inset - android:insetLeft="@dimen/rounded_slider_track_inset" - android:insetRight="@dimen/rounded_slider_track_inset" > - <shape> - <size android:height="@dimen/volume_dialog_track_width" /> - <corners android:radius="@dimen/volume_dialog_track_corner_radius" /> - <solid android:color="?androidprv:attr/colorAccentSecondaryVariant" /> - </shape> - </inset> + <shape> + <size android:height="@dimen/volume_dialog_track_width" /> + <corners android:radius="@dimen/volume_dialog_panel_width_half" /> + <solid android:color="?androidprv:attr/materialColorOutlineVariant" /> + </shape> </item> <item android:id="@android:id/progress" android:gravity="center_vertical|fill_horizontal"> diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml new file mode 100644 index 000000000000..6dd44fbe645d --- /dev/null +++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml @@ -0,0 +1,94 @@ +<?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. + --> + +<!-- TODO(b/298124674) remove this root --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/bluetooth_device_list_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginBottom="4dp"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/bluetooth_device_row" + style="@style/BluetoothTileDialog.Device" + android:layout_height="@dimen/bluetooth_dialog_device_height" + android:paddingEnd="24dp" + android:paddingStart="20dp" + android:baselineAligned="false"> + + <ImageView + android:id="@+id/bluetooth_device_icon" + android:contentDescription="@string/accessibility_bluetooth_device_icon" + android:layout_width="24dp" + android:layout_height="24dp" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + android:layout_gravity="center_vertical" /> + + <View + android:id="@+id/bluetooth_device" + android:layout_width="0dp" + android:layout_height="0dp" + app:layout_constraintTop_toTopOf="@+id/bluetooth_device_name" + app:layout_constraintBottom_toBottomOf="@+id/bluetooth_device_summary" + app:layout_constraintStart_toStartOf="@+id/bluetooth_device_name" + app:layout_constraintEnd_toEndOf="@+id/bluetooth_device_name" /> + + <TextView + android:layout_width="0dp" + android:id="@+id/bluetooth_device_name" + style="@style/BluetoothTileDialog.DeviceName" + android:paddingStart="20dp" + android:paddingTop="10dp" + app:layout_constraintWidth_percent="0.7" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon" + app:layout_constraintEnd_toStartOf="@+id/gear_icon" + app:layout_constraintBottom_toTopOf="@+id/bluetooth_device_summary" + android:gravity="center_vertical" + android:textSize="14sp" /> + + <TextView + android:layout_width="0dp" + android:id="@+id/bluetooth_device_summary" + style="@style/BluetoothTileDialog.DeviceSummary" + android:paddingStart="20dp" + android:paddingBottom="10dp" + app:layout_constraintWidth_percent="0.7" + app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name" + app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon" + app:layout_constraintEnd_toStartOf="@+id/gear_icon" + app:layout_constraintBottom_toBottomOf="parent" + android:gravity="center_vertical" /> + + <ImageView + android:id="@+id/gear_icon" + android:src="@drawable/ic_settings_24dp" + android:contentDescription="@string/accessibility_bluetooth_device_settings_gear" + android:layout_width="0dp" + android:layout_height="24dp" + app:layout_constraintStart_toEndOf="@+id/bluetooth_device_name" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintWidth_percent="0.3" + android:gravity="center_vertical" + android:paddingStart="10dp" /> + </androidx.constraintlayout.widget.ConstraintLayout> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml new file mode 100644 index 000000000000..16aeb951822c --- /dev/null +++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml @@ -0,0 +1,188 @@ +<?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. + --> + +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/root" + style="@style/Widget.SliceView.Panel" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/bluetooth_tile_dialog_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="24dp" + android:ellipsize="end" + android:gravity="center_vertical|center_horizontal" + android:text="@string/quick_settings_bluetooth_label" + android:textAppearance="@style/TextAppearance.Dialog.Title" + android:textSize="24sp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintBottom_toTopOf="@id/bluetooth_tile_dialog_subtitle" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/bluetooth_tile_dialog_subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:layout_marginBottom="@dimen/bluetooth_dialog_layout_margin" + android:ellipsize="end" + android:gravity="center_vertical|center_horizontal" + android:maxLines="1" + android:text="@string/quick_settings_bluetooth_tile_subtitle" + android:textAppearance="@style/TextAppearance.Dialog.Body.Message" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintBottom_toTopOf="@id/bluetooth_toggle_title" + app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_title" /> + + <TextView + android:id="@+id/bluetooth_toggle_title" + style="@style/BluetoothTileDialog.Device" + android:layout_width="0dp" + android:layout_height="64dp" + android:gravity="center_vertical" + android:layout_marginTop="4dp" + android:text="@string/turn_on_bluetooth" + android:textAppearance="@style/TextAppearance.Dialog.Body.Message" + android:textSize="16sp" + app:layout_constraintEnd_toStartOf="@+id/bluetooth_toggle" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_subtitle" /> + + <Switch + android:id="@+id/bluetooth_toggle" + style="@style/BluetoothTileDialog.Device" + android:layout_width="0dp" + android:layout_height="48dp" + android:gravity="center|center_vertical" + android:paddingEnd="24dp" + android:layout_marginTop="10dp" + android:contentDescription="@string/turn_on_bluetooth" + android:switchMinWidth="@dimen/settingslib_switch_track_width" + android:theme="@style/MainSwitch.Settingslib" + android:thumb="@drawable/settingslib_thumb_selector" + android:track="@drawable/settingslib_track_selector" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/bluetooth_toggle_title" + app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_subtitle" /> + + <androidx.constraintlayout.widget.Group + android:id="@+id/pair_new_device_layout_group" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + app:constraint_referenced_ids="ic_add,pair_new_device_text" /> + + <ImageView + android:id="@+id/ic_add" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="36dp" + android:gravity="center_vertical" + android:importantForAccessibility="no" + android:src="@drawable/ic_add" + app:layout_constraintBottom_toTopOf="@id/device_list" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toStartOf="@id/pair_new_device_text" + app:layout_constraintTop_toBottomOf="@id/bluetooth_toggle_title" + android:tint="?android:attr/textColorPrimary" /> + + <TextView + android:id="@+id/pair_new_device_text" + style="@style/BluetoothTileDialog.Device" + android:layout_width="0dp" + android:layout_height="@dimen/bluetooth_dialog_device_height" + android:gravity="center_vertical" + android:layout_marginStart="0dp" + android:paddingStart="20dp" + android:text="@string/pair_new_bluetooth_devices" + android:textSize="14sp" + android:textAppearance="@style/TextAppearance.Dialog.Title" + app:layout_constraintBottom_toTopOf="@id/device_list" + app:layout_constraintStart_toEndOf="@+id/ic_add" + app:layout_constraintTop_toBottomOf="@id/bluetooth_toggle_title" + app:layout_constraintEnd_toEndOf="parent" /> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/device_list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:nestedScrollingEnabled="false" + android:overScrollMode="never" + android:scrollbars="vertical" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/pair_new_device_text" + app:layout_constraintBottom_toTopOf="@+id/see_all_text" /> + + <androidx.constraintlayout.widget.Group + android:id="@+id/see_all_layout_group" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + app:constraint_referenced_ids="ic_arrow,see_all_text" /> + + <ImageView + android:id="@+id/ic_arrow" + android:layout_marginStart="36dp" + android:layout_width="24dp" + android:layout_height="24dp" + android:importantForAccessibility="no" + android:gravity="center_vertical" + android:src="@drawable/ic_arrow_forward" + app:layout_constraintBottom_toTopOf="@+id/done_button" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toStartOf="@id/see_all_text" + app:layout_constraintTop_toBottomOf="@id/device_list" /> + + <TextView + android:id="@+id/see_all_text" + style="@style/BluetoothTileDialog.Device" + android:layout_width="0dp" + android:layout_height="@dimen/bluetooth_dialog_device_height" + android:gravity="center_vertical" + android:layout_marginStart="0dp" + android:paddingStart="20dp" + android:text="@string/see_all_bluetooth_devices" + android:textSize="14sp" + android:textAppearance="@style/TextAppearance.Dialog.Title" + app:layout_constraintBottom_toTopOf="@+id/done_button" + app:layout_constraintStart_toEndOf="@+id/ic_arrow" + app:layout_constraintTop_toBottomOf="@id/device_list" + app:layout_constraintEnd_toEndOf="parent" /> + + <Button + android:id="@+id/done_button" + style="@style/Widget.Dialog.Button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/dialog_bottom_padding" + android:layout_marginEnd="@dimen/dialog_side_padding" + android:layout_marginStart="@dimen/dialog_side_padding" + android:clickable="true" + android:ellipsize="end" + android:focusable="true" + android:maxLines="1" + android:text="@string/inline_done_button" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/see_all_text" /> +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml index 37b8ae0f40c4..c70f8e2b1c07 100644 --- a/packages/SystemUI/res/layout/notif_half_shelf.xml +++ b/packages/SystemUI/res/layout/notif_half_shelf.xml @@ -22,8 +22,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal|bottom" android:paddingStart="4dp" - android:paddingEnd="4dp" -> + android:paddingEnd="4dp"> <LinearLayout android:id="@+id/half_shelf" @@ -82,11 +81,21 @@ android:theme="@style/MainSwitch.Settingslib"/> </com.android.systemui.statusbar.notification.row.AppControlView> - <!-- ChannelRows get added dynamically --> - + <ScrollView + android:layout_width="match_parent" + android:layout_height="@dimen/notification_blocker_channel_list_height" + android:clipToPadding="false"> + <LinearLayout + android:id="@+id/scrollView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + <!-- ChannelRows get added dynamically --> + </LinearLayout> + </ScrollView> </com.android.systemui.statusbar.notification.row.ChannelEditorListView> - <RelativeLayout + <LinearLayout android:id="@+id/bottom_actions" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -98,25 +107,23 @@ android:text="@string/see_more_title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentStart="true" - android:layout_centerVertical="true" - android:gravity="start|center_vertical" android:minWidth="@dimen/notification_importance_toggle_size" android:minHeight="@dimen/notification_importance_toggle_size" android:maxWidth="200dp" style="@style/Widget.Dialog.Button"/> + <Space + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" /> <TextView android:id="@+id/done_button" android:text="@string/inline_ok_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:gravity="end|center_vertical" android:maxWidth="125dp" android:minWidth="@dimen/notification_importance_toggle_size" android:minHeight="@dimen/notification_importance_toggle_size" - android:layout_alignParentEnd="true" style="@style/Widget.Dialog.Button"/> - </RelativeLayout> + </LinearLayout> </LinearLayout> </FrameLayout> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 27db1c95d2f4..0f17288b725d 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Skuif af"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Beweeg links"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Beweeg regs"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Vermeerder vergrootglas se breedte"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Verminder vergrootglas se breedte"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Vermeerder vergrootglas se hoogte"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Verminder vergrootglas se hoogte"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Vergrotingwisselaar"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Vergroot die hele skerm"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Vergroot \'n deel van die skerm"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index a68ec1513356..5f827cd5a2e9 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ወደ ታች ውሰድ"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ወደ ግራ ውሰድ"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"ወደ ቀኝ ውሰድ"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"የማጉያ ስፋትን ጨምር"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"የማጉያ ስፋትን ቀንስ"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"የማጉያ ቁመትን ጨምር"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"የማጉያ ቁመትን ቀንስ"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"የማጉላት ማብሪያ/ማጥፊያ"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ሙሉ ገፅ እይታን ያጉሉ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"የማያ ገጹን ክፍል አጉላ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 42e60c50d95d..813f98997a08 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"نقل للأسفل"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"نقل لليسار"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"نقل لليمين"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"زيادة عرض نافذة المكبِّر"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"تقليل عرض نافذة المكبِّر"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"زيادة ارتفاع نافذة المكبِّر"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"تقليل ارتفاع نافذة المكبِّر"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"مفتاح تبديل وضع التكبير"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"تكبير الشاشة كلها"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"تكبير جزء من الشاشة"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 7026e0c4d754..43461f4ecebf 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"তললৈ নিয়ক"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"বাওঁফাললৈ নিয়ক"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"সোঁফাললৈ নিয়ক"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"বিবৰ্ধকৰ প্ৰস্থ বৃদ্ধি কৰক"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"বিবৰ্ধকৰ প্ৰস্থ হ্ৰাস কৰক"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"বিবৰ্ধকৰ উচ্চতা বৃদ্ধি কৰক"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"বিবৰ্ধকৰ উচ্চতা হ্ৰাস কৰক"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"বিবৰ্ধনৰ ছুইচ"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"পূৰ্ণ স্ক্ৰীন বিবৰ্ধন কৰক"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্ৰীনৰ কিছু অংশ বিবৰ্ধন কৰক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 68a8b87865b7..7cda9dc5c5b5 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Aşağı köçürün"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sola köçürün"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sağa köçürün"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Böyüdücünün enini artırın"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Böyüdücünün enini azaldın"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Böyüdücünün uzunluğunu artırın"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Böyüdücünün uzunluğunu azaldın"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Böyütmə dəyişdiricisi"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Tam ekranı böyüdün"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran hissəsinin böyüdülməsi"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 37aa8231a77d..3366a72c087e 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pomerite nadole"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pomerite nalevo"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pomerite nadesno"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Povećajte širinu lupe"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Smanjite širinu lupe"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Povećajte visinu lupe"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Smanjite visinu lupe"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prelazak na drugi režim uvećanja"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Uvećajte ceo ekran"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećajte deo ekrana"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 69fce40145ea..1b7b1a271ec4 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Перамясціць ніжэй"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Перамясціць улева"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Перамясціць управа"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Павялічыць шырыню лупы"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Паменшыць шырыню лупы"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Павялічыць вышыню лупы"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Паменшыць вышыню лупы"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Пераключальнік павелічэння"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Павялічыць увесь экран"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Павялічыць частку экрана"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 3c0dd63762d5..e9ff7daaf834 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Преместване надолу"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Преместване наляво"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Преместване надясно"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Увеличаване на ширината на лупата"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Намаляване на ширината на лупата"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Увеличаване на височината на лупата"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Намаляване на височината на лупата"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Превключване на увеличението"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Увеличаване на целия екран"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличаване на част от екрана"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 0975b987d06a..0c73df3f1921 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"নিচে নামান"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"বাঁদিকে সরান"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"ডানদিকে সরান"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"ম্যাগনিফায়ার উইন্ডোর প্রস্থ বাড়ান"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"ম্যাগনিফায়ার উইন্ডোর প্রস্থ কমান"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"ম্যাগনিফায়ার উইন্ডোর উচ্চতা বাড়ান"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"ম্যাগনিফায়ার উইন্ডোর উচ্চতা কমান"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"বড় করে দেখার সুইচ"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"সম্পূর্ণ স্ক্রিন বড় করে দেখা"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্রিনের কিছুটা অংশ বড় করুন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 021ba8aba8c3..bdba725cce1a 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pomjeranje prema dolje"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pomjeranje lijevo"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pomjeranje desno"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Povećanje širine povećala"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Smanjenje širine povećala"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Povećanje visine povećala"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Smanjenje visine povećala"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prekidač za uvećavanje"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Uvećavanje prikaza preko cijelog ekrana"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećavanje dijela ekrana"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index ab2c25bc8262..fd49b461d1d8 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mou cap avall"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mou cap a l\'esquerra"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mou cap a la dreta"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Augmenta l\'amplada de la lupa"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Redueix l\'amplada de la lupa"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Augmenta l\'alçada de la lupa"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Redueix l\'alçada de la lupa"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Canvia al mode d\'ampliació"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Amplia la pantalla completa"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplia una part de la pantalla"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 9bde3bb1eadd..5992c6008e54 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Přesunout dolů"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Přesunout doleva"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Přesunout doprava"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Zvýšit šířku lupy"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Snížit šířku lupy"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Zvýšit výšku lupy"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Snížit výšku lupy"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Přepínač zvětšení"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Zvětšit celou obrazovku"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zvětšit část obrazovky"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 94ab18922558..15c13cd84eef 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Flyt ned"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Flyt til venstre"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Flyt til højre"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Forøg bredden på luppen"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Reducer bredden på luppen"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Forøg højden på luppen"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Reducer højden på luppen"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Skift forstørrelsestilstand"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Forstør hele skærmen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstør en del af skærmen"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 4ae8f27d1339..6424c629f812 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Nach unten bewegen"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Nach links bewegen"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Nach rechts bewegen"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Breite der Lupe erhöhen"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Breite der Lupe verringern"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Höhe der Lupe erhöhen"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Höhe der Lupe verringern"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Vergrößerungsschalter"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ganzen Bildschirm vergrößern"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Teil des Bildschirms vergrößern"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index b279abfa0423..029c7249e147 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -33,14 +33,14 @@ <string name="battery_saver_dismiss_action" msgid="7199342621040014738">"Όχι, ευχαριστώ"</string> <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Αυτόματη περιστροφή οθόνης"</string> <string name="usb_device_permission_prompt" msgid="4414719028369181772">"Να επιτρέπεται η πρόσβαση της εφαρμογής <xliff:g id="APPLICATION">%1$s</xliff:g> στη συσκευή <xliff:g id="USB_DEVICE">%2$s</xliff:g>;"</string> - <string name="usb_device_permission_prompt_warn" msgid="2309129784984063656">"Να επιτρέπεται στο <xliff:g id="APPLICATION">%1$s</xliff:g> να έχει πρόσβαση στη συσκευή <xliff:g id="USB_DEVICE">%2$s</xliff:g>;\nΔεν έχει εκχωρηθεί άδεια εγγραφής σε αυτήν την εφαρμογή, αλλά μέσω αυτής της συσκευής USB θα μπορεί να εγγράφει ήχο."</string> + <string name="usb_device_permission_prompt_warn" msgid="2309129784984063656">"Να επιτρέπεται στο <xliff:g id="APPLICATION">%1$s</xliff:g> να έχει πρόσβαση στη συσκευή <xliff:g id="USB_DEVICE">%2$s</xliff:g>;\nΔεν έχει εκχωρηθεί άδεια εγγραφής σε αυτή την εφαρμογή, αλλά μέσω αυτής της συσκευής USB θα μπορεί να εγγράφει ήχο."</string> <string name="usb_audio_device_permission_prompt_title" msgid="4221351137250093451">"Να επιτρέπεται στο <xliff:g id="APPLICATION">%1$s</xliff:g> η πρόσβαση στη συσκευή <xliff:g id="USB_DEVICE">%2$s</xliff:g>;"</string> <string name="usb_audio_device_confirm_prompt_title" msgid="8828406516732985696">"Άνοιγμα <xliff:g id="APPLICATION">%1$s</xliff:g> για διαχείριση συσκευής <xliff:g id="USB_DEVICE">%2$s</xliff:g>;"</string> - <string name="usb_audio_device_prompt_warn" msgid="2504972133361130335">"Δεν έχει εκχωρηθεί άδεια εγγραφής σε αυτήν την εφαρμογή, αλλά μέσω αυτής της συσκευής USB θα μπορεί να εγγράφει ήχο. Η χρήση της εφαρμογής <xliff:g id="APPLICATION">%1$s</xliff:g> με αυτήν τη συσκευή μπορεί να σας εμποδίσει να ακούσετε κλήσεις, ειδοποιήσεις και ξυπνητήρια."</string> + <string name="usb_audio_device_prompt_warn" msgid="2504972133361130335">"Δεν έχει εκχωρηθεί άδεια εγγραφής σε αυτή την εφαρμογή, αλλά μέσω αυτής της συσκευής USB θα μπορεί να εγγράφει ήχο. Η χρήση της εφαρμογής <xliff:g id="APPLICATION">%1$s</xliff:g> με αυτήν τη συσκευή μπορεί να σας εμποδίσει να ακούσετε κλήσεις, ειδοποιήσεις και ξυπνητήρια."</string> <string name="usb_audio_device_prompt" msgid="7944987408206252949">"Η χρήση της εφαρμογής <xliff:g id="APPLICATION">%1$s</xliff:g> με αυτήν τη συσκευή μπορεί να σας εμποδίσει να ακούσετε κλήσεις, ειδοποιήσεις και ξυπνητήρια."</string> <string name="usb_accessory_permission_prompt" msgid="717963550388312123">"Να επιτρέπεται η πρόσβαση της εφαρμογής <xliff:g id="APPLICATION">%1$s</xliff:g> στο αξεσουάρ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>;"</string> <string name="usb_device_confirm_prompt" msgid="4091711472439910809">"Να ανοίγει η εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g> για τη διαχείριση της συσκευής <xliff:g id="USB_DEVICE">%2$s</xliff:g>;"</string> - <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"Άνοιγμα της εφαρμογής <xliff:g id="APPLICATION">%1$s</xliff:g> για τον χειρισμό της συσκευής <xliff:g id="USB_DEVICE">%2$s</xliff:g>;\nΔεν έχει εκχωρηθεί άδεια εγγραφής σε αυτήν την εφαρμογή, αλλά μέσω αυτής της συσκευής USB θα μπορεί να εγγράφει ήχο."</string> + <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"Άνοιγμα της εφαρμογής <xliff:g id="APPLICATION">%1$s</xliff:g> για τον χειρισμό της συσκευής <xliff:g id="USB_DEVICE">%2$s</xliff:g>;\nΔεν έχει εκχωρηθεί άδεια εγγραφής σε αυτή την εφαρμογή, αλλά μέσω αυτής της συσκευής USB θα μπορεί να εγγράφει ήχο."</string> <string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"Να ανοίγει η εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g> για τη διαχείριση του αξεσουάρ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>;"</string> <string name="usb_accessory_uri_prompt" msgid="6756649383432542382">"Δεν έχετε εφαρμογή που να συνεργάζεται με το αξεσουάρ USB. Για περισσότερα: <xliff:g id="URL">%1$s</xliff:g>"</string> <string name="title_usb_accessory" msgid="1236358027511638648">"Αξεσουάρ USB"</string> @@ -413,7 +413,7 @@ <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Όταν κάνετε κοινή χρήση, εγγραφή ή μετάδοση, η εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> έχει πρόσβαση σε οτιδήποτε είναι ορατό στην οθόνη σας ή αναπαράγεται στη συσκευή σας. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string> <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Όταν κάνετε κοινή χρήση, εγγραφή ή μετάδοση μιας εφαρμογής, η εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στη συγκεκριμένη εφαρμογή. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string> <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Έναρξη"</string> - <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> απενεργοποίησε αυτήν την επιλογή"</string> + <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> απενεργοποίησε αυτή την επιλογή"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Έναρξη μετάδοσης;"</string> <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Όταν κάνετε μετάδοση, το Android έχει πρόσβαση σε οτιδήποτε είναι ορατό στην οθόνη σας ή αναπαράγεται στη συσκευή σας. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string> <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Όταν κάνετε μετάδοση μιας εφαρμογής, το Android έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στη συγκεκριμένη εφαρμογή. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string> @@ -495,7 +495,7 @@ <string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Ρυθμίσεις"</string> <string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Η ένταση ήχου μειώθηκε σε πιο ασφαλές επίπεδο"</string> <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Η ένταση ήχου των ακουστικών ήταν σε υψηλό επίπεδο για μεγαλύτερο διάστημα από αυτό που συνιστάται"</string> - <string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Η ένταση ήχου των ακουστικών ξεπέρασε το ασφαλές όριο για αυτήν την εβδομάδα"</string> + <string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Η ένταση ήχου των ακουστικών ξεπέρασε το ασφαλές όριο για αυτή την εβδομάδα"</string> <string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Συνέχιση ακρόασης"</string> <string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Μείωση έντασης ήχου"</string> <string name="screen_pinning_title" msgid="9058007390337841305">"Η εφαρμογή είναι καρφιτσωμένη."</string> @@ -506,8 +506,8 @@ <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Με αυτόν τον τρόπο, παραμένει σε προβολή μέχρι να το ξεκαρφιτσώσετε. Αγγίξτε παρατεταμένα το στοιχείο \"Αρχική οθόνη\" για ξεκαρφίτσωμα."</string> <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Τα προσωπικά δεδομένα ενδέχεται να είναι προσβάσιμα (όπως επαφές και περιεχόμενο μηνυμάτων ηλεκτρονικού ταχυδρομείου)."</string> <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Η καρφιτσωμένη εφαρμογή μπορεί να ανοίξει άλλες εφαρμογές."</string> - <string name="screen_pinning_toast" msgid="8177286912533744328">"Για να ξεκαρφιτσώσετε αυτήν την εφαρμογή, αγγίξτε παρατεταμένα τα κουμπιά Πίσω και Επισκόπηση."</string> - <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Για να ξεκαρφιτσώσετε αυτήν την εφαρμογή, αγγίξτε παρατεταμένα τα κουμπιά Πίσω και Αρχική οθόνη."</string> + <string name="screen_pinning_toast" msgid="8177286912533744328">"Για να ξεκαρφιτσώσετε αυτή την εφαρμογή, αγγίξτε παρατεταμένα τα κουμπιά Πίσω και Επισκόπηση."</string> + <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Για να ξεκαρφιτσώσετε αυτή την εφαρμογή, αγγίξτε παρατεταμένα τα κουμπιά Πίσω και Αρχική οθόνη."</string> <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Για να ξεκαρφ. την εφαρμογή, σύρετε προς τα πάνω και κρατήστε"</string> <string name="screen_pinning_positive" msgid="3285785989665266984">"Το κατάλαβα"</string> <string name="screen_pinning_negative" msgid="6882816864569211666">"Όχι"</string> @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Μετακίνηση κάτω"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Μετακίνηση αριστερά"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Μετακίνηση δεξιά"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Αύξηση πλάτους μεγεθυντικού φακού"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Μείωση πλάτους μεγεθυντικού φακού"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Αύξηση ύψους μεγεθυντικού φακού"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Μείωση ύψους μεγεθυντικού φακού"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Εναλλαγή μεγιστοποίησης"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Μεγέθυνση πλήρους οθόνης"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Μεγέθυνση μέρους της οθόνης"</string> @@ -1140,7 +1136,7 @@ <string name="log_access_confirmation_title" msgid="4843557604739943395">"Να επιτρέπεται στο <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> η πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής;"</string> <string name="log_access_confirmation_allow" msgid="752147861593202968">"Να επιτρέπεται η πρόσβαση για μία φορά"</string> <string name="log_access_confirmation_deny" msgid="2389461495803585795">"Να μην επιτρέπεται"</string> - <string name="log_access_confirmation_body" msgid="6883031912003112634">"Τα αρχεία καταγραφής συσκευής καταγράφουν ό,τι συμβαίνει στη συσκευή σας. Οι εφαρμογές μπορούν να χρησιμοποιούν αυτά τα αρχεία καταγραφής για να εντοπίζουν και να διορθώνουν ζητήματα.\n\nΟρισμένα αρχεία καταγραφής ενδέχεται να περιέχουν ευαίσθητες πληροφορίες. Ως εκ τούτου, επιτρέψτε την πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής μόνο στις εφαρμογές που εμπιστεύεστε. \n\nΕάν δεν επιτρέψετε σε αυτήν την εφαρμογή την πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής, η εφαρμογή εξακολουθεί να έχει πρόσβαση στα δικά της αρχεία καταγραφής. Ο κατασκευαστής της συσκευής σας ενδέχεται να εξακολουθεί να έχει πρόσβαση σε ορισμένα αρχεία καταγραφής ή ορισμένες πληροφορίες στη συσκευή σας."</string> + <string name="log_access_confirmation_body" msgid="6883031912003112634">"Τα αρχεία καταγραφής συσκευής καταγράφουν ό,τι συμβαίνει στη συσκευή σας. Οι εφαρμογές μπορούν να χρησιμοποιούν αυτά τα αρχεία καταγραφής για να εντοπίζουν και να διορθώνουν ζητήματα.\n\nΟρισμένα αρχεία καταγραφής ενδέχεται να περιέχουν ευαίσθητες πληροφορίες. Ως εκ τούτου, επιτρέψτε την πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής μόνο στις εφαρμογές που εμπιστεύεστε. \n\nΕάν δεν επιτρέψετε σε αυτή την εφαρμογή την πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής, η εφαρμογή εξακολουθεί να έχει πρόσβαση στα δικά της αρχεία καταγραφής. Ο κατασκευαστής της συσκευής σας ενδέχεται να εξακολουθεί να έχει πρόσβαση σε ορισμένα αρχεία καταγραφής ή ορισμένες πληροφορίες στη συσκευή σας."</string> <string name="log_access_confirmation_learn_more" msgid="3134565480986328004">"Μάθετε περισσότερα"</string> <string name="log_access_confirmation_learn_more_at" msgid="5635666259505215905">"Μάθετε περισσότερα στο <xliff:g id="URL">%s</xliff:g>."</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Άνοιγμα <xliff:g id="APPNAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index e036eb739358..4ae6ea4891dc 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Increase width of magnifier"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Decrease width of magnifier"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Increase height of magnifier"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Decrease height of magnifier"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index e036eb739358..4ae6ea4891dc 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Increase width of magnifier"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Decrease width of magnifier"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Increase height of magnifier"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Decrease height of magnifier"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index e036eb739358..4ae6ea4891dc 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Increase width of magnifier"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Decrease width of magnifier"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Increase height of magnifier"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Decrease height of magnifier"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 15b3001bee25..4cbe6a778654 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover hacia abajo"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover hacia la izquierda"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover hacia la derecha"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Aumentar el ancho de la lupa"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Reducir el ancho de la lupa"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Aumentar la altura de la lupa"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Reducir la altura de la lupa"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Interruptor de ampliación"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar pantalla completa"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string> @@ -913,7 +909,7 @@ <string name="controls_providers_title" msgid="6879775889857085056">"Elige la app para agregar los controles"</string> <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Se agregó # control.}many{Se agregaron # controles.}other{Se agregaron # controles.}}"</string> <string name="controls_removed" msgid="3731789252222856959">"Quitados"</string> - <string name="controls_panel_authorization_title" msgid="267429338785864842">"¿Quieres agregar <xliff:g id="APPNAME">%s</xliff:g>?"</string> + <string name="controls_panel_authorization_title" msgid="267429338785864842">"¿Quieres agregar a <xliff:g id="APPNAME">%s</xliff:g>?"</string> <string name="controls_panel_authorization" msgid="7045551688535104194">"<xliff:g id="APPNAME">%s</xliff:g> puede elegir qué controles y contenido mostrar aquí."</string> <string name="controls_panel_remove_app_authorization" msgid="5920442084735364674">"¿Quieres quitar los controles para <xliff:g id="APPNAME">%s</xliff:g>?"</string> <string name="accessibility_control_favorite" msgid="8694362691985545985">"Está en favoritos"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index fa435363b02f..73af8681502c 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover hacia abajo"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover hacia la izquierda"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover hacia la derecha"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Aumentar la anchura de la lupa"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Disminuir la anchura de la lupa"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Aumentar la altura de la lupa"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Disminuir la altura de la lupa"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Botón para cambiar el modo de ampliación"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar pantalla completa"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index ad80a590f06e..d2b636c96be0 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Teisalda alla"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Teisalda vasakule"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Teisalda paremale"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Luubi laiuse suurendamine"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Luubi laiuse vähendamine"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Luubi kõrguse suurendamine"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Luubi kõrguse vähendamine"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Suurenduse lüliti"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Täisekraani suurendamine"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekraanikuva osa suurendamine"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index b417c10c4063..37ced048e31e 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Eraman behera"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Eraman ezkerrera"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Eraman eskuinera"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Handitu luparen zabalera"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Murriztu luparen zabalera"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Handitu luparen altuera"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Murriztu luparen altuera"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Lupa aplikatzeko botoia"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Handitu pantaila osoa"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Handitu pantailaren zati bat"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 1135816ff494..7f419c117c46 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"انتقال به پایین"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"انتقال به راست"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"انتقال به چپ"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"افزایش پهنای ذرهبین"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"کاهش پهنای ذرهبین"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"افزایش ارتفاع ذرهبین"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"کاهش ارتفاع ذرهبین"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"کلید درشتنمایی"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"درشتنمایی تمامصفحه"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"درشتنمایی بخشی از صفحه"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index eef5a47e0fe4..0ecfe762b035 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Siirrä alas"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Siirrä vasemmalle"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Siirrä oikealle"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Kasvata suurennuslasin leveyttä"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Vähennä suurennuslasin leveyttä"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Kasvata suurennuslasin korkeutta"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Vähennä suurennuslasin korkeutta"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Suurennusvalinta"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Koko näytön suurennus"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Suurenna osa näytöstä"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index f4f41055b9bd..e305cc420827 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Déplacer vers le bas"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Déplacer vers la gauche"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Déplacer vers la droite"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Augmenter la largeur de la loupe"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Diminuer la largeur de la loupe"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Augmenter la hauteur de la loupe"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Diminuer la hauteur de la loupe"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Commutateur d\'agrandissement"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Agrandir la totalité de l\'écran"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 81a8363dba60..75662f6d100a 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Déplacer vers le bas"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Déplacer vers la gauche"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Déplacer vers la droite"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Augmenter la largeur de la loupe"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Diminuer la largeur de la loupe"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Augmenter la hauteur de la loupe"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Diminuer la hauteur de la loupe"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Changer de mode d\'agrandissement"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Agrandir tout l\'écran"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 509de37e262b..57d27ee4c287 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover cara abaixo"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover cara á esquerda"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover cara á dereita"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Aumentar a largura da lupa"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Reducir a largura da lupa"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Aumentar a altura da lupa"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Reducir a altura da lupa"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Interruptor do modo de ampliación"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar pantalla completa"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplía parte da pantalla"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 55fa871a75c3..7b42c4f9b279 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"નીચે ખસેડો"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ડાબી બાજુ ખસેડો"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"જમણી બાજુ ખસેડો"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"મેગ્નિફાયરની પહોળાઈ વધારો"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"મેગ્નિફાયરની પહોળાઈ ઘટાડો"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"મેગ્નિફાયરની ઊંચાઈ વધારો"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"મેગ્નિફાયરની ઊંચાઈ ઘટાડો"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"મોટું કરવાની સુવિધાવાળી સ્વિચ"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"પૂર્ણ સ્ક્રીનને મોટી કરો"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"સ્ક્રીનનો કોઈ ભાગ મોટો કરો"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 173c6073d9ae..6cc896f265a5 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -428,7 +428,7 @@ <string name="media_projection_task_switcher_notification_channel" msgid="7613206306777814253">"ऐप्लिकेशन स्विच करें"</string> <string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"आपके आईटी एडमिन ने स्क्रीन कैप्चर करने की सुविधा पर रोक लगाई है"</string> <string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"डिवाइस से जुड़ी नीति के तहत स्क्रीन कैप्चर करने की सुविधा बंद है"</string> - <string name="clear_all_notifications_text" msgid="348312370303046130">"सभी को हटाएं"</string> + <string name="clear_all_notifications_text" msgid="348312370303046130">"सभी हटाएं"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"मैनेज करें"</string> <string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string> <string name="notification_section_header_incoming" msgid="850925217908095197">"नई सूचनाएं"</string> @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"नीचे ले जाएं"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"बाईं ओर ले जाएं"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"दाईं ओर ले जाएं"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"कॉन्टेंट को बड़ा करके दिखाने वाली विंडो की चौड़ाई को ज़्यादा करें"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"कॉन्टेंट को बड़ा करके दिखाने वाली विंडो की चौड़ाई को कम करें"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"कॉन्टेंट को बड़ा करके दिखाने वाली विंडो की लंबाई को ज़्यादा करें"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"कॉन्टेंट को बड़ा करके दिखाने वाली विंडो की लंबाई को कम करें"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ज़ूम करने की सुविधा वाला स्विच"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"फ़ुल स्क्रीन को ज़ूम करें"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीन के किसी हिस्से को ज़ूम करें"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 42acd4855e76..1dde67849b84 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Premjesti dolje"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Premjesti ulijevo"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Premjesti udesno"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Povećaj širinu povećala"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Smanji širinu povećala"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Povećaj visinu povećala"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Smanji visinu povećala"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prebacivanje povećavanja"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Povećajte cijeli zaslon"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povećaj dio zaslona"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index e27733f02c29..15c61a853941 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mozgatás lefelé"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mozgatás balra"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mozgatás jobbra"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Nagyító szélességének növelése"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Nagyító szélességének csökkentése"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Nagyító magasságának növelése"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Nagyító magasságának csökkentése"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Nagyításváltó"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"A teljes képernyő felnagyítása"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Képernyő bizonyos részének nagyítása"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index d2a5d751b5ab..c9242b8c8d29 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Տեղափոխել ներքև"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Տեղափոխել ձախ"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Տեղափոխել աջ"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Ավելացնել խոշորացույցի լայնությունը"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Պակասեցնել խոշորացույցի լայնությունը"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Ավելացնել խոշորացույցի բարձրությունը"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Պակասեցնել խոշորացույցի բարձրությունը"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Խոշորացման փոփոխություն"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Խոշորացնել ամբողջ էկրանը"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Խոշորացնել էկրանի որոշակի հատվածը"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index f908c33533a9..bdcb702ee2d0 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pindahkan ke bawah"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pindahkan ke kiri"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pindahkan ke kanan"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Tambahi lebar jendela pembesaran"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Kurangi lebar jendela pembesaran"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Tambah tinggi jendela pembesaran"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Kurangi tinggi jendela pembesaran"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Tombol pembesaran"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Memperbesar tampilan layar penuh"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Perbesar sebagian layar"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 1fb1304daf7f..e6122ccbf37a 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Færa niður"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Færa til vinstri"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Færa til hægri"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Breikka stækkunarglugga"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Þrengja stækkunarglugga"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Hækka stækkunarglugga"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Lækka stækkunarglugga"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Stækkunarrofi"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Stækka allan skjáinn"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Stækka hluta skjásins"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index bab86e37654d..e4e591e4ab81 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Sposta giù"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sposta a sinistra"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sposta a destra"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Aumenta la larghezza della finestra ingrandimento"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Riduci la larghezza della finestra ingrandimento"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Aumenta l\'altezza della finestra ingrandimento"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Riduci l\'altezza della finestra ingrandimento"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Opzione Ingrandimento"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ingrandisci l\'intero schermo"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ingrandisci parte dello schermo"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 294f77f78e73..2e5d27e4dc68 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"הזזה למטה"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"הזזה שמאלה"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"הזזה ימינה"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"הגדלת רוחב זכוכית המגדלת"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"הקטנת רוחב זכוכית המגדלת"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"הגדלת גובה זכוכית המגדלת"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"הקטנת גובה זכוכית המגדלת"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"מעבר למצב הגדלה"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"הגדלה של המסך המלא"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"הגדלת חלק מהמסך"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 9daaabcf7352..888a04684453 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"下に移動"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"左に移動"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"右に移動"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"拡大鏡の幅を広くする"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"拡大鏡の幅を狭くする"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"拡大鏡の高さを高くする"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"拡大鏡の高さを低くする"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"拡大スイッチ"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"画面全体を拡大します"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"画面の一部を拡大します"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 4d3f4c739fe1..ca847593c642 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ქვემოთ გადატანა"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"მარცხნივ გადატანა"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"მარჯვნივ გადატანა"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"გამადიდებლის სიგანის გაზრდა"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"გამადიდებლის სიგანის შემცირება"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"გამადიდებლის სიმაღლის გაზრდა"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"გამადიდებლის სიმაღლის შემცირება"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"გადიდების გადართვა"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"გაადიდეთ სრულ ეკრანზე"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ეკრანის ნაწილის გადიდება"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 0e54c222f332..6ee4b604ac26 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Төмен қарай жылжыту"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Солға жылжыту"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Оңға жылжыту"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Ұлғайтқыштың енін арттыру"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Ұлғайтқыштың енін азайту"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Ұлғайтқыштың биіктігін арттыру"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Ұлғайтқыштың биіктігін азайту"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Ұлғайту режиміне ауыстырғыш"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Толық экранды ұлғайту"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экранның бөлігін ұлғайту"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 564af36ec4e2..6fae9476e7c8 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ផ្លាស់ទីចុះក្រោម"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ផ្លាស់ទីទៅឆ្វេង"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"ផ្លាស់ទីទៅស្តាំ"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"បង្កើនទទឹងនៃកែវពង្រីក"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"បន្ថយទទឹងនៃកែវពង្រីក"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"បង្កើនកម្ពស់នៃកែវពង្រីក"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"បន្ថយកម្ពស់នៃកែវពង្រីក"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ប៊ូតុងបិទបើកការពង្រីក"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ពង្រីកពេញអេក្រង់"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ពង្រីកផ្នែកនៃអេក្រង់"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 65162130470b..4e8c72834746 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ಕೆಳಗೆ ಸರಿಸಿ"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ಎಡಕ್ಕೆ ಸರಿಸಿ"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"ಬಲಕ್ಕೆ ಸರಿಸಿ"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"ಮ್ಯಾಗ್ನಿಫೈಯರ್ ಅಗಲವನ್ನು ಹೆಚ್ಚಿಸಿ"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"ಮ್ಯಾಗ್ನಿಫೈಯರ್ ಅಗಲವನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"ಮ್ಯಾಗ್ನಿಫೈಯರ್ ಎತ್ತರವನ್ನು ಹೆಚ್ಚಿಸಿ"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"ಮ್ಯಾಗ್ನಿಫೈಯರ್ ಎತ್ತರವನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ಝೂಮ್ ಮಾಡುವ ಸ್ವಿಚ್"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಹಿಗ್ಗಿಸಿ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ಸ್ಕ್ರೀನ್ನ ಅರ್ಧಭಾಗವನ್ನು ಝೂಮ್ ಮಾಡಿ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 62d6bd61216c..9bf691f3e659 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"아래로 이동"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"왼쪽으로 이동"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"오른쪽으로 이동"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"확대창 너비 늘리기"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"확대창 너비 줄이기"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"확대창 높이 늘리기"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"확대창 높이 줄이기"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"확대 전환"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"전체 화면 확대"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"화면 일부 확대"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 2eddca253a25..de96264db4bf 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Төмөн жылдыруу"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Солго жылдыруу"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Оңго жылдыруу"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Чоңойткучтун туурасын көбөйтүү"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Чоңойткучтун туурасын азайтуу"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Чоңойткучтун бийиктигин көбөйтүү"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Чоңойткучтун бийиктигин азайтуу"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Чоңойтуу режимине которулуу"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Толук экранда ачуу"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экрандын бир бөлүгүн чоңойтуу"</string> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 259b9adf0209..cfb40171cfc1 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -89,6 +89,9 @@ <dimen name="global_actions_button_size">72dp</dimen> <dimen name="global_actions_button_padding">26dp</dimen> + <!-- scroll view the size of 2 channel rows --> + <dimen name="notification_blocker_channel_list_height">128dp</dimen> + <dimen name="keyguard_indication_margin_bottom">8dp</dimen> <dimen name="lock_icon_margin_bottom">24dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 3f3cc27b4a32..29dc4a9e8047 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ຍ້າຍລົງ"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ຍ້າຍໄປຊ້າຍ"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"ຍ້າຍໄປຂວາ"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"ເພີ່ມຄວາມກວ້າງຂອງແວ່ນຂະຫຍາຍ"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"ຫຼຸດຄວາມກວ້າງຂອງແວ່ນຂະຫຍາຍ"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"ເພີ່ມຄວາມສູງຂອງແວ່ນຂະຫຍາຍ"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"ຫຼຸດຄວາມສູງຂອງແວ່ນຂະຫຍາຍ"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ສະຫຼັບການຂະຫຍາຍ"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ຂະຫຍາຍເຕັມຈໍ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ຂະຫຍາຍບາງສ່ວນຂອງໜ້າຈໍ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 808c732c49c8..bf647e61a639 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Perkelti žemyn"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Perkelti kairėn"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Perkelti dešinėn"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Padidinti didintuvo plotį"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Sumažinti didintuvo plotį"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Padidinti didintuvo aukštį"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Sumažinti didintuvo aukštį"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Didinimo jungiklis"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Viso ekrano didinimas"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Didinti ekrano dalį"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 52ccc16e0ac7..45d4ca2e6318 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pārvietot uz leju"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pārvietot pa kreisi"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pārvietot pa labi"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Palielināt lupas loga platumu"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Samazināt lupas loga platumu"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Palielināt lupas loga augstumu"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Samazināt lupas loga augstumu"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Palielinājuma slēdzis"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Palielināt visu ekrānu"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Palielināt ekrāna daļu"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 7d218baff314..8a870b68b396 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Премести надолу"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Премести налево"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Премести надесно"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Зголемете ја ширината на лупата"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Намалете ја ширината на лупата"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Зголемете ја висината на лупата"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Намалете ја висината на лупата"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Прекинувач за зголемување"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Зголемете го целиот екран"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Зголемувајте дел од екранот"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index b5feddbd5068..8b8fd8ca1238 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"താഴേക്ക് നീക്കുക"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ഇടത്തേക്ക് നീക്കുക"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"വലത്തേക്ക് നീക്കുക"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"മാഗ്നിഫയറിന്റെ വീതി കൂട്ടുക"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"മാഗ്നിഫയറിന്റെ വീതി കുറയ്ക്കുക"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"മാഗ്നിഫയറിന്റെ ഉയരം കൂട്ടുക"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"മാഗ്നിഫയറിന്റെ ഉയരം കുറയ്ക്കുക"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"മാഗ്നിഫിക്കേഷൻ മോഡ് മാറുക"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"സ്ക്രീൻ പൂർണ്ണമായും മാഗ്നിഫൈ ചെയ്യുക"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"സ്ക്രീനിന്റെ ഭാഗം മാഗ്നിഫൈ ചെയ്യുക"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 4bf4f6ae6443..3f3350ad9bbf 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Доош зөөх"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Зүүн тийш зөөх"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Баруун тийш зөөх"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Томруулагчийн өргөнийг ихэсгэх"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Томруулагчийн өргөнийг багасгах"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Томруулагчийн өндрийг ихэсгэх"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Томруулагчийн өндрийг багасгах"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Томруулах сэлгэлт"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Бүтэн дэлгэцийг томруулах"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Дэлгэцийн нэг хэсгийг томруулах"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index eb30836b0460..0fbdf47d8241 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"खाली हलवा"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"डावीकडे हलवा"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"उजवीकडे हलवा"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"मॅग्निफायरच्या विंडोची रुंदी वाढवा"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"मॅग्निफायरच्या विंडोची रुंदी कमी करा"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"मॅग्निफायरच्या विंडोची उंची वाढवा"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"मॅग्निफायरच्या विंडोची उंची कमी करा"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"मॅग्निफिकेशन स्विच"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"फुल स्क्रीन मॅग्निफाय करा"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index f57a66517274..4275d110fcf6 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Alih ke bawah"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Alih ke kiri"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Alih ke kanan"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Tingkatkan lebar penggadang"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Kurangkan lebar penggadang"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Tingkatkan tinggi penggadang"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Kurangkan tinggi penggadang"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Suis pembesaran"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Besarkan skrin penuh"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Besarkan sebahagian skrin"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index bfb43ff2eae4..b6b0e7fc189f 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -668,8 +668,8 @@ <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_cycle_forward" msgid="9202444850838205990">"မကြာသေးမီကအက်ပ်များ ရှာဖွေကြည့်ရှုရန် (ရှေ့သို့)"</string> + <string name="group_system_cycle_back" msgid="5163464503638229131">"မကြာသေးမီကအက်ပ်များ ရှာဖွေကြည့်ရှုရန် (နောက်သို့)"</string> <string name="group_system_access_all_apps_search" msgid="488070738028991753">"အက်ပ်အားလုံးစာရင်းကို ဝင်ကြည့်ပြီး ရှာပါ (ဥပမာ- Search/Launcher)"</string> <string name="group_system_hide_reshow_taskbar" msgid="3809304065624351131">"လုပ်ဆောင်စရာဘားကို ဖျောက်ထားပြီး ပြန်ပြရန်"</string> <string name="group_system_access_system_settings" msgid="7961639365383008053">"စက်စနစ်ဆက်တင်များ ဝင်ကြည့်ရန်"</string> @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"အောက်သို့ရွှေ့ရန်"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ဘယ်ဘက်သို့ရွှေ့ရန်"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"ညာဘက်သို့ရွှေ့ရန်"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"မှန်ဘီလူးအကျယ်ကို တိုးရန်"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"မှန်ဘီလူးအကျယ်ကို လျှော့ရန်"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"မှန်ဘီလူးအမြင့်ကို တိုးရန်"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"မှန်ဘီလူးအမြင့်ကို လျှော့ရန်"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ချဲ့ရန် ခလုတ်"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ဖန်သားပြင်အပြည့် ချဲ့သည်"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ဖန်သားပြင် တစ်စိတ်တစ်ပိုင်းကို ချဲ့ပါ"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 6923671c399b..aef9f58eca54 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Flytt ned"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Flytt til venstre"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Flytt til høyre"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Øk bredden på forstørrelsen"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Reduser bredden på forstørrelsen"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Øk høyden på forstørrelsen"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Reduser høyden på forstørrelsen"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Forstørringsbryter"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Forstørr hele skjermen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstørr en del av skjermen"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 934718449155..4cae93fc49e8 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"तल सार्नुहोस्"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"बायाँ सार्नुहोस्"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"दायाँ सार्नुहोस्"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"म्याग्निफायरको चौडाइ बढाउनुहोस्"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"म्याग्निफायरको चौडाइ घटाउनुहोस्"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"म्याग्निफायरको उचाइ बढाउनुहोस्"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"म्याग्निफायरको उचाइ घटाउनुहोस्"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"म्याग्निफिकेसन स्विच"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"पूरै स्क्रिन जुम इन गर्नुहोस्"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रिनको केही भाग म्याग्निफाइ गर्नुहोस्"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index a42337f895c7..d71a4e3d1342 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Omlaag verplaatsen"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Naar links verplaatsen"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Naar rechts verplaatsen"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Breedte van vergrootglas vergroten"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Breedte van vergrootglas verkleinen"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Hoogte van vergrootglas vergroten"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Hoogte van vergrootglas verkleinen"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Vergrotingsschakelaar"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Volledig scherm vergroten"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Deel van het scherm vergroten"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 24aef78e987b..a8da287d07bc 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -105,7 +105,7 @@ <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"ଆପଣ ଏକ ଆପ ରେକର୍ଡ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ Androidର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string> <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ରେକର୍ଡିଂ ଆରମ୍ଭ କରନ୍ତୁ"</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"ଅଡିଓ ରେକର୍ଡ କରନ୍ତୁ"</string> - <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"ଡିଭାଇସ୍ ଅଡିଓ"</string> + <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"ଡିଭାଇସ ଅଡିଓ"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"ମ୍ୟୁଜିକ, କଲ ଏବଂ ରିଂଟୋନଗୁଡ଼ିକ ପରି ଆପଣଙ୍କ ଡିଭାଇସରୁ ସାଉଣ୍ଡ"</string> <string name="screenrecord_mic_label" msgid="2111264835791332350">"ମାଇକ୍ରୋଫୋନ"</string> <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"ଡିଭାଇସ୍ ଅଡିଓ ଏବଂ ମାଇକ୍ରୋଫୋନ୍"</string> @@ -298,8 +298,8 @@ <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ଚେତାବନୀ"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"ୱାର୍କ ଆପ୍ସ"</string> <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"ବିରତ କରାଯାଇଛି"</string> - <string name="quick_settings_night_display_label" msgid="8180030659141778180">"ନାଇଟ୍ ଲାଇଟ୍"</string> - <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ସୂର୍ଯ୍ୟାସ୍ତ ବେଳେ ଅନ୍ ହେବ"</string> + <string name="quick_settings_night_display_label" msgid="8180030659141778180">"ନାଇଟ ଲାଇଟ"</string> + <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ସନ୍ଧ୍ୟାରେ ଚାଲୁ ହେବ"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ସୂର୍ଯ୍ୟୋଦୟ ପର୍ଯ୍ୟନ୍ତ"</string> <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"<xliff:g id="TIME">%s</xliff:g>ରେ ଅନ୍ ହେବ"</string> <string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string> @@ -698,7 +698,7 @@ <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</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> + <string name="battery" msgid="769686279459897127">"ବେଟେରୀ"</string> <string name="headset" msgid="4485892374984466437">"ହେଡସେଟ୍"</string> <string name="accessibility_long_click_tile" msgid="210472753156768705">"ସେଟିଂସ୍ ଖୋଲନ୍ତୁ"</string> <string name="accessibility_status_bar_headphones" msgid="1304082414912647414">"ହେଡଫୋନ୍ ସଂଯୁକ୍ତ"</string> @@ -793,7 +793,7 @@ <string name="tuner_menu" msgid="363690665924769420">"ମେନୁ"</string> <string name="tuner_app" msgid="6949280415826686972">"<xliff:g id="APP">%1$s</xliff:g> ଆପ୍"</string> <string name="notification_channel_alerts" msgid="3385787053375150046">"ଆଲର୍ଟଗୁଡ଼ିକ"</string> - <string name="notification_channel_battery" msgid="9219995638046695106">"ବ୍ୟାଟେରୀ"</string> + <string name="notification_channel_battery" msgid="9219995638046695106">"ବେଟେରୀ"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"ସ୍କ୍ରୀନଶଟ୍"</string> <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> <string name="notification_channel_setup" msgid="7660580986090760350">"ସେଟଅପ"</string> @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ତଳକୁ ମୁଭ୍ କରନ୍ତୁ"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ବାମକୁ ମୁଭ୍ କରନ୍ତୁ"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"ଡାହାଣକୁ ମୁଭ୍ କରନ୍ତୁ"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"ମେଗ୍ନିଫାୟରର ପ୍ରସ୍ଥ ବଢ଼ାନ୍ତୁ"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"ମେଗ୍ନିଫାୟରର ପ୍ରସ୍ଥ କମାନ୍ତୁ"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"ମେଗ୍ନିଫାୟରର ଉଚ୍ଚତା ବଢ଼ାନ୍ତୁ"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"ମେଗ୍ନିଫାୟରର ଉଚ୍ଚତା କମାନ୍ତୁ"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ସ୍ୱିଚ୍"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ମ୍ୟାଗ୍ନିଫାଏ କରନ୍ତୁ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ସ୍କ୍ରିନର ଅଂଶ ମାଗ୍ନିଫାଏ କରନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 2ebc398fe7ce..1d4c5bd4d371 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ਹੇਠਾਂ ਲਿਜਾਓ"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ਖੱਬੇ ਲਿਜਾਓ"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"ਸੱਜੇ ਲਿਜਾਓ"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"ਵੱਡਦਰਸ਼ੀ ਵਿੰਡੋ ਦੀ ਚੌੜਾਈ ਵਧਾਓ"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"ਵੱਡਦਰਸ਼ੀ ਵਿੰਡੋ ਦੀ ਚੌੜਾਈ ਘਟਾਓ"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"ਵੱਡਦਰਸ਼ੀ ਵਿੰਡੋ ਦੀ ਉਚਾਈ ਵਧਾਓ"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"ਵੱਡਦਰਸ਼ੀ ਵਿੰਡੋ ਦੀ ਉਚਾਈ ਘਟਾਓ"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਸਵਿੱਚ"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਵੱਡਦਰਸ਼ੀ ਕਰੋ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ਸਕ੍ਰੀਨ ਦੇ ਹਿੱਸੇ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index b495a5768b4b..47912cbaa342 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Przesuń w dół"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Przesuń w lewo"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Przesuń w prawo"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Zwiększ szerokość lupy"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Zmniejsz szerokość lupy"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Zwiększ wysokość lupy"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Zmniejsz wysokość lupy"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Przełączanie powiększenia"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Powiększanie pełnego ekranu"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Powiększ część ekranu"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index abfc9412b904..104ad392d810 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Aumentar a largura da lupa"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Diminuir a largura da lupa"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Aumentar a altura da lupa"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Diminuir a altura da lupa"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Chave de ampliação"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar toda a tela"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 85cd09aa3f5d..c7dba2e9d5be 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Aumentar largura da lupa"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Diminuir largura da lupa"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Aumentar altura da lupa"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Diminuir altura da lupa"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Interruptor de ampliação"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar o ecrã inteiro"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte do ecrã"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index abfc9412b904..104ad392d810 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Aumentar a largura da lupa"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Diminuir a largura da lupa"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Aumentar a altura da lupa"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Diminuir a altura da lupa"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Chave de ampliação"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar toda a tela"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 1f57dfe09d64..f850708436e0 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mută în jos"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mută la stânga"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mută spre dreapta"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Crește lățimea lupei"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Redu lățimea lupei"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Crește înălțimea lupei"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Redu înălțimea lupei"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Comutator de mărire"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Mărește tot ecranul"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Mărește o parte a ecranului"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 1ea9d34e548f..060cb8880439 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Переместить вниз"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Переместить влево"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Переместить вправо"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Увеличить ширину лупы"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Уменьшить ширину лупы"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Увеличить высоту лупы"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Уменьшить высоту лупы"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Переключатель режима увеличения"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Увеличение всего экрана"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличить часть экрана"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 3f2978cbac52..1d860d7ff188 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"පහළට ගෙන යන්න"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"වමට ගෙන යන්න"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"දකුණට ගෙන යන්න"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"විශාලකයෙහි පළල වැඩි කරන්න"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"විශාලකයෙහි පළල අඩු කරන්න"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"විශාලකයෙහි උස වැඩි කරන්න"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"විශාලකයෙහි උස අඩු කරන්න"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"විශාලන ස්විචය"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"පූර්ණ තිරය විශාලනය කරන්න"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"තිරයේ කොටසක් විශාලනය කරන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index ba94b1af65cd..81b3a2f8e7e2 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Posunúť nadol"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Posunúť doľava"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Posunúť doprava"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Zväčšiť šírku lupy"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Zmenšiť šírku lupy"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Zväčšiť výšku lupy"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Zmenšiť výšku lupy"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prepínač zväčenia"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Zväčšenie celej obrazovky"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zväčšiť časť obrazovky"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 8baa3b6a107d..f71f72f76721 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Premakni navzdol"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Premakni levo"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Premakni desno"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Povečanje širine povečevalnika"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Zmanjšanje širine povečevalnika"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Povečanje višine povečevalnika"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Zmanjšanje višine povečevalnika"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Stikalo za povečavo"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Povečanje celotnega zaslona"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povečava dela zaslona"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index ec1a6ccd11ac..c7b5a774947f 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Lëvize poshtë"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Lëvize majtas"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Lëvize djathtas"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Rrit gjerësinë e zmadhuesit"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Ul gjerësinë e zmadhuesit"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Rrit lartësinë e zmadhuesit"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Ul lartësinë e zmadhuesit"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Ndërrimi i zmadhimit"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Zmadho ekranin e plotë"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zmadho një pjesë të ekranit"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index fc67121e7b5d..2e813c554328 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Померите надоле"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Померите налево"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Померите надесно"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Повећајте ширину лупе"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Смањите ширину лупе"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Повећајте висину лупе"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Смањите висину лупе"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Прелазак на други режим увећања"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Увећајте цео екран"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увећајте део екрана"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 98c830189526..35da63a5e414 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Flytta nedåt"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Flytta åt vänster"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Flytta åt höger"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Öka bredden på förstoringen"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Minska bredden på förstoringen"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Öka höjden på förstoringen"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Minska höjden på förstoringen"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Förstoringsreglage"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Förstora hela skärmen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Förstora en del av skärmen"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 0bc5750d9a77..12eb1ae43251 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Sogeza chini"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sogeza kushoto"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sogeza kulia"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Ongeza upana wa kikuzaji"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Punguza upana wa kikuzaji"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Ongeza urefu wa kikuzaji"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Punguza urefu wa kikuzaji"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Swichi ya ukuzaji"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Kuza skrini nzima"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Kuza sehemu ya skrini"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index f3954d6a06c2..7b30d5cb3289 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"கீழே நகர்த்து"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"இடப்புறம் நகர்த்து"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"வலப்புறம் நகர்த்து"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"பெரிதாக்கும் கருவியின் அகலத்தை அதிகரி"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"பெரிதாக்கும் கருவியின் அகலத்தைக் குறை"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"பெரிதாக்கும் கருவியின் உயரத்தை அதிகரி"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"பெரிதாக்கும் கருவியின் உயரத்தைக் குறை"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"பெரிதாக்கல் ஸ்விட்ச்"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"முழுத்திரையைப் பெரிதாக்கும்"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"திரையின் ஒரு பகுதியைப் பெரிதாக்கும்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 1d221f42c852..4989fe938a94 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"కిందకి పంపండి"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ఎడమవైపుగా జరపండి"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"కుడివైపుగా జరపండి"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"మాగ్నిఫైయర్ వెడల్పును పెంచండి"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"మాగ్నిఫైయర్ వెడల్పును తగ్గించండి"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"మాగ్నిఫైయర్ ఎత్తును పెంచండి"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"మాగ్నిఫైయర్ ఎత్తును తగ్గించండి"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"మ్యాగ్నిఫికేషన్ స్విచ్"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ఫుల్ స్క్రీన్ను మ్యాగ్నిఫై చేయండి"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"స్క్రీన్లో భాగాన్ని మ్యాగ్నిఫై చేయండి"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index df4ab83ee5b1..70bdb1877dd0 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ย้ายลง"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ย้ายไปทางซ้าย"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"ย้ายไปทางขวา"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"เพิ่มความกว้างของแว่นขยาย"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"ลดความกว้างของแว่นขยาย"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"เพิ่มความสูงของแว่นขยาย"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"ลดความสูงของแว่นขยาย"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"เปลี่ยนโหมดการขยาย"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ขยายเป็นเต็มหน้าจอ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ขยายบางส่วนของหน้าจอ"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index a6535fdee5d5..fbf4506f2c7a 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Ibaba"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Ilipat pakaliwa"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Ilipat pakanan"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Dagdagan ang lapad ng magnifier"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Bawasan ang lapad ng magnifier"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Dagdagan ang taas ng magnifier"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Bawasan ang taas ng magnifier"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Switch ng pag-magnify"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"I-magnify ang buong screen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"I-magnify ang isang bahagi ng screen"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index a8a9ba4725f2..f82a92814847 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Aşağı taşı"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sola taşı"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sağa taşı"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Büyüteç genişliğini artır"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Büyüteç genişliğini azalt"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Büyüteç yüksekliğini artır"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Büyüteç yüksekliğini azalt"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Büyütme moduna geçin"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Tam ekran büyütme"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekranın bir parçasını büyütün"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 48c45ae68414..997be733d589 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Перемістити вниз"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Перемістити ліворуч"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Перемістити праворуч"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Збільшити ширину лупи"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Зменшити ширину лупи"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Збільшити висоту лупи"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Зменшити висоту лупи"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Перемикач режиму збільшення"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Збільшення всього екрана"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Збільшити частину екрана"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index de38ca103ac8..731517142cd8 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"نیچے منتقل کریں"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"بائیں منتقل کریں"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"دائیں منتقل کریں"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"میگنیفائر کی چوڑائی میں اضافہ کریں"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"میگنیفائر کی چوڑائی کو کم کریں"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"میگنیفائر کی اونچائی میں اضافہ کریں"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"میگنیفائر کی اونچائی کو کم کریں"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"میگنیفکیشن پر سوئچ کریں"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"فُل اسکرین کو بڑا کریں"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"اسکرین کا حصہ بڑا کریں"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index e5703b292a1d..8877d39cea83 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pastga siljitish"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Chapga siljitish"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Oʻngga siljitish"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Lupa kengligini oshiring"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Lupaning kengligini kamaytiring"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Lupa balandligini oshiring"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Lupa balandligini kamaytiring"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Kattalashtirish rejimini almashtirish"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ekranni toʻliq kattalashtirish"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran qismini kattalashtirish"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index f022aea90797..5990822502a3 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Di chuyển xuống"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Di chuyển sang trái"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Di chuyển sang phải"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Tăng chiều rộng của trình phóng to"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Giảm chiều rộng của trình phóng to"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Tăng chiều cao của trình phóng to"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Giảm chiều cao của trình phóng to"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Nút chuyển phóng to"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Phóng to toàn màn hình"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Phóng to một phần màn hình"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 6876b1cf623b..b9f79b1247a0 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"下移"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"左移"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"右移"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"增加放大镜宽度"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"缩减放大镜宽度"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"增加放大镜高度"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"缩减放大镜高度"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"切换放大模式"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"放大整个屏幕"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分屏幕"</string> @@ -1137,7 +1133,7 @@ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"未知"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string> <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string> - <string name="log_access_confirmation_title" msgid="4843557604739943395">"允许“<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>”访问所有设备日志吗?"</string> + <string name="log_access_confirmation_title" msgid="4843557604739943395">"要允许“<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>”访问所有设备日志吗?"</string> <string name="log_access_confirmation_allow" msgid="752147861593202968">"允许访问一次"</string> <string name="log_access_confirmation_deny" msgid="2389461495803585795">"不允许"</string> <string name="log_access_confirmation_body" msgid="6883031912003112634">"设备日志会记录设备上发生的活动。应用可以使用这些日志查找和修复问题。\n\n部分日志可能包含敏感信息,因此请仅允许您信任的应用访问所有设备日志。\n\n如果您不授予此应用访问所有设备日志的权限,它仍然可以访问自己的日志。您的设备制造商可能仍然能够访问设备上的部分日志或信息。"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 31f2a8532d0e..29ad1410a452 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"向下移"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"向左移"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"向右移"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"增加放大鏡闊度"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"縮窄放大鏡闊度"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"增加放大鏡高度"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"縮短放大鏡高度"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"放大開關"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"放大成個畫面"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分螢幕畫面"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 81ce876faf3c..723a6555b742 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"向下移"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"向左移"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"向右移"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"增加放大鏡寬度"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"縮減放大鏡寬度"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"增加放大鏡高度"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"縮減放大鏡高度"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"切換放大模式"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"放大整個螢幕畫面"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大局部螢幕畫面"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 63f32227d7e7..88a3994afd75 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -863,14 +863,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Yehlisa"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Yisa kwesokunxele"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Yisa kwesokudla"</string> - <!-- no translation found for accessibility_control_increase_window_width (6992249470832493283) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_width (5740401560105929681) --> - <skip /> - <!-- no translation found for accessibility_control_increase_window_height (2200966116612324260) --> - <skip /> - <!-- no translation found for accessibility_control_decrease_window_height (2054479949445332761) --> - <skip /> + <string name="accessibility_control_increase_window_width" msgid="6992249470832493283">"Khulisa ububanzi besibonakhulu"</string> + <string name="accessibility_control_decrease_window_width" msgid="5740401560105929681">"Khulisa ububanzi besibonakhulu"</string> + <string name="accessibility_control_increase_window_height" msgid="2200966116612324260">"Khulisa ubude besibonakhulu"</string> + <string name="accessibility_control_decrease_window_height" msgid="2054479949445332761">"Nciphisa ubude besibonakhulu"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Iswishi yokukhulisa"</string> <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Khulisa isikrini esigcwele"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Khulisa ingxenye eyesikrini"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index c72f5657176b..b6bca65b8174 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -70,6 +70,16 @@ <!-- The number of rows in the QuickSettings --> <integer name="quick_settings_max_rows">4</integer> + <!-- Override column number for quick settings. + For now, this value has effect only when flag lockscreen.enable_landscape is enabled. + TODO (b/293252410) - change this comment/resource when flag is enabled --> + <integer name="small_land_lockscreen_quick_settings_num_columns">2</integer> + + <!-- Override row number for quick settings. + For now, this value has effect only when flag lockscreen.enable_landscape is enabled. + TODO (b/293252410) - change this comment/resource when flag is enabled --> + <integer name="small_land_lockscreen_quick_settings_max_rows">2</integer> + <!-- If the dp width of the available space is <= this value, potentially adjust the number of media recommendation items--> <integer name="default_qs_media_rec_width_dp">380</integer> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 7e9d3f59369b..0ee5da22a31b 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -313,6 +313,8 @@ <!-- The space around a notification menu item --> <dimen name="notification_menu_icon_padding">20dp</dimen> + <!-- scroll view the size of 3 channel rows --> + <dimen name="notification_blocker_channel_list_height">192dp</dimen> <!-- The vertical space around the buttons in the inline settings --> <dimen name="notification_guts_button_spacing">12dp</dimen> @@ -545,7 +547,7 @@ <!-- (volume_dialog_panel_width - rounded_slider_icon_size) / 2 --> <dimen name="volume_slider_icon_inset">11dp</dimen> - <dimen name="volume_dialog_track_width">4dp</dimen> + <dimen name="volume_dialog_track_width">40dp</dimen> <dimen name="volume_dialog_track_corner_radius">2dp</dimen> @@ -1643,6 +1645,11 @@ <!-- Radius of switch track --> <dimen name="settingslib_switch_track_radius">35dp</dimen> + <!-- Bluetooth dialog related dimensions --> + <dimen name="bluetooth_dialog_layout_margin">16dp</dimen> + <!-- The height of the bluetooth device in bluetooth dialog. --> + <dimen name="bluetooth_dialog_device_height">72dp</dimen> + <!-- Height percentage of the parent container occupied by the communal view --> <item name="communal_source_height_percentage" format="float" type="dimen">0.80</item> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 29c9767866e9..5860806c6aec 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -459,7 +459,12 @@ <!-- Content description of the bluetooth icon when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_bluetooth_connected">Bluetooth connected.</string> - <!-- Content description of the bluetooth icon when connecting for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + + <!-- Content description of the bluetooth device icon. [CHAR LIMIT=NONE] --> + <string name="accessibility_bluetooth_device_icon">Bluetooth device icon</string> + + <!-- Content description of the bluetooth device settings gear icon. [CHAR LIMIT=NONE] --> + <string name="accessibility_bluetooth_device_settings_gear">Bluetooth device settings gear</string> <!-- Content description of the battery when battery state is unknown for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_battery_unknown">Battery percentage unknown.</string> @@ -621,6 +626,19 @@ <!-- QuickSettings: Bluetooth (Off) [CHAR LIMIT=NONE] --> <!-- QuickSettings: Bluetooth detail panel, text when there are no items [CHAR LIMIT=NONE] --> <string name="quick_settings_bluetooth_detail_empty_text">No paired devices available</string> + <!-- QuickSettings: Bluetooth dialog subtitle [CHAR LIMIT=NONE]--> + <string name="quick_settings_bluetooth_tile_subtitle">Tap a device to connect</string> + <!-- QuickSettings: Bluetooth dialog pair new devices [CHAR LIMIT=NONE]--> + <string name="pair_new_bluetooth_devices">Pair new device</string> + <!-- QuickSettings: Bluetooth dialog see all devices [CHAR LIMIT=NONE]--> + <string name="see_all_bluetooth_devices">See all</string> + <!-- QuickSettings: Bluetooth dialog turn on Bluetooth [CHAR LIMIT=NONE]--> + <string name="turn_on_bluetooth">Use Bluetooth</string> + <!-- QuickSettings: Bluetooth dialog device connected default summary [CHAR LIMIT=NONE]--> + <string name="quick_settings_bluetooth_device_connected">Connected</string> + <!-- QuickSettings: Bluetooth dialog device saved default summary [CHAR LIMIT=NONE]--> + <string name="quick_settings_bluetooth_device_saved">Saved</string> + <!-- QuickSettings: Bluetooth secondary label for the battery level of a connected device [CHAR LIMIT=20]--> <string name="quick_settings_bluetooth_secondary_label_battery_level"><xliff:g id="battery_level_as_percentage">%s</xliff:g> battery</string> <!-- QuickSettings: Bluetooth secondary label for an audio device being connected [CHAR LIMIT=20]--> @@ -3086,7 +3104,7 @@ configured. This is shown as part of a dialog that explains to the user why they cannot select this shortcut for their lock screen right now. [CHAR LIMIT=NONE]. --> - <string name="home_quick_affordance_unavailable_configure_the_app">• At least one device is available</string> + <string name="home_quick_affordance_unavailable_configure_the_app">• At least one device or device panel are available</string> <!--- Explains that the notes app is not available. This is shown as part of a dialog that explains to diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 6991b964c504..084cb883d766 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -15,7 +15,7 @@ --> <resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> <style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon"> <item name="android:textSize">@dimen/status_bar_clock_size</item> @@ -54,10 +54,10 @@ </style> <style name="TextAppearance.StatusBar.Expanded.EmergencyCallsOnly" - parent="TextAppearance.StatusBar.Expanded.AboveDateTime" /> + parent="TextAppearance.StatusBar.Expanded.AboveDateTime" /> <style name="TextAppearance.StatusBar.Expanded.ChargingInfo" - parent="TextAppearance.StatusBar.Expanded.AboveDateTime" /> + parent="TextAppearance.StatusBar.Expanded.AboveDateTime" /> <style name="TextAppearance.StatusBar.Expanded.UserSwitcher"> <item name="android:textSize">@dimen/kg_user_switcher_text_size</item> @@ -645,7 +645,7 @@ </style> <style name="TextAppearance.HeadsUpStatusBarText" - parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info"> + parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info"> </style> <style name="TextAppearance.QSEdit" > @@ -699,7 +699,7 @@ </style> <style name="MediaPlayer.SessionAction" - parent="@android:style/Widget.Material.Button.Borderless.Small"> + parent="@android:style/Widget.Material.Button.Borderless.Small"> <item name="android:background">@drawable/qs_media_light_source</item> <item name="android:tint">?android:attr/textColorPrimary</item> <item name="android:paddingTop">12dp</item> @@ -929,12 +929,13 @@ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> </style> - <style name="Theme.SystemUI.Dialog.Control.DetailPanel" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar"> - <item name="android:windowFullscreen">false</item> - <item name="android:windowIsFloating">false</item> - <item name="android:windowBackground">@color/controls_task_view_bg</item> - <item name="android:backgroundDimEnabled">false</item> - <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item> + <style name="Theme.SystemUI.Dialog.Control.DetailPanel" + parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar"> + <item name="android:windowFullscreen">false</item> + <item name="android:windowIsFloating">false</item> + <item name="android:windowBackground">@color/controls_task_view_bg</item> + <item name="android:backgroundDimEnabled">false</item> + <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item> </style> <style name="Control" /> @@ -1034,17 +1035,17 @@ <style name="Wallet" /> <style name="Wallet.TextAppearance"> - <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> - <item name="android:textColor">?android:attr/textColorPrimary</item> - <item name="android:singleLine">true</item> - <item name="android:textSize">14sp</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:singleLine">true</item> + <item name="android:textSize">14sp</item> </style> <style name="Wallet.Theme" parent="@android:style/Theme.DeviceDefault"> - <item name="android:colorBackground">@color/material_dynamic_neutral10</item> - <item name="android:itemBackground">@color/material_dynamic_neutral20</item> - <!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen. --> - <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item> + <item name="android:colorBackground">@color/material_dynamic_neutral10</item> + <item name="android:itemBackground">@color/material_dynamic_neutral20</item> + <!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen. --> + <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item> </style> <style name="Animation.InternetDialog" parent="@android:style/Animation.InputMethod"> @@ -1169,7 +1170,7 @@ </style> <style name="TrimmedHorizontalProgressBar" - parent="android:Widget.Material.ProgressBar.Horizontal"> + parent="android:Widget.Material.ProgressBar.Horizontal"> <item name="android:indeterminateDrawable"> @drawable/progress_indeterminate_horizontal_material_trimmed </item> @@ -1254,6 +1255,36 @@ <item name="android:textColor">?android:attr/textColorSecondary</item> </style> + <style name="BluetoothTileDialog"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center_vertical|start</item> + </style> + + <style name="BluetoothTileDialog.Device"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">88dp</item> + <item name="android:layout_gravity">center_vertical|start</item> + <item name="android:layout_marginStart">@dimen/bluetooth_dialog_layout_margin</item> + <item name="android:layout_marginEnd">@dimen/bluetooth_dialog_layout_margin</item> + <item name="android:paddingStart">22dp</item> + <item name="android:paddingEnd">22dp</item> + <item name="android:orientation">horizontal</item> + <item name="android:focusable">true</item> + <item name="android:clickable">true</item> + </style> + + <style name="BluetoothTileDialog.DeviceName"> + <item name="android:textSize">14sp</item> + <item name="android:textAppearance">@style/TextAppearance.Dialog.Title</item> + </style> + + <style name="BluetoothTileDialog.DeviceSummary"> + <item name="android:ellipsize">end</item> + <item name="android:maxLines">2</item> + <item name="android:textAppearance">@style/TextAppearance.Dialog.Body.Message</item> + </style> + <style name="BroadcastDialog"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> @@ -1397,7 +1428,7 @@ </style> <style name="PermissionGrantButtonTop" - parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"> + parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"> <item name="android:layout_width">332dp</item> <item name="android:layout_height">56dp</item> <item name="android:layout_marginTop">2dp</item> @@ -1406,7 +1437,7 @@ </style> <style name="PermissionGrantButtonBottom" - parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"> + parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"> <item name="android:layout_width">332dp</item> <item name="android:layout_height">56dp</item> <item name="android:layout_marginTop">2dp</item> @@ -1465,14 +1496,14 @@ </style> <style name="TextAppearance.PrivacyDialog.Item.Title" - parent="@android:style/TextAppearance.DeviceDefault.Medium"> + parent="@android:style/TextAppearance.DeviceDefault.Medium"> <item name="android:textSize">14sp</item> <item name="android:lineHeight">20sp</item> <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item> </style> <style name="TextAppearance.PrivacyDialog.Item.Summary" - parent="@android:style/TextAppearance.DeviceDefault.Small"> + parent="@android:style/TextAppearance.DeviceDefault.Small"> <item name="android:textSize">14sp</item> <item name="android:lineHeight">20sp</item> <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item> @@ -1481,4 +1512,4 @@ <style name="Theme.PrivacyDialog" parent="@style/Theme.SystemUI.Dialog"> <item name="android:colorBackground">?androidprv:attr/materialColorSurfaceContainer</item> </style> -</resources> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java index 905923039f8b..c0749885846f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java @@ -282,9 +282,9 @@ public class RotationButtonController { TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener); } - public void setRotationLockedAtAngle(int rotationSuggestion) { + public void setRotationLockedAtAngle(int rotationSuggestion, String caller) { RotationPolicy.setRotationLockAtAngle(mContext, /* enabled= */ isRotationLocked(), - /* rotation= */ rotationSuggestion); + /* rotation= */ rotationSuggestion, caller); } public boolean isRotationLocked() { @@ -468,7 +468,8 @@ public class RotationButtonController { if (rotationLocked || mRotationButton.isVisible()) { // Do not allow a change in rotation to set user rotation when docked. if (shouldOverrideUserLockPrefs(rotation) && rotationLocked && !mDocked) { - setRotationLockedAtAngle(rotation); + setRotationLockedAtAngle(rotation, /* caller= */ + "RotationButtonController#onRotationWatcherChanged"); } setRotateSuggestionButtonState(false /* visible */, true /* forced */); } @@ -572,7 +573,8 @@ public class RotationButtonController { private void onRotateSuggestionClick(View v) { mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED); incrementNumAcceptedRotationSuggestionsIfNeeded(); - setRotationLockedAtAngle(mLastRotationSuggestion); + setRotationLockedAtAngle(mLastRotationSuggestion, + /* caller= */ "RotationButtonController#onRotateSuggestionClick"); Log.i(TAG, "onRotateSuggestionClick() mLastRotationSuggestion=" + mLastRotationSuggestion); v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java index 1741e306c29f..622b67f25da3 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java @@ -17,6 +17,7 @@ package com.android.keyguard; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_APPEAR; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_DISAPPEAR; @@ -52,6 +53,8 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { private final DisappearAnimationUtils mDisappearAnimationUtils; private final DisappearAnimationUtils mDisappearAnimationUtilsLocked; @Nullable private MotionLayout mContainerMotionLayout; + // TODO (b/293252410) - usage of mContainerConstraintLayout should be removed + // when the flag is enabled/removed @Nullable private ConstraintLayout mContainerConstraintLayout; private int mDisappearYTranslation; private View[][] mViews; @@ -59,7 +62,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { private int mYTransOffset; private View mBouncerMessageArea; private boolean mAlreadyUsingSplitBouncer = false; - private boolean mIsLockScreenLandscapeEnabled = false; + private boolean mIsSmallLockScreenLandscapeEnabled = false; @DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN; public static final long ANIMATION_DURATION = 650; @@ -87,12 +90,12 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { /** Use motion layout (new bouncer implementation) if LOCKSCREEN_ENABLE_LANDSCAPE flag is * enabled, instead of constraint layout (old bouncer implementation) */ public void setIsLockScreenLandscapeEnabled(boolean isLockScreenLandscapeEnabled) { - mIsLockScreenLandscapeEnabled = isLockScreenLandscapeEnabled; + mIsSmallLockScreenLandscapeEnabled = isLockScreenLandscapeEnabled; findContainerLayout(); } private void findContainerLayout() { - if (mIsLockScreenLandscapeEnabled) { + if (mIsSmallLockScreenLandscapeEnabled) { mContainerMotionLayout = findViewById(R.id.pin_container); } else { mContainerConstraintLayout = findViewById(R.id.pin_container); @@ -109,7 +112,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { if (mLastDevicePosture == posture) return; mLastDevicePosture = posture; - if (mIsLockScreenLandscapeEnabled) { + if (mIsSmallLockScreenLandscapeEnabled) { boolean useSplitBouncerAfterFold = mLastDevicePosture == DEVICE_POSTURE_CLOSED && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE @@ -166,21 +169,45 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { } } + if (mIsSmallLockScreenLandscapeEnabled) { + updateHalfFoldedConstraints(); + } else { + updateHalfFoldedGuideline(); + } + } + + private void updateHalfFoldedConstraints() { + // Update the constraints based on the device posture... + if (mAlreadyUsingSplitBouncer) return; + + boolean shouldCollapsePin = + mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED + && mContext.getResources().getConfiguration().orientation + == ORIENTATION_PORTRAIT; + + int expectedMotionLayoutState = shouldCollapsePin + ? R.id.half_folded_single_constraints + : R.id.single_constraints; + + transitionToMotionLayoutState(expectedMotionLayoutState); + } + + // TODO (b/293252410) - this method can be removed when the flag is enabled/removed + private void updateHalfFoldedGuideline() { // Update the guideline based on the device posture... float halfOpenPercentage = mContext.getResources().getFloat(R.dimen.half_opened_bouncer_height_ratio); - if (mIsLockScreenLandscapeEnabled) { - ConstraintSet cs = mContainerMotionLayout.getConstraintSet(R.id.single_constraints); - cs.setGuidelinePercent(R.id.pin_pad_top_guideline, - mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f); - cs.applyTo(mContainerMotionLayout); - } else { - ConstraintSet cs = new ConstraintSet(); - cs.clone(mContainerConstraintLayout); - cs.setGuidelinePercent(R.id.pin_pad_top_guideline, - mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f); - cs.applyTo(mContainerConstraintLayout); + ConstraintSet cs = new ConstraintSet(); + cs.clone(mContainerConstraintLayout); + cs.setGuidelinePercent(R.id.pin_pad_top_guideline, + mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f); + cs.applyTo(mContainerConstraintLayout); + } + + private void transitionToMotionLayoutState(int state) { + if (mContainerMotionLayout.getCurrentState() != state) { + mContainerMotionLayout.transitionToState(state); } } @@ -189,12 +216,24 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { * Only called when flag LANDSCAPE_ENABLE_LOCKSCREEN is enabled. */ @Override protected void updateConstraints(boolean useSplitBouncer) { + if (!mIsSmallLockScreenLandscapeEnabled) return; + mAlreadyUsingSplitBouncer = useSplitBouncer; + if (useSplitBouncer) { mContainerMotionLayout.jumpToState(R.id.split_constraints); mContainerMotionLayout.setMaxWidth(Integer.MAX_VALUE); } else { - mContainerMotionLayout.jumpToState(R.id.single_constraints); + boolean useHalfFoldedConstraints = + mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED + && mContext.getResources().getConfiguration().orientation + == ORIENTATION_PORTRAIT; + + if (useHalfFoldedConstraints) { + mContainerMotionLayout.jumpToState(R.id.half_folded_single_constraints); + } else { + mContainerMotionLayout.jumpToState(R.id.single_constraints); + } mContainerMotionLayout.setMaxWidth(getResources() .getDimensionPixelSize(R.dimen.keyguard_security_width)); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java index 802e222b3897..5c206e95966b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java @@ -16,6 +16,7 @@ package com.android.keyguard; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED; @@ -81,9 +82,11 @@ public class KeyguardPatternView extends KeyguardInputView BouncerKeyguardMessageArea mSecurityMessageDisplay; private View mEcaView; @Nullable private MotionLayout mContainerMotionLayout; + // TODO (b/293252410) - usage of mContainerConstraintLayout should be removed + // when the flag is enabled/removed @Nullable private ConstraintLayout mContainerConstraintLayout; private boolean mAlreadyUsingSplitBouncer = false; - private boolean mIsLockScreenLandscapeEnabled = false; + private boolean mIsSmallLockScreenLandscapeEnabled = false; @DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN; public KeyguardPatternView(Context context) { @@ -111,12 +114,12 @@ public class KeyguardPatternView extends KeyguardInputView * enabled, instead of constraint layout (old bouncer implementation) */ public void setIsLockScreenLandscapeEnabled(boolean isLockScreenLandscapeEnabled) { - mIsLockScreenLandscapeEnabled = isLockScreenLandscapeEnabled; + mIsSmallLockScreenLandscapeEnabled = isLockScreenLandscapeEnabled; findContainerLayout(); } private void findContainerLayout() { - if (mIsLockScreenLandscapeEnabled) { + if (mIsSmallLockScreenLandscapeEnabled) { mContainerMotionLayout = findViewById(R.id.pattern_container); } else { mContainerConstraintLayout = findViewById(R.id.pattern_container); @@ -132,7 +135,7 @@ public class KeyguardPatternView extends KeyguardInputView if (mLastDevicePosture == posture) return; mLastDevicePosture = posture; - if (mIsLockScreenLandscapeEnabled) { + if (mIsSmallLockScreenLandscapeEnabled) { boolean useSplitBouncerAfterFold = mLastDevicePosture == DEVICE_POSTURE_CLOSED && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE @@ -147,21 +150,45 @@ public class KeyguardPatternView extends KeyguardInputView } private void updateMargins() { + if (mIsSmallLockScreenLandscapeEnabled) { + updateHalfFoldedConstraints(); + } else { + updateHalfFoldedGuideline(); + } + } + + private void updateHalfFoldedConstraints() { + // Update the constraints based on the device posture... + if (mAlreadyUsingSplitBouncer) return; + + boolean shouldCollapsePattern = + mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED + && mContext.getResources().getConfiguration().orientation + == ORIENTATION_PORTRAIT; + + int expectedMotionLayoutState = shouldCollapsePattern + ? R.id.half_folded_single_constraints + : R.id.single_constraints; + + transitionToMotionLayoutState(expectedMotionLayoutState); + } + + // TODO (b/293252410) - this method can be removed when the flag is enabled/removed + private void updateHalfFoldedGuideline() { // Update the guideline based on the device posture... float halfOpenPercentage = mContext.getResources().getFloat(R.dimen.half_opened_bouncer_height_ratio); - if (mIsLockScreenLandscapeEnabled) { - ConstraintSet cs = mContainerMotionLayout.getConstraintSet(R.id.single_constraints); - cs.setGuidelinePercent(R.id.pattern_top_guideline, - mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f); - cs.applyTo(mContainerMotionLayout); - } else { - ConstraintSet cs = new ConstraintSet(); - cs.clone(mContainerConstraintLayout); - cs.setGuidelinePercent(R.id.pattern_top_guideline, - mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f); - cs.applyTo(mContainerConstraintLayout); + ConstraintSet cs = new ConstraintSet(); + cs.clone(mContainerConstraintLayout); + cs.setGuidelinePercent(R.id.pattern_top_guideline, + mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f); + cs.applyTo(mContainerConstraintLayout); + } + + private void transitionToMotionLayoutState(int state) { + if (mContainerMotionLayout.getCurrentState() != state) { + mContainerMotionLayout.transitionToState(state); } } @@ -172,12 +199,24 @@ public class KeyguardPatternView extends KeyguardInputView */ @Override protected void updateConstraints(boolean useSplitBouncer) { + if (!mIsSmallLockScreenLandscapeEnabled) return; + mAlreadyUsingSplitBouncer = useSplitBouncer; + if (useSplitBouncer) { mContainerMotionLayout.jumpToState(R.id.split_constraints); mContainerMotionLayout.setMaxWidth(Integer.MAX_VALUE); } else { - mContainerMotionLayout.jumpToState(R.id.single_constraints); + boolean useHalfFoldedConstraints = + mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED + && mContext.getResources().getConfiguration().orientation + == ORIENTATION_PORTRAIT; + + if (useHalfFoldedConstraints) { + mContainerMotionLayout.jumpToState(R.id.half_folded_single_constraints); + } else { + mContainerMotionLayout.jumpToState(R.id.single_constraints); + } mContainerMotionLayout.setMaxWidth(getResources() .getDimensionPixelSize(R.dimen.biometric_auth_pattern_view_max_size)); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java index 21960e219fc9..83b1a2cbbf52 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java @@ -67,42 +67,6 @@ public interface KeyguardSecurityView { int PROMPT_REASON_TRUSTAGENT_EXPIRED = 8; /** - * Prompt that is shown when there is an incorrect primary authentication input. - */ - int PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT = 9; - - /** - * Prompt that is shown when there is an incorrect face biometric input. - */ - int PROMPT_REASON_INCORRECT_FACE_INPUT = 10; - - /** - * Prompt that is shown when there is an incorrect fingerprint biometric input. - */ - int PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT = 11; - - /** - * Prompt that is shown when face authentication is in locked out state. - */ - int PROMPT_REASON_FACE_LOCKED_OUT = 12; - - /** - * Prompt that is shown when fingerprint authentication is in locked out state. - */ - int PROMPT_REASON_FINGERPRINT_LOCKED_OUT = 13; - - /** - * Default prompt that is shown on the bouncer. - */ - int PROMPT_REASON_DEFAULT = 14; - - /** - * Prompt that is shown when primary authentication is in locked out state after too many - * attempts - */ - int PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT = 15; - - /** * Strong auth is required because the device has just booted because of an automatic * mainline update. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java index 9c4d224115b2..ec2999ffcfe1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java @@ -139,12 +139,12 @@ public class KeyguardSecurityViewFlipperController onViewInflatedListener.onViewInflated(childController); // Single bouncer constrains are default - if (mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE) - && - getResources().getBoolean(R.bool.update_bouncer_constraints)) { + if (mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)) { boolean useSplitBouncer = - getResources().getConfiguration().orientation + getResources().getBoolean(R.bool.update_bouncer_constraints) + && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE; + updateConstraints(useSplitBouncer); } } diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt index 57a42247af76..4cfc6aaa2b50 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt @@ -185,9 +185,6 @@ constructor( /** Whether the pattern should be visible for the currently-selected user. */ val isPatternVisible: StateFlow<Boolean> = repository.isPatternVisible - /** The minimal length of a pattern. */ - val minPatternLength: Int = repository.minPatternLength - private var throttlingCountdownJob: Job? = null init { @@ -243,39 +240,46 @@ constructor( * Attempts to authenticate the user and unlock the device. * * If [tryAutoConfirm] is `true`, authentication is attempted if and only if the auth method - * supports auto-confirming, and the input's length is at least the code's length. Otherwise, - * `null` is returned. + * supports auto-confirming, and the input's length is at least the required length. Otherwise, + * `AuthenticationResult.SKIPPED` is returned. * * @param input The input from the user to try to authenticate with. This can be a list of * different things, based on the current authentication method. * @param tryAutoConfirm `true` if called while the user inputs the code, without an explicit * request to validate. - * @return `true` if the authentication succeeded and the device is now unlocked; `false` when - * authentication failed, `null` if the check was not performed. + * @return The result of this authentication attempt. */ - suspend fun authenticate(input: List<Any>, tryAutoConfirm: Boolean = false): Boolean? { + suspend fun authenticate( + input: List<Any>, + tryAutoConfirm: Boolean = false + ): AuthenticationResult { if (input.isEmpty()) { throw IllegalArgumentException("Input was empty!") } + val authMethod = getAuthenticationMethod() val skipCheck = when { // We're being throttled, the UI layer should not have called this; skip the // attempt. isThrottled.value -> true + // The pattern is too short; skip the attempt. + authMethod == DomainLayerAuthenticationMethodModel.Pattern && + input.size < repository.minPatternLength -> true // Auto-confirm attempt when the feature is not enabled; skip the attempt. tryAutoConfirm && !isAutoConfirmEnabled.value -> true // Auto-confirm should skip the attempt if the pin entered is too short. - tryAutoConfirm && input.size < repository.getPinLength() -> true + tryAutoConfirm && + authMethod == DomainLayerAuthenticationMethodModel.Pin && + input.size < repository.getPinLength() -> true else -> false } if (skipCheck) { - return null + return AuthenticationResult.SKIPPED } // Attempt to authenticate: - val authMethod = getAuthenticationMethod() - val credential = authMethod.createCredential(input) ?: return null + val credential = authMethod.createCredential(input) ?: return AuthenticationResult.SKIPPED val authenticationResult = repository.checkCredential(credential) credential.zeroize() @@ -299,7 +303,11 @@ constructor( refreshThrottling() } - return authenticationResult.isSuccessful + return if (authenticationResult.isSuccessful) { + AuthenticationResult.SUCCEEDED + } else { + AuthenticationResult.FAILED + } } /** Starts refreshing the throttling state every second. */ @@ -383,3 +391,16 @@ constructor( } } } + +/** Result of a user authentication attempt. */ +enum class AuthenticationResult { + /** Authentication succeeded and the device is now unlocked. */ + SUCCEEDED, + /** Authentication failed and the device remains unlocked. */ + FAILED, + /** + * Authentication was not performed, e.g. due to insufficient input, and the device remains + * unlocked. + */ + SKIPPED, +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 46d3c8a0c3e3..79d9c1ba70bc 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -1096,8 +1096,19 @@ public class UdfpsController implements DozeReceiver, Dumpable { // cancel the fingerprint scan. mCancelAodFingerUpAction = mFgExecutor.executeDelayed(this::tryAodSendFingerUp, AOD_SEND_FINGER_UP_DELAY_MILLIS); - // using a hard-coded value for major and minor until it is available from the sensor - onFingerDown(requestId, screenX, screenY, minor, major); + // using a hard-coded value for orientation, time and gestureStart until they are + // available from the sensor. + onFingerDown( + requestId, + MotionEvent.INVALID_POINTER_ID /* pointerId */, + screenX, + screenY, + minor, + major, + 0f /* orientation */, + 0L /* time */, + 0L /* gestureStart */, + true /* isAod */); }; if (mScreenOn) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt index f2d4f8912c3f..72fcfe777e79 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt @@ -16,10 +16,12 @@ package com.android.systemui.biometrics.dagger -import com.android.systemui.biometrics.UdfpsUtils import android.content.res.Resources import com.android.internal.R import com.android.systemui.biometrics.EllipseOverlapDetectorParams +import com.android.systemui.biometrics.UdfpsUtils +import com.android.systemui.biometrics.data.repository.DisplayStateRepository +import com.android.systemui.biometrics.data.repository.DisplayStateRepositoryImpl import com.android.systemui.biometrics.data.repository.FacePropertyRepository import com.android.systemui.biometrics.data.repository.FacePropertyRepositoryImpl import com.android.systemui.biometrics.data.repository.FaceSettingsRepository @@ -28,18 +30,6 @@ import com.android.systemui.biometrics.data.repository.FingerprintPropertyReposi import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl import com.android.systemui.biometrics.data.repository.PromptRepository import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl -import com.android.systemui.biometrics.data.repository.DisplayStateRepository -import com.android.systemui.biometrics.data.repository.DisplayStateRepositoryImpl -import com.android.systemui.biometrics.domain.interactor.CredentialInteractor -import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl -import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor -import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl -import com.android.systemui.biometrics.domain.interactor.LogContextInteractor -import com.android.systemui.biometrics.domain.interactor.LogContextInteractorImpl -import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor -import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl -import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractor -import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractorImpl import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector import com.android.systemui.biometrics.udfps.EllipseOverlapDetector import com.android.systemui.biometrics.udfps.OverlapDetector @@ -59,9 +49,7 @@ interface BiometricsModule { @SysUISingleton fun faceSettings(impl: FaceSettingsRepositoryImpl): FaceSettingsRepository - @Binds - @SysUISingleton - fun faceSensors(impl: FacePropertyRepositoryImpl): FacePropertyRepository + @Binds @SysUISingleton fun faceSensors(impl: FacePropertyRepositoryImpl): FacePropertyRepository @Binds @SysUISingleton @@ -77,30 +65,6 @@ interface BiometricsModule { @SysUISingleton fun displayStateRepository(impl: DisplayStateRepositoryImpl): DisplayStateRepository - @Binds - @SysUISingleton - fun providesPromptSelectorInteractor( - impl: PromptSelectorInteractorImpl - ): PromptSelectorInteractor - - @Binds - @SysUISingleton - fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor - - @Binds - @SysUISingleton - fun providesDisplayStateInteractor(impl: DisplayStateInteractorImpl): DisplayStateInteractor - - @Binds - @SysUISingleton - fun bindsLogContextInteractor(impl: LogContextInteractorImpl): LogContextInteractor - - @Binds - @SysUISingleton - fun providesSideFpsOverlayInteractor( - impl: SideFpsOverlayInteractorImpl - ): SideFpsOverlayInteractor - companion object { /** Background [Executor] for HAL related operations. */ @Provides @@ -110,8 +74,7 @@ interface BiometricsModule { fun providesPluginExecutor(threadFactory: ThreadFactory): Executor = threadFactory.buildExecutorOnNewThread("biometrics") - @Provides - fun providesUdfpsUtils(): UdfpsUtils = UdfpsUtils() + @Provides fun providesUdfpsUtils(): UdfpsUtils = UdfpsUtils() @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt index 7a9efcf78999..c4c52e8b358e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt @@ -41,6 +41,12 @@ import kotlinx.coroutines.flow.stateIn /** Repository for the current state of the display */ interface DisplayStateRepository { + /** + * Whether or not the direction rotation is applied to get to an application's requested + * orientation is reversed. + */ + val isReverseDefaultRotation: Boolean + /** Provides the current rear display state. */ val isInRearDisplayMode: StateFlow<Boolean> @@ -59,6 +65,9 @@ constructor( @Main handler: Handler, @Main mainExecutor: Executor ) : DisplayStateRepository { + override val isReverseDefaultRotation = + context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation) + override val isInRearDisplayMode: StateFlow<Boolean> = conflatedCallbackFlow { val sendRearDisplayStateUpdate = { state: Boolean -> @@ -94,7 +103,11 @@ constructor( private fun getDisplayRotation(): DisplayRotation { val cachedDisplayInfo = DisplayInfo() context.display?.getDisplayInfo(cachedDisplayInfo) - return cachedDisplayInfo.rotation.toDisplayRotation() + var rotation = cachedDisplayInfo.rotation + if (isReverseDefaultRotation) { + rotation = (rotation + 1) % 4 + } + return rotation.toDisplayRotation() } override val currentRotation: StateFlow<DisplayRotation> = diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt new file mode 100644 index 000000000000..a590dccd5318 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.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.biometrics.domain + +import com.android.systemui.biometrics.domain.interactor.CredentialInteractor +import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl +import com.android.systemui.biometrics.domain.interactor.LogContextInteractor +import com.android.systemui.biometrics.domain.interactor.LogContextInteractorImpl +import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor +import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl +import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractor +import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractorImpl +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module + +@Module +interface BiometricsDomainLayerModule { + + @Binds + @SysUISingleton + fun providesPromptSelectorInteractor( + impl: PromptSelectorInteractorImpl + ): PromptSelectorInteractor + + @Binds + @SysUISingleton + fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor + + @Binds + @SysUISingleton + fun providesDisplayStateInteractor(impl: DisplayStateInteractorImpl): DisplayStateInteractor + + @Binds + @SysUISingleton + fun bindsLogContextInteractor(impl: LogContextInteractorImpl): LogContextInteractor + + @Binds + @SysUISingleton + fun providesSideFpsOverlayInteractor( + impl: SideFpsOverlayInteractorImpl + ): SideFpsOverlayInteractor +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt index 188c82b52374..d28f1dc78c13 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt @@ -17,7 +17,6 @@ package com.android.systemui.biometrics.ui.binder -import android.view.DisplayInfo import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.airbnb.lottie.LottieAnimationView @@ -33,14 +32,14 @@ object PromptFingerprintIconViewBinder { fun bind(view: LottieAnimationView, viewModel: PromptFingerprintIconViewModel) { view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { - val displayInfo = DisplayInfo() - view.context.display?.getDisplayInfo(displayInfo) - viewModel.setRotation(displayInfo.rotation) viewModel.onConfigurationChanged(view.context.resources.configuration) launch { viewModel.iconAsset.collect { iconAsset -> if (iconAsset != -1) { view.setAnimation(iconAsset) + // TODO: must replace call below once non-sfps asset logic and + // shouldAnimateIconView logic is migrated to this ViewModel. + view.playAnimation() } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt index a95e257188dc..dfd3a9b8aebe 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt @@ -19,10 +19,10 @@ package com.android.systemui.biometrics.ui.viewmodel import android.annotation.RawRes import android.content.res.Configuration -import android.view.Surface import com.android.systemui.res.R import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor +import com.android.systemui.biometrics.shared.model.DisplayRotation import com.android.systemui.biometrics.shared.model.FingerprintSensorType import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -35,19 +35,21 @@ constructor( private val displayStateInteractor: DisplayStateInteractor, promptSelectorInteractor: PromptSelectorInteractor, ) { - /** Current device rotation. */ - private var rotation: Int = Surface.ROTATION_0 - /** Current BiometricPromptLayout.iconView asset. */ val iconAsset: Flow<Int> = combine( + displayStateInteractor.currentRotation, displayStateInteractor.isFolded, displayStateInteractor.isInRearDisplayMode, promptSelectorInteractor.sensorType, - ) { isFolded: Boolean, isInRearDisplayMode: Boolean, sensorType: FingerprintSensorType -> + ) { + rotation: DisplayRotation, + isFolded: Boolean, + isInRearDisplayMode: Boolean, + sensorType: FingerprintSensorType -> when (sensorType) { FingerprintSensorType.POWER_BUTTON -> - getSideFpsAnimationAsset(isFolded, isInRearDisplayMode) + getSideFpsAnimationAsset(rotation, isFolded, isInRearDisplayMode) // Replace below when non-SFPS iconAsset logic is migrated to this ViewModel else -> -1 } @@ -55,11 +57,12 @@ constructor( @RawRes private fun getSideFpsAnimationAsset( + rotation: DisplayRotation, isDeviceFolded: Boolean, isInRearDisplayMode: Boolean, ): Int = when (rotation) { - Surface.ROTATION_90 -> + DisplayRotation.ROTATION_90 -> if (isInRearDisplayMode) { R.raw.biometricprompt_rear_portrait_reverse_base } else if (isDeviceFolded) { @@ -67,7 +70,7 @@ constructor( } else { R.raw.biometricprompt_portrait_base_topleft } - Surface.ROTATION_270 -> + DisplayRotation.ROTATION_270 -> if (isInRearDisplayMode) { R.raw.biometricprompt_rear_portrait_base } else if (isDeviceFolded) { @@ -89,8 +92,4 @@ constructor( fun onConfigurationChanged(newConfig: Configuration) { displayStateInteractor.onConfigurationChanged(newConfig) } - - fun setRotation(newRotation: Int) { - rotation = newRotation - } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt deleted file mode 100644 index f93337c993c8..000000000000 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt +++ /dev/null @@ -1,390 +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.systemui.bouncer.data.factory - -import android.annotation.IntDef -import com.android.keyguard.KeyguardSecurityModel -import com.android.keyguard.KeyguardSecurityModel.SecurityMode -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FACE_LOCKED_OUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FINGERPRINT_LOCKED_OUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FACE_INPUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST -import com.android.systemui.res.R.string.bouncer_face_not_recognized -import com.android.systemui.res.R.string.keyguard_enter_password -import com.android.systemui.res.R.string.keyguard_enter_pattern -import com.android.systemui.res.R.string.keyguard_enter_pin -import com.android.systemui.res.R.string.kg_bio_too_many_attempts_password -import com.android.systemui.res.R.string.kg_bio_too_many_attempts_pattern -import com.android.systemui.res.R.string.kg_bio_too_many_attempts_pin -import com.android.systemui.res.R.string.kg_bio_try_again_or_password -import com.android.systemui.res.R.string.kg_bio_try_again_or_pattern -import com.android.systemui.res.R.string.kg_bio_try_again_or_pin -import com.android.systemui.res.R.string.kg_face_locked_out -import com.android.systemui.res.R.string.kg_fp_locked_out -import com.android.systemui.res.R.string.kg_fp_not_recognized -import com.android.systemui.res.R.string.kg_primary_auth_locked_out_password -import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pattern -import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pin -import com.android.systemui.res.R.string.kg_prompt_after_dpm_lock -import com.android.systemui.res.R.string.kg_prompt_after_update_password -import com.android.systemui.res.R.string.kg_prompt_after_update_pattern -import com.android.systemui.res.R.string.kg_prompt_after_update_pin -import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_password -import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_pattern -import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_pin -import com.android.systemui.res.R.string.kg_prompt_auth_timeout -import com.android.systemui.res.R.string.kg_prompt_password_auth_timeout -import com.android.systemui.res.R.string.kg_prompt_pattern_auth_timeout -import com.android.systemui.res.R.string.kg_prompt_pin_auth_timeout -import com.android.systemui.res.R.string.kg_prompt_reason_restart_password -import com.android.systemui.res.R.string.kg_prompt_reason_restart_pattern -import com.android.systemui.res.R.string.kg_prompt_reason_restart_pin -import com.android.systemui.res.R.string.kg_prompt_unattended_update -import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown -import com.android.systemui.res.R.string.kg_trust_agent_disabled -import com.android.systemui.res.R.string.kg_unlock_with_password_or_fp -import com.android.systemui.res.R.string.kg_unlock_with_pattern_or_fp -import com.android.systemui.res.R.string.kg_unlock_with_pin_or_fp -import com.android.systemui.res.R.string.kg_wrong_input_try_fp_suggestion -import com.android.systemui.res.R.string.kg_wrong_password_try_again -import com.android.systemui.res.R.string.kg_wrong_pattern_try_again -import com.android.systemui.res.R.string.kg_wrong_pin_try_again -import com.android.systemui.bouncer.shared.model.BouncerMessageModel -import com.android.systemui.bouncer.shared.model.Message -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository -import javax.inject.Inject - -@SysUISingleton -class BouncerMessageFactory -@Inject -constructor( - private val biometricSettingsRepository: BiometricSettingsRepository, - private val securityModel: KeyguardSecurityModel, -) { - - fun createFromPromptReason( - @BouncerPromptReason reason: Int, - userId: Int, - secondaryMsgOverride: String? = null - ): BouncerMessageModel? { - val pair = - getBouncerMessage( - reason, - securityModel.getSecurityMode(userId), - biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value - ) - return pair?.let { - BouncerMessageModel( - message = Message(messageResId = pair.first, animate = false), - secondaryMessage = - secondaryMsgOverride?.let { - Message(message = secondaryMsgOverride, animate = false) - } - ?: Message(messageResId = pair.second, animate = false) - ) - } - } - - /** - * Helper method that provides the relevant bouncer message that should be shown for different - * scenarios indicated by [reason]. [securityMode] & [fpAuthIsAllowed] parameters are used to - * provide a more specific message. - */ - private fun getBouncerMessage( - @BouncerPromptReason reason: Int, - securityMode: SecurityMode, - fpAuthIsAllowed: Boolean = false - ): Pair<Int, Int>? { - return when (reason) { - // Primary auth locked out - PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode) - // Primary auth required reasons - PROMPT_REASON_RESTART -> authRequiredAfterReboot(securityMode) - PROMPT_REASON_TIMEOUT -> authRequiredAfterPrimaryAuthTimeout(securityMode) - PROMPT_REASON_DEVICE_ADMIN -> authRequiredAfterAdminLockdown(securityMode) - PROMPT_REASON_USER_REQUEST -> authRequiredAfterUserLockdown(securityMode) - PROMPT_REASON_PREPARE_FOR_UPDATE -> authRequiredForUnattendedUpdate(securityMode) - PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE -> authRequiredForMainlineUpdate(securityMode) - PROMPT_REASON_FINGERPRINT_LOCKED_OUT -> fingerprintUnlockUnavailable(securityMode) - PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode) - // Non strong auth not available reasons - PROMPT_REASON_FACE_LOCKED_OUT -> - if (fpAuthIsAllowed) faceLockedOutButFingerprintAvailable(securityMode) - else faceLockedOut(securityMode) - PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT -> - if (fpAuthIsAllowed) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode) - else nonStrongAuthTimeout(securityMode) - PROMPT_REASON_TRUSTAGENT_EXPIRED -> - if (fpAuthIsAllowed) trustAgentDisabledWithFingerprintAllowed(securityMode) - else trustAgentDisabled(securityMode) - // Auth incorrect input reasons. - PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT -> - if (fpAuthIsAllowed) incorrectSecurityInputWithFingerprint(securityMode) - else incorrectSecurityInput(securityMode) - PROMPT_REASON_INCORRECT_FACE_INPUT -> - if (fpAuthIsAllowed) incorrectFaceInputWithFingerprintAllowed(securityMode) - else incorrectFaceInput(securityMode) - PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -> incorrectFingerprintInput(securityMode) - // Default message - PROMPT_REASON_DEFAULT -> - if (fpAuthIsAllowed) defaultMessageWithFingerprint(securityMode) - else defaultMessage(securityMode) - else -> null - } - } - - fun emptyMessage(): BouncerMessageModel = - BouncerMessageModel(Message(message = ""), Message(message = "")) -} - -@Retention(AnnotationRetention.SOURCE) -@IntDef( - PROMPT_REASON_TIMEOUT, - PROMPT_REASON_DEVICE_ADMIN, - PROMPT_REASON_USER_REQUEST, - PROMPT_REASON_AFTER_LOCKOUT, - PROMPT_REASON_PREPARE_FOR_UPDATE, - PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT, - PROMPT_REASON_TRUSTAGENT_EXPIRED, - PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, - PROMPT_REASON_INCORRECT_FACE_INPUT, - PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT, - PROMPT_REASON_FACE_LOCKED_OUT, - PROMPT_REASON_FINGERPRINT_LOCKED_OUT, - PROMPT_REASON_DEFAULT, - PROMPT_REASON_NONE, - PROMPT_REASON_RESTART, - PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT, - PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE, -) -annotation class BouncerPromptReason - -private fun defaultMessage(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) - else -> Pair(0, 0) - } -} - -private fun defaultMessageWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, 0) - SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, 0) - SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, 0) - else -> Pair(0, 0) - } -} - -private fun incorrectSecurityInput(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, 0) - SecurityMode.Password -> Pair(kg_wrong_password_try_again, 0) - SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, 0) - else -> Pair(0, 0) - } -} - -private fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, kg_wrong_input_try_fp_suggestion) - SecurityMode.Password -> Pair(kg_wrong_password_try_again, kg_wrong_input_try_fp_suggestion) - SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, kg_wrong_input_try_fp_suggestion) - else -> Pair(0, 0) - } -} - -private fun incorrectFingerprintInput(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pattern) - SecurityMode.Password -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_password) - SecurityMode.PIN -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pin) - else -> Pair(0, 0) - } -} - -private fun incorrectFaceInput(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pattern) - SecurityMode.Password -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_password) - SecurityMode.PIN -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pin) - else -> Pair(0, 0) - } -} - -private fun incorrectFaceInputWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, bouncer_face_not_recognized) - SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, bouncer_face_not_recognized) - SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, bouncer_face_not_recognized) - else -> Pair(0, 0) - } -} - -private fun biometricLockout(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern) - SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password) - SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_bio_too_many_attempts_pin) - else -> Pair(0, 0) - } -} - -private fun authRequiredAfterReboot(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_reason_restart_pattern) - SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_reason_restart_password) - SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin) - else -> Pair(0, 0) - } -} - -private fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_dpm_lock) - SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_dpm_lock) - SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock) - else -> Pair(0, 0) - } -} - -private fun authRequiredAfterUserLockdown(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern) - SecurityMode.Password -> - Pair(keyguard_enter_password, kg_prompt_after_user_lockdown_password) - SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin) - else -> Pair(0, 0) - } -} - -private fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_unattended_update) - SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_unattended_update) - SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_unattended_update) - else -> Pair(0, 0) - } -} - -private fun authRequiredForMainlineUpdate(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_update_pattern) - SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_update_password) - SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_update_pin) - else -> Pair(0, 0) - } -} - -private fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_pattern_auth_timeout) - SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_password_auth_timeout) - SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout) - else -> Pair(0, 0) - } -} - -private fun nonStrongAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_auth_timeout) - SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_auth_timeout) - SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_auth_timeout) - else -> Pair(0, 0) - } -} - -private fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_prompt_auth_timeout) - SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_prompt_auth_timeout) - SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_prompt_auth_timeout) - else -> Pair(0, 0) - } -} - -private fun faceLockedOut(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_face_locked_out) - SecurityMode.Password -> Pair(keyguard_enter_password, kg_face_locked_out) - SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_face_locked_out) - else -> Pair(0, 0) - } -} - -private fun faceLockedOutButFingerprintAvailable(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_face_locked_out) - SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_face_locked_out) - SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_face_locked_out) - else -> Pair(0, 0) - } -} - -private fun fingerprintUnlockUnavailable(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_fp_locked_out) - SecurityMode.Password -> Pair(keyguard_enter_password, kg_fp_locked_out) - SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_fp_locked_out) - else -> Pair(0, 0) - } -} - -private fun trustAgentDisabled(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_trust_agent_disabled) - SecurityMode.Password -> Pair(keyguard_enter_password, kg_trust_agent_disabled) - SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_trust_agent_disabled) - else -> Pair(0, 0) - } -} - -private fun trustAgentDisabledWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_trust_agent_disabled) - SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_trust_agent_disabled) - SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_trust_agent_disabled) - else -> Pair(0, 0) - } -} - -private fun primaryAuthLockedOut(securityMode: SecurityMode): Pair<Int, Int> { - return when (securityMode) { - SecurityMode.Pattern -> - Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pattern) - SecurityMode.Password -> - Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_password) - SecurityMode.PIN -> - Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pin) - else -> Pair(0, 0) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt index 97c1bdb180a1..094dc0a33865 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt @@ -16,315 +16,26 @@ package com.android.systemui.bouncer.data.repository -import android.hardware.biometrics.BiometricSourceType -import android.hardware.biometrics.BiometricSourceType.FACE -import android.hardware.biometrics.BiometricSourceType.FINGERPRINT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FACE_LOCKED_OUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FINGERPRINT_LOCKED_OUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FACE_INPUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST -import com.android.keyguard.KeyguardUpdateMonitor -import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.systemui.bouncer.data.factory.BouncerMessageFactory import com.android.systemui.bouncer.shared.model.BouncerMessageModel -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.flags.SystemPropertiesHelper -import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository -import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository -import com.android.systemui.keyguard.data.repository.TrustRepository -import com.android.systemui.user.data.repository.UserRepository import javax.inject.Inject -import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onStart /** Provide different sources of messages that needs to be shown on the bouncer. */ interface BouncerMessageRepository { - /** - * Messages that are shown in response to the incorrect security attempts on the bouncer and - * primary authentication method being locked out, along with countdown messages before primary - * auth is active again. - */ - val primaryAuthMessage: Flow<BouncerMessageModel?> + val bouncerMessage: Flow<BouncerMessageModel> - /** - * Help messages that are shown to the user on how to successfully perform authentication using - * face. - */ - val faceAcquisitionMessage: Flow<BouncerMessageModel?> - - /** - * Help messages that are shown to the user on how to successfully perform authentication using - * fingerprint. - */ - val fingerprintAcquisitionMessage: Flow<BouncerMessageModel?> - - /** Custom message that is displayed when the bouncer is being shown to launch an app. */ - val customMessage: Flow<BouncerMessageModel?> - - /** - * Messages that are shown in response to biometric authentication attempts through face or - * fingerprint. - */ - val biometricAuthMessage: Flow<BouncerMessageModel?> - - /** Messages that are shown when certain auth flags are set. */ - val authFlagsMessage: Flow<BouncerMessageModel?> - - /** Messages that are show after biometrics are locked out temporarily or permanently */ - val biometricLockedOutMessage: Flow<BouncerMessageModel?> - - /** Set the value for [primaryAuthMessage] */ - fun setPrimaryAuthMessage(value: BouncerMessageModel?) - - /** Set the value for [faceAcquisitionMessage] */ - fun setFaceAcquisitionMessage(value: BouncerMessageModel?) - /** Set the value for [fingerprintAcquisitionMessage] */ - fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?) - - /** Set the value for [customMessage] */ - fun setCustomMessage(value: BouncerMessageModel?) - - /** - * Clear any previously set messages for [primaryAuthMessage], [faceAcquisitionMessage], - * [fingerprintAcquisitionMessage] & [customMessage] - */ - fun clearMessage() + fun setMessage(message: BouncerMessageModel) } -private const val SYS_BOOT_REASON_PROP = "sys.boot.reason.last" -private const val REBOOT_MAINLINE_UPDATE = "reboot,mainline_update" - @SysUISingleton -class BouncerMessageRepositoryImpl -@Inject -constructor( - trustRepository: TrustRepository, - biometricSettingsRepository: BiometricSettingsRepository, - updateMonitor: KeyguardUpdateMonitor, - private val bouncerMessageFactory: BouncerMessageFactory, - private val userRepository: UserRepository, - private val systemPropertiesHelper: SystemPropertiesHelper, - fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository, -) : BouncerMessageRepository { - - private val isAnyBiometricsEnabledAndEnrolled = - or( - biometricSettingsRepository.isFaceAuthEnrolledAndEnabled, - biometricSettingsRepository.isFingerprintEnrolledAndEnabled, - ) - - private val wasRebootedForMainlineUpdate - get() = systemPropertiesHelper.get(SYS_BOOT_REASON_PROP) == REBOOT_MAINLINE_UPDATE - - private val authFlagsBasedPromptReason: Flow<Int> = - combine( - biometricSettingsRepository.authenticationFlags, - trustRepository.isCurrentUserTrustManaged, - isAnyBiometricsEnabledAndEnrolled, - ::Triple - ) - .map { (flags, isTrustManaged, biometricsEnrolledAndEnabled) -> - val trustOrBiometricsAvailable = (isTrustManaged || biometricsEnrolledAndEnabled) - return@map if ( - trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterReboot - ) { - if (wasRebootedForMainlineUpdate) PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE - else PROMPT_REASON_RESTART - } else if (trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterTimeout) { - PROMPT_REASON_TIMEOUT - } else if (flags.isPrimaryAuthRequiredAfterDpmLockdown) { - PROMPT_REASON_DEVICE_ADMIN - } else if (isTrustManaged && flags.someAuthRequiredAfterUserRequest) { - PROMPT_REASON_TRUSTAGENT_EXPIRED - } else if (isTrustManaged && flags.someAuthRequiredAfterTrustAgentExpired) { - PROMPT_REASON_TRUSTAGENT_EXPIRED - } else if (trustOrBiometricsAvailable && flags.isInUserLockdown) { - PROMPT_REASON_USER_REQUEST - } else if ( - trustOrBiometricsAvailable && flags.primaryAuthRequiredForUnattendedUpdate - ) { - PROMPT_REASON_PREPARE_FOR_UPDATE - } else if ( - trustOrBiometricsAvailable && - flags.strongerAuthRequiredAfterNonStrongBiometricsTimeout - ) { - PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT - } else { - PROMPT_REASON_NONE - } - } - - private val biometricAuthReason: Flow<Int> = - conflatedCallbackFlow { - val callback = - object : KeyguardUpdateMonitorCallback() { - override fun onBiometricAuthFailed( - biometricSourceType: BiometricSourceType? - ) { - val promptReason = - if (biometricSourceType == FINGERPRINT) - PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT - else if ( - biometricSourceType == FACE && !updateMonitor.isFaceLockedOut - ) { - PROMPT_REASON_INCORRECT_FACE_INPUT - } else PROMPT_REASON_NONE - trySendWithFailureLogging(promptReason, TAG, "onBiometricAuthFailed") - } - - override fun onBiometricsCleared() { - trySendWithFailureLogging( - PROMPT_REASON_NONE, - TAG, - "onBiometricsCleared" - ) - } - - override fun onBiometricAcquired( - biometricSourceType: BiometricSourceType?, - acquireInfo: Int - ) { - trySendWithFailureLogging( - PROMPT_REASON_NONE, - TAG, - "clearBiometricPrompt for new auth session." - ) - } - - override fun onBiometricAuthenticated( - userId: Int, - biometricSourceType: BiometricSourceType?, - isStrongBiometric: Boolean - ) { - trySendWithFailureLogging( - PROMPT_REASON_NONE, - TAG, - "onBiometricAuthenticated" - ) - } - } - updateMonitor.registerCallback(callback) - awaitClose { updateMonitor.removeCallback(callback) } - } - .distinctUntilChanged() - - private val _primaryAuthMessage = MutableStateFlow<BouncerMessageModel?>(null) - override val primaryAuthMessage: Flow<BouncerMessageModel?> = _primaryAuthMessage +class BouncerMessageRepositoryImpl @Inject constructor() : BouncerMessageRepository { - private val _faceAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null) - override val faceAcquisitionMessage: Flow<BouncerMessageModel?> = _faceAcquisitionMessage + private val _bouncerMessage = MutableStateFlow(BouncerMessageModel()) + override val bouncerMessage: Flow<BouncerMessageModel> = _bouncerMessage - private val _fingerprintAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null) - override val fingerprintAcquisitionMessage: Flow<BouncerMessageModel?> = - _fingerprintAcquisitionMessage - - private val _customMessage = MutableStateFlow<BouncerMessageModel?>(null) - override val customMessage: Flow<BouncerMessageModel?> = _customMessage - - override val biometricAuthMessage: Flow<BouncerMessageModel?> = - biometricAuthReason - .map { - if (it == PROMPT_REASON_NONE) null - else - bouncerMessageFactory.createFromPromptReason( - it, - userRepository.getSelectedUserInfo().id - ) - } - .onStart { emit(null) } - .distinctUntilChanged() - - override val authFlagsMessage: Flow<BouncerMessageModel?> = - authFlagsBasedPromptReason - .map { - if (it == PROMPT_REASON_NONE) null - else - bouncerMessageFactory.createFromPromptReason( - it, - userRepository.getSelectedUserInfo().id - ) - } - .onStart { emit(null) } - .distinctUntilChanged() - - // TODO (b/262838215): Replace with DeviceEntryFaceAuthRepository when the new face auth system - // has been launched. - private val faceLockedOut: Flow<Boolean> = conflatedCallbackFlow { - val callback = - object : KeyguardUpdateMonitorCallback() { - override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType?) { - if (biometricSourceType == FACE) { - trySendWithFailureLogging( - updateMonitor.isFaceLockedOut, - TAG, - "face lock out state changed." - ) - } - } - } - updateMonitor.registerCallback(callback) - trySendWithFailureLogging(updateMonitor.isFaceLockedOut, TAG, "face lockout initial value") - awaitClose { updateMonitor.removeCallback(callback) } - } - - override val biometricLockedOutMessage: Flow<BouncerMessageModel?> = - combine(fingerprintAuthRepository.isLockedOut, faceLockedOut) { fp, face -> - return@combine if (fp) { - bouncerMessageFactory.createFromPromptReason( - PROMPT_REASON_FINGERPRINT_LOCKED_OUT, - userRepository.getSelectedUserInfo().id - ) - } else if (face) { - bouncerMessageFactory.createFromPromptReason( - PROMPT_REASON_FACE_LOCKED_OUT, - userRepository.getSelectedUserInfo().id - ) - } else null - } - - override fun setPrimaryAuthMessage(value: BouncerMessageModel?) { - _primaryAuthMessage.value = value - } - - override fun setFaceAcquisitionMessage(value: BouncerMessageModel?) { - _faceAcquisitionMessage.value = value - } - - override fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?) { - _fingerprintAcquisitionMessage.value = value - } - - override fun setCustomMessage(value: BouncerMessageModel?) { - _customMessage.value = value - } - - override fun clearMessage() { - _fingerprintAcquisitionMessage.value = null - _faceAcquisitionMessage.value = null - _primaryAuthMessage.value = null - _customMessage.value = null - } - - companion object { - const val TAG = "BouncerDetailedMessageRepository" + override fun setMessage(message: BouncerMessageModel) { + _bouncerMessage.value = message } } - -private fun or(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) = - flow.combine(anotherFlow) { a, b -> a || b } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 9b2f2baba94b..f3a463ba44a4 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -17,8 +17,8 @@ package com.android.systemui.bouncer.domain.interactor import android.content.Context -import com.android.systemui.res.R import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor +import com.android.systemui.authentication.domain.interactor.AuthenticationResult import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.bouncer.data.repository.BouncerRepository @@ -26,6 +26,7 @@ import com.android.systemui.classifier.FalsingClassifier import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey @@ -34,6 +35,7 @@ import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine @@ -92,9 +94,6 @@ constructor( /** Whether the pattern should be visible for the currently-selected user. */ val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible - /** The minimal length of a pattern. */ - val minPatternLength = authenticationInteractor.minPatternLength - init { if (flags.isEnabled()) { // Clear the message if moved from throttling to no-longer throttling. @@ -184,33 +183,44 @@ constructor( * dismissed and hidden. * * If [tryAutoConfirm] is `true`, authentication is attempted if and only if the auth method - * supports auto-confirming, and the input's length is at least the code's length. Otherwise, - * `null` is returned. + * supports auto-confirming, and the input's length is at least the required length. Otherwise, + * `AuthenticationResult.SKIPPED` is returned. * * @param input The input from the user to try to authenticate with. This can be a list of * different things, based on the current authentication method. * @param tryAutoConfirm `true` if called while the user inputs the code, without an explicit * request to validate. - * @return `true` if the authentication succeeded and the device is now unlocked; `false` when - * authentication failed, `null` if the check was not performed. + * @return The result of this authentication attempt. */ suspend fun authenticate( input: List<Any>, tryAutoConfirm: Boolean = false, - ): Boolean? { - val isAuthenticated = - authenticationInteractor.authenticate(input, tryAutoConfirm) ?: return null - - if (isAuthenticated) { - sceneInteractor.changeScene( - scene = SceneModel(SceneKey.Gone), - loggingReason = "successful authentication", - ) - } else { - showErrorMessage() + ): AuthenticationResult { + if (input.isEmpty()) { + return AuthenticationResult.SKIPPED } - - return isAuthenticated + // Switching to the application scope here since this method is often called from + // view-models, whose lifecycle (and thus scope) is shorter than this interactor. + // This allows the task to continue running properly even when the calling scope has been + // cancelled. + return applicationScope + .async { + val authResult = authenticationInteractor.authenticate(input, tryAutoConfirm) + when (authResult) { + // Authentication succeeded. + AuthenticationResult.SUCCEEDED -> + sceneInteractor.changeScene( + scene = SceneModel(SceneKey.Gone), + loggingReason = "successful authentication", + ) + // Authentication failed. + AuthenticationResult.FAILED -> showErrorMessage() + // Authentication skipped. + AuthenticationResult.SKIPPED -> if (!tryAutoConfirm) showErrorMessage() + } + authResult + } + .await() } /** @@ -221,21 +231,20 @@ constructor( * For example, if the user entered a pattern that's too short, the system can show the error * message without having the attempt trigger throttling. */ - suspend fun showErrorMessage() { + private suspend fun showErrorMessage() { repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod())) } - /** If the bouncer is showing, hides the bouncer and return to the lockscreen scene. */ - fun hide( - loggingReason: String, - ) { + /** Notifies the interactor that the input method editor has been hidden. */ + fun onImeHidden() { + // If the bouncer is showing, hide it and return to the lockscreen scene. if (sceneInteractor.desiredScene.value.key != SceneKey.Bouncer) { return } sceneInteractor.changeScene( scene = SceneModel(SceneKey.Lockscreen), - loggingReason = loggingReason, + loggingReason = "IME hidden", ) } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt index 497747f59034..aecfe1d2c9ad 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt @@ -16,16 +16,13 @@ package com.android.systemui.bouncer.domain.interactor -import android.os.Build import android.util.Log import com.android.systemui.CoreStartable import com.android.systemui.bouncer.data.repository.BouncerMessageRepository -import com.android.systemui.bouncer.shared.model.BouncerMessageModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch private val TAG = BouncerMessageAuditLogger::class.simpleName!! @@ -37,24 +34,8 @@ class BouncerMessageAuditLogger constructor( @Application private val scope: CoroutineScope, private val repository: BouncerMessageRepository, - private val interactor: BouncerMessageInteractor, ) : CoreStartable { override fun start() { - if (Build.isDebuggable()) { - collectAndLog(repository.biometricAuthMessage, "biometricMessage: ") - collectAndLog(repository.primaryAuthMessage, "primaryAuthMessage: ") - collectAndLog(repository.customMessage, "customMessage: ") - collectAndLog(repository.faceAcquisitionMessage, "faceAcquisitionMessage: ") - collectAndLog( - repository.fingerprintAcquisitionMessage, - "fingerprintAcquisitionMessage: " - ) - collectAndLog(repository.authFlagsMessage, "authFlagsMessage: ") - collectAndLog(interactor.bouncerMessage, "interactor.bouncerMessage: ") - } - } - - private fun collectAndLog(flow: Flow<BouncerMessageModel?>, context: String) { - scope.launch { flow.collect { Log.d(TAG, context + it) } } + scope.launch { repository.bouncerMessage.collect { Log.d(TAG, "bouncerMessage: $it") } } } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt index fe01d081e60c..f612f9afed77 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt @@ -16,55 +16,234 @@ package com.android.systemui.bouncer.domain.interactor +import android.hardware.biometrics.BiometricSourceType import android.os.CountDownTimer -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -import com.android.systemui.bouncer.data.factory.BouncerMessageFactory +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardSecurityModel.SecurityMode +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.keyguard.KeyguardUpdateMonitorCallback +import com.android.systemui.biometrics.data.repository.FacePropertyRepository +import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.bouncer.data.repository.BouncerMessageRepository import com.android.systemui.bouncer.shared.model.BouncerMessageModel +import com.android.systemui.bouncer.shared.model.Message import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES +import com.android.systemui.flags.SystemPropertiesHelper +import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository +import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.data.repository.TrustRepository +import com.android.systemui.res.R.string.bouncer_face_not_recognized +import com.android.systemui.res.R.string.keyguard_enter_password +import com.android.systemui.res.R.string.keyguard_enter_pattern +import com.android.systemui.res.R.string.keyguard_enter_pin +import com.android.systemui.res.R.string.kg_bio_too_many_attempts_password +import com.android.systemui.res.R.string.kg_bio_too_many_attempts_pattern +import com.android.systemui.res.R.string.kg_bio_too_many_attempts_pin +import com.android.systemui.res.R.string.kg_bio_try_again_or_password +import com.android.systemui.res.R.string.kg_bio_try_again_or_pattern +import com.android.systemui.res.R.string.kg_bio_try_again_or_pin +import com.android.systemui.res.R.string.kg_face_locked_out +import com.android.systemui.res.R.string.kg_fp_not_recognized +import com.android.systemui.res.R.string.kg_primary_auth_locked_out_password +import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pattern +import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pin +import com.android.systemui.res.R.string.kg_prompt_after_dpm_lock +import com.android.systemui.res.R.string.kg_prompt_after_update_password +import com.android.systemui.res.R.string.kg_prompt_after_update_pattern +import com.android.systemui.res.R.string.kg_prompt_after_update_pin +import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_password +import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_pattern +import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_pin +import com.android.systemui.res.R.string.kg_prompt_auth_timeout +import com.android.systemui.res.R.string.kg_prompt_password_auth_timeout +import com.android.systemui.res.R.string.kg_prompt_pattern_auth_timeout +import com.android.systemui.res.R.string.kg_prompt_pin_auth_timeout +import com.android.systemui.res.R.string.kg_prompt_reason_restart_password +import com.android.systemui.res.R.string.kg_prompt_reason_restart_pattern +import com.android.systemui.res.R.string.kg_prompt_reason_restart_pin +import com.android.systemui.res.R.string.kg_prompt_unattended_update +import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown +import com.android.systemui.res.R.string.kg_trust_agent_disabled +import com.android.systemui.res.R.string.kg_unlock_with_password_or_fp +import com.android.systemui.res.R.string.kg_unlock_with_pattern_or_fp +import com.android.systemui.res.R.string.kg_unlock_with_pin_or_fp +import com.android.systemui.res.R.string.kg_wrong_input_try_fp_suggestion +import com.android.systemui.res.R.string.kg_wrong_password_try_again +import com.android.systemui.res.R.string.kg_wrong_pattern_try_again +import com.android.systemui.res.R.string.kg_wrong_pin_try_again import com.android.systemui.user.data.repository.UserRepository +import com.android.systemui.util.kotlin.Quint import javax.inject.Inject import kotlin.math.roundToInt +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.stateIn + +private const val SYS_BOOT_REASON_PROP = "sys.boot.reason.last" +private const val REBOOT_MAINLINE_UPDATE = "reboot,mainline_update" +private const val TAG = "BouncerMessageInteractor" @SysUISingleton class BouncerMessageInteractor @Inject constructor( private val repository: BouncerMessageRepository, - private val factory: BouncerMessageFactory, private val userRepository: UserRepository, private val countDownTimerUtil: CountDownTimerUtil, private val featureFlags: FeatureFlags, + private val updateMonitor: KeyguardUpdateMonitor, + trustRepository: TrustRepository, + biometricSettingsRepository: BiometricSettingsRepository, + private val systemPropertiesHelper: SystemPropertiesHelper, + primaryBouncerInteractor: PrimaryBouncerInteractor, + @Application private val applicationScope: CoroutineScope, + private val facePropertyRepository: FacePropertyRepository, + deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository, + faceAuthRepository: DeviceEntryFaceAuthRepository, + private val securityModel: KeyguardSecurityModel, ) { + + private val isFingerprintAuthCurrentlyAllowed = + deviceEntryFingerprintAuthRepository.isLockedOut + .isFalse() + .and(biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed) + .stateIn(applicationScope, SharingStarted.Eagerly, false) + + private val currentSecurityMode + get() = securityModel.getSecurityMode(currentUserId) + private val currentUserId + get() = userRepository.getSelectedUserInfo().id + + private val kumCallback = + object : KeyguardUpdateMonitorCallback() { + override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) { + repository.setMessage( + when (biometricSourceType) { + BiometricSourceType.FINGERPRINT -> + incorrectFingerprintInput(currentSecurityMode) + BiometricSourceType.FACE -> + incorrectFaceInput( + currentSecurityMode, + isFingerprintAuthCurrentlyAllowed.value + ) + else -> + defaultMessage( + currentSecurityMode, + isFingerprintAuthCurrentlyAllowed.value + ) + } + ) + } + + override fun onBiometricAcquired( + biometricSourceType: BiometricSourceType?, + acquireInfo: Int + ) { + super.onBiometricAcquired(biometricSourceType, acquireInfo) + } + + override fun onBiometricAuthenticated( + userId: Int, + biometricSourceType: BiometricSourceType?, + isStrongBiometric: Boolean + ) { + repository.setMessage(defaultMessage) + } + } + + private val isAnyBiometricsEnabledAndEnrolled = + biometricSettingsRepository.isFaceAuthEnrolledAndEnabled.or( + biometricSettingsRepository.isFingerprintEnrolledAndEnabled + ) + + private val wasRebootedForMainlineUpdate + get() = systemPropertiesHelper.get(SYS_BOOT_REASON_PROP) == REBOOT_MAINLINE_UPDATE + + private val isFaceAuthClass3 + get() = facePropertyRepository.sensorInfo.value?.strength == SensorStrength.STRONG + + private val initialBouncerMessage: Flow<BouncerMessageModel> = + combine( + biometricSettingsRepository.authenticationFlags, + trustRepository.isCurrentUserTrustManaged, + isAnyBiometricsEnabledAndEnrolled, + deviceEntryFingerprintAuthRepository.isLockedOut, + faceAuthRepository.isLockedOut, + ::Quint + ) + .map { (flags, _, biometricsEnrolledAndEnabled, fpLockedOut, faceLockedOut) -> + val isTrustUsuallyManaged = trustRepository.isCurrentUserTrustUsuallyManaged.value + val trustOrBiometricsAvailable = + (isTrustUsuallyManaged || biometricsEnrolledAndEnabled) + return@map if ( + trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterReboot + ) { + if (wasRebootedForMainlineUpdate) { + authRequiredForMainlineUpdate(currentSecurityMode) + } else { + authRequiredAfterReboot(currentSecurityMode) + } + } else if (trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterTimeout) { + authRequiredAfterPrimaryAuthTimeout(currentSecurityMode) + } else if (flags.isPrimaryAuthRequiredAfterDpmLockdown) { + authRequiredAfterAdminLockdown(currentSecurityMode) + } else if ( + trustOrBiometricsAvailable && flags.primaryAuthRequiredForUnattendedUpdate + ) { + authRequiredForUnattendedUpdate(currentSecurityMode) + } else if (fpLockedOut) { + class3AuthLockedOut(currentSecurityMode) + } else if (faceLockedOut) { + if (isFaceAuthClass3) { + class3AuthLockedOut(currentSecurityMode) + } else { + faceLockedOut(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value) + } + } else if ( + trustOrBiometricsAvailable && + flags.strongerAuthRequiredAfterNonStrongBiometricsTimeout + ) { + nonStrongAuthTimeout( + currentSecurityMode, + isFingerprintAuthCurrentlyAllowed.value + ) + } else if (isTrustUsuallyManaged && flags.someAuthRequiredAfterUserRequest) { + trustAgentDisabled(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value) + } else if (isTrustUsuallyManaged && flags.someAuthRequiredAfterTrustAgentExpired) { + trustAgentDisabled(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value) + } else if (trustOrBiometricsAvailable && flags.isInUserLockdown) { + authRequiredAfterUserLockdown(currentSecurityMode) + } else { + defaultMessage + } + } + fun onPrimaryAuthLockedOut(secondsBeforeLockoutReset: Long) { if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return val callback = object : CountDownTimerCallback { override fun onFinish() { - repository.clearMessage() + repository.setMessage(defaultMessage) } override fun onTick(millisUntilFinished: Long) { val secondsRemaining = (millisUntilFinished / 1000.0).roundToInt() - val message = - factory.createFromPromptReason( - reason = PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT, - userId = userRepository.getSelectedUserInfo().id - ) - message?.message?.animate = false - message?.message?.formatterArgs = + val message = primaryAuthLockedOut(currentSecurityMode) + message.message?.animate = false + message.message?.formatterArgs = mutableMapOf<String, Any>(Pair("count", secondsRemaining)) - repository.setPrimaryAuthMessage(message) + repository.setMessage(message) } } countDownTimerUtil.startNewTimer(secondsBeforeLockoutReset * 1000, 1000, callback) @@ -73,104 +252,58 @@ constructor( fun onPrimaryAuthIncorrectAttempt() { if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return - repository.setPrimaryAuthMessage( - factory.createFromPromptReason( - PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, - userRepository.getSelectedUserInfo().id - ) + repository.setMessage( + incorrectSecurityInput(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value) ) } fun setFingerprintAcquisitionMessage(value: String?) { if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return - - repository.setFingerprintAcquisitionMessage( - if (value != null) { - factory.createFromPromptReason( - PROMPT_REASON_DEFAULT, - userRepository.getSelectedUserInfo().id, - secondaryMsgOverride = value - ) - } else { - null - } + repository.setMessage( + defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value) ) } fun setFaceAcquisitionMessage(value: String?) { if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return - - repository.setFaceAcquisitionMessage( - if (value != null) { - factory.createFromPromptReason( - PROMPT_REASON_DEFAULT, - userRepository.getSelectedUserInfo().id, - secondaryMsgOverride = value - ) - } else { - null - } + repository.setMessage( + defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value) ) } fun setCustomMessage(value: String?) { if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return - repository.setCustomMessage( - if (value != null) { - factory.createFromPromptReason( - PROMPT_REASON_DEFAULT, - userRepository.getSelectedUserInfo().id, - secondaryMsgOverride = value - ) - } else { - null - } + repository.setMessage( + defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value) ) } + private val defaultMessage: BouncerMessageModel + get() = defaultMessage(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value) + fun onPrimaryBouncerUserInput() { if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return - - repository.clearMessage() + repository.setMessage(defaultMessage) } - fun onBouncerBeingHidden() { - if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return + val bouncerMessage = repository.bouncerMessage - repository.clearMessage() - } + init { + updateMonitor.registerCallback(kumCallback) - private fun firstNonNullMessage( - oneMessageModel: Flow<BouncerMessageModel?>, - anotherMessageModel: Flow<BouncerMessageModel?> - ): Flow<BouncerMessageModel?> { - return oneMessageModel.combine(anotherMessageModel) { a, b -> a ?: b } + combine(primaryBouncerInteractor.isShowing, initialBouncerMessage) { showing, bouncerMessage + -> + if (showing) { + bouncerMessage + } else { + null + } + } + .filterNotNull() + .onEach { repository.setMessage(it) } + .launchIn(applicationScope) } - - // Null if feature flag is enabled which gets ignored always or empty bouncer message model that - // always maps to an empty string. - private fun nullOrEmptyMessage() = - flowOf( - if (featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) null else factory.emptyMessage() - ) - - val bouncerMessage = - listOf( - nullOrEmptyMessage(), - repository.primaryAuthMessage, - repository.biometricAuthMessage, - repository.fingerprintAcquisitionMessage, - repository.faceAcquisitionMessage, - repository.customMessage, - repository.authFlagsMessage, - repository.biometricLockedOutMessage, - userRepository.selectedUserInfo.map { - factory.createFromPromptReason(PROMPT_REASON_DEFAULT, it.id) - }, - ) - .reduce(::firstNonNullMessage) - .distinctUntilChanged() } interface CountDownTimerCallback { @@ -199,3 +332,272 @@ open class CountDownTimerUtil @Inject constructor() { .start() } } + +private fun Flow<Boolean>.or(anotherFlow: Flow<Boolean>) = + this.combine(anotherFlow) { a, b -> a || b } + +private fun Flow<Boolean>.and(anotherFlow: Flow<Boolean>) = + this.combine(anotherFlow) { a, b -> a && b } + +private fun Flow<Boolean>.isFalse() = this.map { !it } + +private fun defaultMessage( + securityMode: SecurityMode, + secondaryMessage: String?, + fpAuthIsAllowed: Boolean +): BouncerMessageModel { + return BouncerMessageModel( + message = + Message( + messageResId = defaultMessage(securityMode, fpAuthIsAllowed).message?.messageResId, + animate = false + ), + secondaryMessage = Message(message = secondaryMessage, animate = false) + ) +} + +private fun defaultMessage( + securityMode: SecurityMode, + fpAuthIsAllowed: Boolean +): BouncerMessageModel { + return if (fpAuthIsAllowed) { + defaultMessageWithFingerprint(securityMode) + } else + when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) + SecurityMode.Password -> Pair(keyguard_enter_password, 0) + SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + else -> Pair(0, 0) + }.toMessage() +} + +private fun defaultMessageWithFingerprint(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, 0) + SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, 0) + SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, 0) + else -> Pair(0, 0) + }.toMessage() +} + +private fun incorrectSecurityInput( + securityMode: SecurityMode, + fpAuthIsAllowed: Boolean +): BouncerMessageModel { + return if (fpAuthIsAllowed) { + incorrectSecurityInputWithFingerprint(securityMode) + } else + when (securityMode) { + SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, 0) + SecurityMode.Password -> Pair(kg_wrong_password_try_again, 0) + SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, 0) + else -> Pair(0, 0) + }.toMessage() +} + +private fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, kg_wrong_input_try_fp_suggestion) + SecurityMode.Password -> Pair(kg_wrong_password_try_again, kg_wrong_input_try_fp_suggestion) + SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, kg_wrong_input_try_fp_suggestion) + else -> Pair(0, 0) + }.toMessage() +} + +private fun incorrectFingerprintInput(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pattern) + SecurityMode.Password -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_password) + SecurityMode.PIN -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pin) + else -> Pair(0, 0) + }.toMessage() +} + +private fun incorrectFaceInput( + securityMode: SecurityMode, + fpAuthIsAllowed: Boolean +): BouncerMessageModel { + return if (fpAuthIsAllowed) incorrectFaceInputWithFingerprintAllowed(securityMode) + else + when (securityMode) { + SecurityMode.Pattern -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pattern) + SecurityMode.Password -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_password) + SecurityMode.PIN -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pin) + else -> Pair(0, 0) + }.toMessage() +} + +private fun incorrectFaceInputWithFingerprintAllowed( + securityMode: SecurityMode +): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, bouncer_face_not_recognized) + SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, bouncer_face_not_recognized) + SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, bouncer_face_not_recognized) + else -> Pair(0, 0) + }.toMessage() +} + +private fun biometricLockout(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_bio_too_many_attempts_pin) + else -> Pair(0, 0) + }.toMessage() +} + +private fun authRequiredAfterReboot(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_reason_restart_pattern) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_reason_restart_password) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin) + else -> Pair(0, 0) + }.toMessage() +} + +private fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_dpm_lock) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_dpm_lock) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock) + else -> Pair(0, 0) + }.toMessage() +} + +private fun authRequiredAfterUserLockdown(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern) + SecurityMode.Password -> + Pair(keyguard_enter_password, kg_prompt_after_user_lockdown_password) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin) + else -> Pair(0, 0) + }.toMessage() +} + +private fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_unattended_update) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_unattended_update) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_unattended_update) + else -> Pair(0, 0) + }.toMessage() +} + +private fun authRequiredForMainlineUpdate(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_update_pattern) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_update_password) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_update_pin) + else -> Pair(0, 0) + }.toMessage() +} + +private fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_pattern_auth_timeout) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_password_auth_timeout) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout) + else -> Pair(0, 0) + }.toMessage() +} + +private fun nonStrongAuthTimeout( + securityMode: SecurityMode, + fpAuthIsAllowed: Boolean +): BouncerMessageModel { + return if (fpAuthIsAllowed) { + nonStrongAuthTimeoutWithFingerprintAllowed(securityMode) + } else + when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_auth_timeout) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_auth_timeout) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_auth_timeout) + else -> Pair(0, 0) + }.toMessage() +} + +fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_prompt_auth_timeout) + SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_prompt_auth_timeout) + SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_prompt_auth_timeout) + else -> Pair(0, 0) + }.toMessage() +} + +private fun faceLockedOut( + securityMode: SecurityMode, + fpAuthIsAllowed: Boolean +): BouncerMessageModel { + return if (fpAuthIsAllowed) faceLockedOutButFingerprintAvailable(securityMode) + else + when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_face_locked_out) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_face_locked_out) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_face_locked_out) + else -> Pair(0, 0) + }.toMessage() +} + +private fun faceLockedOutButFingerprintAvailable(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_face_locked_out) + SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_face_locked_out) + SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_face_locked_out) + else -> Pair(0, 0) + }.toMessage() +} + +private fun class3AuthLockedOut(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_bio_too_many_attempts_pin) + else -> Pair(0, 0) + }.toMessage() +} + +private fun trustAgentDisabled( + securityMode: SecurityMode, + fpAuthIsAllowed: Boolean +): BouncerMessageModel { + return if (fpAuthIsAllowed) trustAgentDisabledWithFingerprintAllowed(securityMode) + else + when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_trust_agent_disabled) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_trust_agent_disabled) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_trust_agent_disabled) + else -> Pair(0, 0) + }.toMessage() +} + +private fun trustAgentDisabledWithFingerprintAllowed( + securityMode: SecurityMode +): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_trust_agent_disabled) + SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_trust_agent_disabled) + SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_trust_agent_disabled) + else -> Pair(0, 0) + }.toMessage() +} + +private fun primaryAuthLockedOut(securityMode: SecurityMode): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> + Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pattern) + SecurityMode.Password -> + Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_password) + SecurityMode.PIN -> + Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pin) + else -> Pair(0, 0) + }.toMessage() +} + +private fun Pair<Int, Int>.toMessage(): BouncerMessageModel { + return BouncerMessageModel( + message = Message(messageResId = this.first, animate = false), + secondaryMessage = Message(messageResId = this.second, animate = false) + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt index 0e9e96213340..7b169f4b0a57 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt @@ -22,7 +22,10 @@ import android.content.res.ColorStateList * Represents the message displayed on the bouncer. It has two parts, primary and a secondary * message */ -data class BouncerMessageModel(val message: Message? = null, val secondaryMessage: Message? = null) +data class BouncerMessageModel( + val message: Message? = null, + val secondaryMessage: Message? = null, +) /** * Representation of a single message on the bouncer. It can be either a string or a string resource diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt index e29d6bdf14ef..36e5db4c62e0 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt @@ -144,7 +144,6 @@ object KeyguardBouncerViewBinder { ) } } else { - bouncerMessageInteractor.onBouncerBeingHidden() securityContainerController.onBouncerVisibilityChanged( /* isVisible= */ false ) diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt index 4546bea3b89b..66c6162533bf 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt @@ -16,12 +16,21 @@ package com.android.systemui.bouncer.ui.viewmodel +import android.annotation.StringRes +import android.util.Log +import com.android.systemui.authentication.domain.interactor.AuthenticationResult +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch sealed class AuthMethodBouncerViewModel( + protected val viewModelScope: CoroutineScope, + protected val interactor: BouncerInteractor, + /** * Whether user input is enabled. * @@ -29,7 +38,6 @@ sealed class AuthMethodBouncerViewModel( * being able to attempt to unlock the device. */ val isInputEnabled: StateFlow<Boolean>, - private val interactor: BouncerInteractor, ) { private val _animateFailure = MutableStateFlow(false) @@ -42,12 +50,26 @@ sealed class AuthMethodBouncerViewModel( /** Whether the input method editor (for example, the software keyboard) is visible. */ private var isImeVisible: Boolean = false + /** The authentication method that corresponds to this view model. */ + abstract val authenticationMethod: AuthenticationMethodModel + /** - * Notifies that the failure animation has been shown. This should be called to consume a `true` - * value in [animateFailure]. + * String resource ID of the failure message to be shown during throttling. + * + * The message must include 2 number parameters: the first one indicating how many unsuccessful + * attempts were made, and the second one indicating in how many seconds throttling will expire. */ - fun onFailureAnimationShown() { - _animateFailure.value = false + @get:StringRes abstract val throttlingMessageId: Int + + /** Notifies that the UI has been shown to the user. */ + fun onShown() { + clearInput() + interactor.resetMessage() + } + + /** Notifies that the user has placed down a pointer. */ + fun onDown() { + interactor.onDown() } /** @@ -56,17 +78,44 @@ sealed class AuthMethodBouncerViewModel( */ fun onImeVisibilityChanged(isVisible: Boolean) { if (isImeVisible && !isVisible) { - // The IME has gone from visible to invisible, dismiss the bouncer. - interactor.hide( - loggingReason = "IME hidden", - ) + interactor.onImeHidden() } isImeVisible = isVisible } - /** Ask the UI to show the failure animation. */ - protected fun showFailureAnimation() { - _animateFailure.value = true + /** + * Notifies that the failure animation has been shown. This should be called to consume a `true` + * value in [animateFailure]. + */ + fun onFailureAnimationShown() { + _animateFailure.value = false + } + + /** Clears any previously-entered input. */ + protected abstract fun clearInput() + + /** Returns the input entered so far. */ + protected abstract fun getInput(): List<Any> + + /** + * Attempts to authenticate the user using the current input value. + * + * @see BouncerInteractor.authenticate + */ + protected fun tryAuthenticate(useAutoConfirm: Boolean = false) { + viewModelScope.launch { + Log.d("Danny", "tryAuthenticate(useAutoConfirm=$useAutoConfirm)") + val authenticationResult = interactor.authenticate(getInput(), useAutoConfirm) + Log.d("Danny", "result = $authenticationResult") + if (authenticationResult == AuthenticationResult.SKIPPED && useAutoConfirm) { + return@launch + } + _animateFailure.value = authenticationResult != AuthenticationResult.SUCCEEDED + + // TODO(b/291528545): On success, this should only be cleared after the view is animated + // away). + clearInput() + } } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt index 15d1dae239a0..782ead360d51 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt @@ -17,16 +17,19 @@ package com.android.systemui.bouncer.ui.viewmodel import android.content.Context -import com.android.systemui.res.R import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.scene.shared.flag.SceneContainerFlags import javax.inject.Inject import kotlin.math.ceil +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -35,6 +38,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.job import kotlinx.coroutines.launch /** Holds UI state and handles user input on bouncer UIs. */ @@ -44,8 +48,9 @@ class BouncerViewModel constructor( @Application private val applicationContext: Context, @Application private val applicationScope: CoroutineScope, + @Main private val mainDispatcher: CoroutineDispatcher, private val bouncerInteractor: BouncerInteractor, - private val authenticationInteractor: AuthenticationInteractor, + authenticationInteractor: AuthenticationInteractor, flags: SceneContainerFlags, ) { private val isInputEnabled: StateFlow<Boolean> = @@ -57,91 +62,45 @@ constructor( initialValue = !bouncerInteractor.isThrottled.value, ) - private val pin: PinBouncerViewModel by lazy { - PinBouncerViewModel( - applicationContext = applicationContext, - applicationScope = applicationScope, - interactor = bouncerInteractor, - isInputEnabled = isInputEnabled, - ) - } - - private val password: PasswordBouncerViewModel by lazy { - PasswordBouncerViewModel( - applicationScope = applicationScope, - interactor = bouncerInteractor, - isInputEnabled = isInputEnabled, - ) - } - - private val pattern: PatternBouncerViewModel by lazy { - PatternBouncerViewModel( - applicationContext = applicationContext, - applicationScope = applicationScope, - interactor = bouncerInteractor, - isInputEnabled = isInputEnabled, - ) - } - /** View-model for the current UI, based on the current authentication method. */ - val authMethod: StateFlow<AuthMethodBouncerViewModel?> = + val authMethodViewModel: StateFlow<AuthMethodBouncerViewModel?> = authenticationInteractor.authenticationMethod - .map { authenticationMethod -> - when (authenticationMethod) { - is AuthenticationMethodModel.Pin -> pin - is AuthenticationMethodModel.Password -> password - is AuthenticationMethodModel.Pattern -> pattern - else -> null - } - } + .map(::getChildViewModel) .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = null, ) + // Handle to the scope of the child ViewModel (stored in [authMethod]). + private var childViewModelScope: CoroutineScope? = null + init { if (flags.isEnabled()) { applicationScope.launch { - bouncerInteractor.isThrottled - .map { isThrottled -> - if (isThrottled) { - when (authenticationInteractor.getAuthenticationMethod()) { - is AuthenticationMethodModel.Pin -> - R.string.kg_too_many_failed_pin_attempts_dialog_message - is AuthenticationMethodModel.Password -> - R.string.kg_too_many_failed_password_attempts_dialog_message - is AuthenticationMethodModel.Pattern -> - R.string.kg_too_many_failed_pattern_attempts_dialog_message - else -> null - }?.let { stringResourceId -> - applicationContext.getString( - stringResourceId, - bouncerInteractor.throttling.value.failedAttemptCount, - ceil(bouncerInteractor.throttling.value.remainingMs / 1000f) - .toInt(), - ) - } + combine(bouncerInteractor.isThrottled, authMethodViewModel) { + isThrottled, + authMethodViewModel -> + if (isThrottled && authMethodViewModel != null) { + applicationContext.getString( + authMethodViewModel.throttlingMessageId, + bouncerInteractor.throttling.value.failedAttemptCount, + ceil(bouncerInteractor.throttling.value.remainingMs / 1000f) + .toInt(), + ) } else { null } } .distinctUntilChanged() - .collect { dialogMessageOrNull -> - if (dialogMessageOrNull != null) { - _throttlingDialogMessage.value = dialogMessageOrNull - } - } + .collect { dialogMessage -> _throttlingDialogMessage.value = dialogMessage } } } } /** The user-facing message to show in the bouncer. */ val message: StateFlow<MessageViewModel> = - combine( - bouncerInteractor.message, - bouncerInteractor.isThrottled, - ) { message, isThrottled -> + combine(bouncerInteractor.message, bouncerInteractor.isThrottled) { message, isThrottled -> toMessageViewModel(message, isThrottled) } .stateIn( @@ -186,6 +145,50 @@ constructor( ) } + private fun getChildViewModel( + authenticationMethod: AuthenticationMethodModel, + ): AuthMethodBouncerViewModel? { + // If the current child view-model matches the authentication method, reuse it instead of + // creating a new instance. + val childViewModel = authMethodViewModel.value + if (authenticationMethod == childViewModel?.authenticationMethod) { + return childViewModel + } + + childViewModelScope?.cancel() + val newViewModelScope = createChildCoroutineScope(applicationScope) + childViewModelScope = newViewModelScope + return when (authenticationMethod) { + is AuthenticationMethodModel.Pin -> + PinBouncerViewModel( + applicationContext = applicationContext, + viewModelScope = newViewModelScope, + interactor = bouncerInteractor, + isInputEnabled = isInputEnabled, + ) + is AuthenticationMethodModel.Password -> + PasswordBouncerViewModel( + viewModelScope = newViewModelScope, + interactor = bouncerInteractor, + isInputEnabled = isInputEnabled, + ) + is AuthenticationMethodModel.Pattern -> + PatternBouncerViewModel( + applicationContext = applicationContext, + viewModelScope = newViewModelScope, + interactor = bouncerInteractor, + isInputEnabled = isInputEnabled, + ) + else -> null + } + } + + private fun createChildCoroutineScope(parentScope: CoroutineScope): CoroutineScope { + return CoroutineScope( + SupervisorJob(parent = parentScope.coroutineContext.job) + mainDispatcher + ) + } + data class MessageViewModel( val text: String, diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt index 9e10f29a00f9..fe7741930d33 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt @@ -16,22 +16,24 @@ package com.android.systemui.bouncer.ui.viewmodel +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor +import com.android.systemui.res.R import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.launch /** Holds UI state and handles user input for the password bouncer UI. */ class PasswordBouncerViewModel( - private val applicationScope: CoroutineScope, - private val interactor: BouncerInteractor, + viewModelScope: CoroutineScope, + interactor: BouncerInteractor, isInputEnabled: StateFlow<Boolean>, ) : AuthMethodBouncerViewModel( - isInputEnabled = isInputEnabled, + viewModelScope = viewModelScope, interactor = interactor, + isInputEnabled = isInputEnabled, ) { private val _password = MutableStateFlow("") @@ -39,10 +41,16 @@ class PasswordBouncerViewModel( /** The password entered so far. */ val password: StateFlow<String> = _password.asStateFlow() - /** Notifies that the UI has been shown to the user. */ - fun onShown() { + override val authenticationMethod = AuthenticationMethodModel.Password + + override val throttlingMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message + + override fun clearInput() { _password.value = "" - interactor.resetMessage() + } + + override fun getInput(): List<Any> { + return _password.value.toCharArray().toList() } /** Notifies that the user has changed the password input. */ @@ -60,17 +68,8 @@ class PasswordBouncerViewModel( /** Notifies that the user has pressed the key for attempting to authenticate the password. */ fun onAuthenticateKeyPressed() { - val password = _password.value.toCharArray().toList() - if (password.isEmpty()) { - return - } - - _password.value = "" - - applicationScope.launch { - if (interactor.authenticate(password) != true) { - showFailureAnimation() - } + if (_password.value.isNotEmpty()) { + tryAuthenticate() } } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt index 497276b47996..52adf54f8d24 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt @@ -18,8 +18,10 @@ package com.android.systemui.bouncer.ui.viewmodel import android.content.Context import android.util.TypedValue +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.bouncer.domain.interactor.BouncerInteractor +import com.android.systemui.res.R import kotlin.math.max import kotlin.math.min import kotlin.math.pow @@ -31,18 +33,18 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch /** Holds UI state and handles user input for the pattern bouncer UI. */ class PatternBouncerViewModel( private val applicationContext: Context, - private val applicationScope: CoroutineScope, - private val interactor: BouncerInteractor, + viewModelScope: CoroutineScope, + interactor: BouncerInteractor, isInputEnabled: StateFlow<Boolean>, ) : AuthMethodBouncerViewModel( - isInputEnabled = isInputEnabled, + viewModelScope = viewModelScope, interactor = interactor, + isInputEnabled = isInputEnabled, ) { /** The number of columns in the dot grid. */ @@ -58,7 +60,7 @@ class PatternBouncerViewModel( _selectedDots .map { it.toList() } .stateIn( - scope = applicationScope, + scope = viewModelScope, started = SharingStarted.WhileSubscribed(), initialValue = emptyList(), ) @@ -76,15 +78,9 @@ class PatternBouncerViewModel( /** Whether the pattern itself should be rendered visibly. */ val isPatternVisible: StateFlow<Boolean> = interactor.isPatternVisible - /** Notifies that the UI has been shown to the user. */ - fun onShown() { - interactor.resetMessage() - } + override val authenticationMethod = AuthenticationMethodModel.Pattern - /** Notifies that the user has placed down a pointer, not necessarily dragging just yet. */ - fun onDown() { - interactor.onDown() - } + override val throttlingMessageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message /** Notifies that the user has started a drag gesture across the dot grid. */ fun onDragStart() { @@ -164,24 +160,23 @@ class PatternBouncerViewModel( /** Notifies that the user has ended the drag gesture across the dot grid. */ fun onDragEnd() { - val pattern = _selectedDots.value.map { it.toCoordinate() } - + val pattern = getInput() if (pattern.size == 1) { // Single dot patterns are treated as erroneous/false taps: interactor.onFalseUserInput() } + tryAuthenticate() + } + + override fun clearInput() { _dots.value = defaultDots() _currentDot.value = null _selectedDots.value = linkedSetOf() + } - applicationScope.launch { - if (pattern.size < interactor.minPatternLength) { - interactor.showErrorMessage() - } else if (interactor.authenticate(pattern) != true) { - showFailureAnimation() - } - } + override fun getInput(): List<Any> { + return _selectedDots.value.map(PatternDotViewModel::toCoordinate) } private fun defaultDots(): List<PatternDotViewModel> { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt index 8e6421ed3f0a..b90e25526726 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt @@ -18,7 +18,9 @@ package com.android.systemui.bouncer.ui.viewmodel import android.content.Context import com.android.keyguard.PinShapeAdapter +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor +import com.android.systemui.res.R import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -26,18 +28,18 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch /** Holds UI state and handles user input for the PIN code bouncer UI. */ class PinBouncerViewModel( applicationContext: Context, - private val applicationScope: CoroutineScope, - private val interactor: BouncerInteractor, + viewModelScope: CoroutineScope, + interactor: BouncerInteractor, isInputEnabled: StateFlow<Boolean>, ) : AuthMethodBouncerViewModel( - isInputEnabled = isInputEnabled, + viewModelScope = viewModelScope, interactor = interactor, + isInputEnabled = isInputEnabled, ) { val pinShapes = PinShapeAdapter(applicationContext) @@ -61,7 +63,7 @@ class PinBouncerViewModel( ) } .stateIn( - scope = applicationScope, + scope = viewModelScope, // Make sure this is kept as WhileSubscribed or we can run into a bug where the // downstream continues to receive old/stale/cached values. started = SharingStarted.WhileSubscribed(), @@ -73,21 +75,14 @@ class PinBouncerViewModel( interactor.isAutoConfirmEnabled .map { if (it) ActionButtonAppearance.Hidden else ActionButtonAppearance.Shown } .stateIn( - scope = applicationScope, + scope = viewModelScope, started = SharingStarted.Eagerly, initialValue = ActionButtonAppearance.Hidden, ) - /** Notifies that the UI has been shown to the user. */ - fun onShown() { - clearPinInput() - interactor.resetMessage() - } + override val authenticationMethod = AuthenticationMethodModel.Pin - /** Notifies that the user has placed down a pointer. */ - fun onDown() { - interactor.onDown() - } + override val throttlingMessageId = R.string.kg_too_many_failed_pin_attempts_dialog_message /** Notifies that the user clicked on a PIN button with the given digit value. */ fun onPinButtonClicked(input: Int) { @@ -109,7 +104,8 @@ class PinBouncerViewModel( /** Notifies that the user long-pressed the backspace button. */ fun onBackspaceButtonLongPressed() { - clearPinInput() + clearInput() + interactor.clearMessage() } /** Notifies that the user clicked the "enter" button. */ @@ -117,24 +113,12 @@ class PinBouncerViewModel( tryAuthenticate(useAutoConfirm = false) } - private fun clearPinInput() { + override fun clearInput() { mutablePinInput.value = mutablePinInput.value.clearAll() } - private fun tryAuthenticate(useAutoConfirm: Boolean) { - val pinCode = mutablePinInput.value.getPin() - - applicationScope.launch { - val isSuccess = interactor.authenticate(pinCode, useAutoConfirm) ?: return@launch - - if (!isSuccess) { - showFailureAnimation() - } - - // TODO(b/291528545): this should not be cleared on success (at least until the view - // is animated away). - clearPinInput() - } + override fun getInput(): List<Any> { + return mutablePinInput.value.getPin() } private fun computeBackspaceButtonAppearance( diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index a325ee203838..283a07b206b9 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -38,6 +38,7 @@ import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider; import com.android.systemui.biometrics.FingerprintReEnrollNotification; import com.android.systemui.biometrics.UdfpsDisplayModeProvider; import com.android.systemui.biometrics.dagger.BiometricsModule; +import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule; import com.android.systemui.bouncer.ui.BouncerViewModule; import com.android.systemui.classifier.FalsingModule; import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule; @@ -122,6 +123,7 @@ import com.android.systemui.temporarydisplay.dagger.TemporaryDisplayModule; import com.android.systemui.tuner.dagger.TunerModule; import com.android.systemui.unfold.SysUIUnfoldModule; import com.android.systemui.user.UserModule; +import com.android.systemui.user.domain.UserDomainLayerModule; import com.android.systemui.util.concurrency.SysUIConcurrencyModule; import com.android.systemui.util.dagger.UtilModule; import com.android.systemui.util.kotlin.CoroutinesModule; @@ -162,6 +164,7 @@ import javax.inject.Named; AssistModule.class, AuthenticationModule.class, BiometricsModule.class, + BiometricsDomainLayerModule.class, BouncerViewModule.class, ClipboardOverlayModule.class, ClockRegistryModule.class, @@ -209,6 +212,7 @@ import javax.inject.Named; TelephonyRepositoryModule.class, TemporaryDisplayModule.class, TunerModule.class, + UserDomainLayerModule.class, UserModule.class, UtilModule.class, NoteTaskModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index e170849e487f..3b70555ad32a 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -124,6 +124,11 @@ object Flags { val NOTIFICATION_GROUP_EXPANSION_CHANGE = unreleasedFlag("notification_group_expansion_change") + // TODO(b/301955929) + @JvmField + val NOTIF_LS_BACKGROUND_THREAD = + unreleasedFlag("notification_lockscreen_mgr_bg_thread") + // 200 - keyguard/lockscreen // ** Flag retired ** // public static final BooleanFlag KEYGUARD_LAYOUT = @@ -248,7 +253,8 @@ object Flags { /** Provide new auth messages on the bouncer. */ // TODO(b/277961132): Tracking bug. - @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages") + @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages", + teamfood = true) /** Keyguard Migration */ @@ -292,6 +298,11 @@ object Flags { @JvmField val MIGRATE_KEYGUARD_STATUS_BAR_VIEW = unreleasedFlag("migrate_keyguard_status_bar_view") + /** Migrate clocks from keyguard status view to keyguard root view*/ + // TODO(b/301502635): Tracking Bug. + @JvmField val MIGRATE_CLOCKS_TO_BLUEPRINT = + unreleasedFlag("migrate_clocks_to_blueprint") + /** Enables preview loading animation in the wallpaper picker. */ // TODO(b/274443705): Tracking Bug @JvmField @@ -757,6 +768,9 @@ object Flags { // TODO(b/289573946): Tracking Bug @JvmField val PRECOMPUTED_TEXT = unreleasedFlag("precomputed_text", teamfood = true) + // TODO(b/302087895): Tracking Bug + @JvmField val CALL_LAYOUT_ASYNC_SET_DATA = unreleasedFlag("call_layout_async_set_data") + // 2900 - CentralSurfaces-related flags // TODO(b/285174336): Tracking Bug @@ -798,7 +812,7 @@ object Flags { /** Enable showing a dialog when clicking on Quick Settings bluetooth tile. */ @JvmField - val BLUETOOTH_QS_TILE_DIALOG = unreleasedFlag("bluetooth_qs_tile_dialog") + val BLUETOOTH_QS_TILE_DIALOG = unreleasedFlag("bluetooth_qs_tile_dialog", teamfood = true) // TODO(b/300995746): Tracking Bug /** Enable communal hub features. */ diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt index cc51d21744e8..d9b2c39b9bef 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt @@ -36,8 +36,8 @@ import kotlinx.coroutines.launch * * @param[sliderStateListener] Listener of the slider state. * @param[sliderEventProducer] Producer of slider events arising from the slider. - * @property[scope] [CoroutineScope] where the collection of slider events and the launch of timer - * jobs occur. + * @param[mainDispatcher] [CoroutineDispatcher] used to launch coroutines for the collection of + * slider events and the launch of timer jobs. * @property[config] Configuration parameters of the slider tracker. */ class SeekableSliderTracker( diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt index e1f57089d792..002b5aa743ba 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt @@ -27,7 +27,8 @@ import kotlinx.coroutines.launch * The tracker maintains a state machine operated by slider events coming from a * [SliderEventProducer]. An action is executed in each state via a [SliderListener]. * - * @param[scope] [CoroutineScope] to launch the collection of [SliderEvent]. + * @property[scope] [CoroutineScope] to launch the collection of [SliderEvent] and state machine + * logic. * @property[sliderListener] [SliderListener] to execute actions on a given [SliderState]. * @property[eventProducer] Producer of [SliderEvent] to iterate over a state machine. */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index efd25d5d69e6..b506a363d3d4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -3798,6 +3798,13 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } /** + * Notify whether keyguard has created a remote animation runner for next app launch. + */ + public void launchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) { + mKeyguardTransitions.setLaunchingActivityOverLockscreen(isLaunchingActivityOverLockscreen); + } + + /** * Implementation of RemoteAnimationRunner that creates a new * {@link ActivityLaunchAnimator.Runner} whenever onAnimationStart is called, delegating the * remote animation methods to that runner. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt index 4e71ef4e34f2..cc36961ff973 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt @@ -78,6 +78,7 @@ constructor( component.getControlsListingController().getOrNull()?.getCurrentServices() val hasFavorites = component.getControlsController().getOrNull()?.getFavorites()?.isNotEmpty() == true + val hasPanels = currentServices?.any { it.panelActivity != null } == true val componentPackageName = component.getPackageName() when { currentServices.isNullOrEmpty() && !componentPackageName.isNullOrEmpty() -> { @@ -100,8 +101,8 @@ constructor( ), ) } - !hasFavorites -> { - // Home app installed but no favorites selected. + !hasFavorites && !hasPanels -> { + // Home app installed but no favorites selected or panel activities available. val activityClass = component.getControlsUiController().get().resolveActivity() return disabledPickerState( explanation = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt index 00036ce5e226..6522439460e4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt @@ -29,6 +29,7 @@ import com.android.systemui.keyguard.shared.model.TrustModel import com.android.systemui.user.data.repository.UserRepository import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted @@ -36,6 +37,8 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart @@ -44,19 +47,25 @@ import kotlinx.coroutines.flow.stateIn /** Encapsulates any state relevant to trust agents and trust grants. */ interface TrustRepository { + /** Flow representing whether the current user has enabled any trust agents. */ + val isCurrentUserTrustUsuallyManaged: StateFlow<Boolean> + /** Flow representing whether the current user is trusted. */ val isCurrentUserTrusted: Flow<Boolean> /** Flow representing whether active unlock is running for the current user. */ val isCurrentUserActiveUnlockRunning: Flow<Boolean> - /** Reports that whether trust is managed has changed for the current user. */ + /** + * Reports whether a trust agent is currently enabled and managing the trust of the current user + */ val isCurrentUserTrustManaged: StateFlow<Boolean> /** A trust agent is requesting to dismiss the keyguard from a trust change. */ val trustAgentRequestingToDismissKeyguard: Flow<TrustModel> } +@OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class TrustRepositoryImpl @Inject @@ -174,6 +183,11 @@ constructor( } .map { it!! } + override val isCurrentUserTrustUsuallyManaged: StateFlow<Boolean> = + userRepository.selectedUserInfo + .flatMapLatest { flowOf(trustManager.isTrustUsuallyManaged(it.id)) } + .stateIn(applicationScope, started = SharingStarted.Eagerly, false) + private fun isUserTrustManaged(userId: Int) = trustManagedForUser[userId]?.isTrustManaged ?: false 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 e59fc15c3876..a48e56a9f158 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 @@ -606,6 +606,9 @@ constructor( } ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex) } + } else if (isRtl && mediaContent.childCount > 0) { + // In RTL, Scroll to the first player as it is the rightmost player in media carousel. + mediaCarouselScrollHandler.scrollToPlayer(destIndex = 0) } // Check postcondition: mediaContent should have the same number of children as there // are diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt index 126115294e75..02f0d12c7069 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt @@ -282,13 +282,14 @@ class MediaCarouselScrollHandler( // It's an up and the fling didn't take it above val relativePos = scrollView.relativeScrollX % playerWidthPlusPadding val scrollXAmount: Int = - if (isRtl xor (relativePos > playerWidthPlusPadding / 2)) { + if (relativePos > playerWidthPlusPadding / 2) { playerWidthPlusPadding - relativePos } else { -1 * relativePos } if (scrollXAmount != 0) { - val newScrollX = scrollView.relativeScrollX + scrollXAmount + val dx = if (isRtl) -scrollXAmount else scrollXAmount + val newScrollX = scrollView.scrollX + dx // Delay the scrolling since scrollView calls springback which cancels // the animation again.. mainExecutor.execute { scrollView.smoothScrollTo(newScrollX, scrollView.scrollY) } @@ -539,7 +540,8 @@ class MediaCarouselScrollHandler( // If the removed media item is "left of" the active one (in an absolute sense), we need to // scroll the view to keep that player in view. This is because scroll position is always // calculated from left to right. - val leftOfActive = if (isRtl) !beforeActive else beforeActive + // For RTL, we need to scroll if the visible media player is the last item. + val leftOfActive = if (isRtl && visibleMediaIndex != 0) !beforeActive else beforeActive if (leftOfActive) { scrollView.scrollX = Math.max(scrollView.scrollX - playerWidthPlusPadding, 0) } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt index 0e0746590776..10512f1c4aed 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt @@ -74,6 +74,14 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 scrollX = transformScrollX(value) } + override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { + if (!isLaidOut && isLayoutRtl) { + // Reset scroll because onLayout method overrides RTL scroll if view was not laid out. + mScrollX = relativeScrollX + } + super.onLayout(changed, l, t, r, b) + } + /** Allow all scrolls to go through, use base implementation */ override fun scrollTo(x: Int, y: Int) { if (mScrollX != x || mScrollY != y) { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index cb52a5fb53fa..ad1c77d05534 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -795,7 +795,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements // Reset user rotation pref to match that of the WindowManager if starting in locked // mode. This will automatically happen when switching from auto-rotate to locked mode. if (display != null && rotationButtonController.isRotationLocked()) { - rotationButtonController.setRotationLockedAtAngle(display.getRotation()); + rotationButtonController.setRotationLockedAtAngle( + display.getRotation(), /* caller= */ "NavigationBar#onViewAttached"); } } else { mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS; diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 826f75f2f63b..19012e29b184 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -78,6 +78,14 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { private int mMinRows = 1; private int mMaxColumns = TileLayout.NO_MAX_COLUMNS; + /** + * it's fine to read this value when class is initialized because SysUI is always restarted + * when running tests in test harness, see SysUiTestIsolationRule. This check is done quite + * often - with every shade open action - so we don't want to potentially make it less + * performant only for test use case + */ + private boolean mRunningInTestHarness = ActivityManager.isRunningInTestHarness(); + public PagedTileLayout(Context context, AttributeSet attrs) { super(context, attrs); mScroller = new Scroller(context, SCROLL_CUBIC); @@ -590,11 +598,11 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { private boolean shouldNotRunAnimation(Set<String> tilesToReveal) { boolean noAnimationNeeded = tilesToReveal.isEmpty() || mPages.size() < 2; boolean scrollingInProgress = getScrollX() != 0 || !beginFakeDrag(); - // isRunningInTestHarness() to disable animation in functional testing as it caused + // checking mRunningInTestHarness to disable animation in functional testing as it caused // flakiness and is not needed there. Alternative solutions were more complex and would // still be either potentially flaky or modify internal data. // For more info see b/253493927 and b/293234595 - return noAnimationNeeded || scrollingInProgress || ActivityManager.isRunningInTestHarness(); + return noAnimationNeeded || scrollingInProgress || mRunningInTestHarness; } private int sanitizePageAction(int action) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt index b52554d4e997..11759f7ce06b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt @@ -18,6 +18,8 @@ package com.android.systemui.qs import android.content.Context import android.util.AttributeSet +import com.android.systemui.flags.Flags +import com.android.systemui.flags.ViewRefactorFlag import com.android.systemui.res.R open class SideLabelTileLayout( @@ -25,9 +27,25 @@ open class SideLabelTileLayout( attrs: AttributeSet? ) : TileLayout(context, attrs) { + private final val isSmallLandscapeLockscreenEnabled = + ViewRefactorFlag(flag = Flags.LOCKSCREEN_ENABLE_LANDSCAPE).isEnabled + override fun updateResources(): Boolean { return super.updateResources().also { - mMaxAllowedRows = context.resources.getInteger(R.integer.quick_settings_max_rows) + // TODO (b/293252410) remove condition here when flag is launched + // Instead update quick_settings_max_rows resource to be the same as + // small_land_lockscreen_quick_settings_max_rows whenever is_small_screen_landscape is + // true. Then, only use quick_settings_max_rows resource. + val useSmallLandscapeLockscreenResources = + isSmallLandscapeLockscreenEnabled && + mContext.resources.getBoolean(R.bool.is_small_screen_landscape) + + mMaxAllowedRows = if (useSmallLandscapeLockscreenResources) { + context.resources.getInteger( + R.integer.small_land_lockscreen_quick_settings_max_rows) + } else { + context.resources.getInteger(R.integer.quick_settings_max_rows) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index 9dc6aeebc41c..9ba16454aea6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -15,7 +15,9 @@ import androidx.annotation.Nullable; import com.android.internal.logging.UiEventLogger; import com.android.systemui.FontSizeUtils; +import com.android.systemui.flags.ViewRefactorFlag; import com.android.systemui.res.R; +import com.android.systemui.flags.Flags; import com.android.systemui.qs.QSPanel.QSTileLayout; import com.android.systemui.qs.QSPanelControllerBase.TileRecord; import com.android.systemui.qs.tileimpl.HeightOverrideable; @@ -51,8 +53,9 @@ public class TileLayout extends ViewGroup implements QSTileLayout { protected int mResourceColumns; private float mSquishinessFraction = 1f; protected int mLastTileBottom; - protected TextView mTempTextView; + private final Boolean mIsSmallLandscapeLockscreenEnabled = + new ViewRefactorFlag(Flags.LOCKSCREEN_ENABLE_LANDSCAPE).isEnabled(); public TileLayout(Context context) { this(context, null); @@ -127,12 +130,18 @@ public class TileLayout extends ViewGroup implements QSTileLayout { public boolean updateResources() { Resources res = getResources(); - mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns)); + int columns = useSmallLandscapeLockscreenResources() + ? res.getInteger(R.integer.small_land_lockscreen_quick_settings_num_columns) + : res.getInteger(R.integer.quick_settings_num_columns); + mResourceColumns = Math.max(1, columns); mResourceCellHeight = res.getDimensionPixelSize(mResourceCellHeightResId); mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal); mSidePadding = useSidePadding() ? mCellMarginHorizontal / 2 : 0; mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical); - mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows)); + int rows = useSmallLandscapeLockscreenResources() + ? res.getInteger(R.integer.small_land_lockscreen_quick_settings_max_rows) + : res.getInteger(R.integer.quick_settings_max_rows); + mMaxAllowedRows = Math.max(1, rows); if (mLessRows) { mMaxAllowedRows = Math.max(mMinRows, mMaxAllowedRows - 1); } @@ -146,6 +155,17 @@ public class TileLayout extends ViewGroup implements QSTileLayout { return false; } + // TODO (b/293252410) remove condition here when flag is launched + // Instead update quick_settings_num_columns and quick_settings_max_rows to be the same as + // the small_land_lockscreen_quick_settings_num_columns or + // small_land_lockscreen_quick_settings_max_rows respectively whenever + // is_small_screen_landscape is true. + // Then, only use quick_settings_num_columns and quick_settings_max_rows. + private boolean useSmallLandscapeLockscreenResources() { + return mIsSmallLandscapeLockscreenEnabled + && mContext.getResources().getBoolean(R.bool.is_small_screen_landscape); + } + protected boolean useSidePadding() { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index c9a24d6a4608..e5af8e6df932 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -44,6 +44,8 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder; import com.android.internal.logging.UiEventLogger; import com.android.systemui.FontSizeUtils; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.res.R; import com.android.systemui.qs.QSEditEvent; import com.android.systemui.qs.QSHost; @@ -117,12 +119,14 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private TextView mTempTextView; private int mMinTileViewHeight; + private final boolean mIsSmallLandscapeLockscreenEnabled; @Inject public TileAdapter( @QSThemedContext Context context, QSHost qsHost, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + FeatureFlags featureFlags) { mContext = context; mHost = qsHost; mUiEventLogger = uiEventLogger; @@ -130,7 +134,12 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta mDecoration = new TileItemDecoration(context); mMarginDecoration = new MarginTileDecoration(); mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles); - mNumColumns = context.getResources().getInteger(NUM_COLUMNS_ID); + mIsSmallLandscapeLockscreenEnabled = + featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE); + mNumColumns = useSmallLandscapeLockscreenResources() + ? context.getResources().getInteger( + R.integer.small_land_lockscreen_quick_settings_num_columns) + : context.getResources().getInteger(NUM_COLUMNS_ID); mAccessibilityDelegate = new TileAdapterDelegate(); mSizeLookup.setSpanIndexCacheEnabled(true); mTempTextView = new TextView(context); @@ -153,7 +162,10 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta * @return {@code true} if the number of columns changed, {@code false} otherwise */ public boolean updateNumColumns() { - int numColumns = mContext.getResources().getInteger(NUM_COLUMNS_ID); + int numColumns = useSmallLandscapeLockscreenResources() + ? mContext.getResources().getInteger( + R.integer.small_land_lockscreen_quick_settings_num_columns) + : mContext.getResources().getInteger(NUM_COLUMNS_ID); if (numColumns != mNumColumns) { mNumColumns = numColumns; return true; @@ -162,6 +174,17 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta } } + // TODO (b/293252410) remove condition here when flag is launched + // Instead update quick_settings_num_columns and quick_settings_max_rows to be the same as + // the small_land_lockscreen_quick_settings_num_columns or + // small_land_lockscreen_quick_settings_max_rows respectively whenever + // is_small_screen_landscape is true. + // Then, only use quick_settings_num_columns and quick_settings_max_rows. + private boolean useSmallLandscapeLockscreenResources() { + return mIsSmallLandscapeLockscreenEnabled + && mContext.getResources().getBoolean(R.bool.is_small_screen_landscape); + } + public int getNumColumns() { return mNumColumns; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index 1be514db44c4..d862f563c8f9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -42,6 +42,8 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.systemui.res.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; @@ -50,6 +52,7 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialogViewModel; import com.android.systemui.statusbar.policy.BluetoothController; import java.util.List; @@ -72,6 +75,10 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { private final Executor mExecutor; + private final BluetoothTileDialogViewModel mDialogViewModel; + + private final FeatureFlags mFeatureFlags; + @Inject public BluetoothTile( QSHost host, @@ -83,13 +90,17 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, - BluetoothController bluetoothController + BluetoothController bluetoothController, + FeatureFlags featureFlags, + BluetoothTileDialogViewModel dialogViewModel ) { super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mController = bluetoothController; mController.observe(getLifecycle(), mCallback); mExecutor = new HandlerExecutor(mainHandler); + mFeatureFlags = featureFlags; + mDialogViewModel = dialogViewModel; } @Override @@ -99,11 +110,15 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { @Override protected void handleClick(@Nullable View view) { - // Secondary clicks are header clicks, just toggle. - final boolean isEnabled = mState.value; - // Immediately enter transient enabling state when turning bluetooth on. - refreshState(isEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING); - mController.setBluetoothEnabled(!isEnabled); + if (mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG)) { + mDialogViewModel.showDialog(mContext, view); + } else { + // Secondary clicks are header clicks, just toggle. + final boolean isEnabled = mState.value; + // Immediately enter transient enabling state when turning bluetooth on. + refreshState(isEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING); + mController.setBluetoothEnabled(!isEnabled); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java index b393f39719e7..736f035ace6f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java @@ -25,6 +25,7 @@ import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.os.Handler; import android.os.Looper; import android.provider.DeviceConfig; +import android.safetycenter.SafetyCenterManager; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; @@ -60,10 +61,11 @@ public class CameraToggleTile extends SensorPrivacyToggleTile { ActivityStarter activityStarter, QSLogger qsLogger, IndividualSensorPrivacyController sensorPrivacyController, - KeyguardStateController keyguardStateController) { + KeyguardStateController keyguardStateController, + SafetyCenterManager safetyCenterManager) { super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger, sensorPrivacyController, - keyguardStateController); + keyguardStateController, safetyCenterManager); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java index 7a7dbbc79558..92338cbc3410 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java @@ -25,6 +25,7 @@ import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.os.Handler; import android.os.Looper; import android.provider.DeviceConfig; +import android.safetycenter.SafetyCenterManager; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; @@ -60,10 +61,11 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile { ActivityStarter activityStarter, QSLogger qsLogger, IndividualSensorPrivacyController sensorPrivacyController, - KeyguardStateController keyguardStateController) { + KeyguardStateController keyguardStateController, + SafetyCenterManager safetyCenterManager) { super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger, sensorPrivacyController, - keyguardStateController); + keyguardStateController, safetyCenterManager); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index ea162fa2921f..5a9500494de0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -133,7 +133,7 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements @Override protected void handleClick(@Nullable View view) { final boolean newState = !mState.value; - mController.setRotationLocked(!newState); + mController.setRotationLocked(!newState, /* caller= */ "RotationLockTile#handleClick"); refreshState(newState); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java index 5832217f3e85..1b0d5f9a9fdf 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java @@ -23,6 +23,7 @@ import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.os.Handler; import android.os.Looper; import android.provider.Settings; +import android.safetycenter.SafetyCenterManager; import android.service.quicksettings.Tile; import android.view.View; import android.widget.Switch; @@ -54,6 +55,8 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS private final KeyguardStateController mKeyguard; protected IndividualSensorPrivacyController mSensorPrivacyController; + private final SafetyCenterManager mSafetyCenterManager; + /** * @return Id of the sensor that will be toggled */ @@ -80,11 +83,13 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS ActivityStarter activityStarter, QSLogger qsLogger, IndividualSensorPrivacyController sensorPrivacyController, - KeyguardStateController keyguardStateController) { + KeyguardStateController keyguardStateController, + SafetyCenterManager safetyCenterManager) { super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mSensorPrivacyController = sensorPrivacyController; mKeyguard = keyguardStateController; + mSafetyCenterManager = safetyCenterManager; mSensorPrivacyController.observe(getLifecycle(), this); } @@ -133,7 +138,11 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS @Override public Intent getLongClickIntent() { - return new Intent(Settings.ACTION_PRIVACY_SETTINGS); + if (mSafetyCenterManager.isSafetyCenterEnabled()) { + return new Intent(Settings.ACTION_PRIVACY_CONTROLS); + } else { + return new Intent(Settings.ACTION_PRIVACY_SETTINGS); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt new file mode 100644 index 000000000000..efad9ec548f9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.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.qs.tiles.dialog.bluetooth + +import android.bluetooth.BluetoothAdapter.STATE_OFF +import android.bluetooth.BluetoothAdapter.STATE_ON +import com.android.settingslib.bluetooth.BluetoothCallback +import com.android.settingslib.bluetooth.LocalBluetoothManager +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.dagger.qualifiers.Application +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.stateIn + +/** Holds business logic for the Bluetooth Dialog's bluetooth and device connection state */ +@SysUISingleton +internal class BluetoothStateInteractor +@Inject +constructor( + private val localBluetoothManager: LocalBluetoothManager?, + @Application private val coroutineScope: CoroutineScope, +) { + + internal val updateBluetoothStateFlow: StateFlow<Boolean?> = + conflatedCallbackFlow { + val listener = + object : BluetoothCallback { + override fun onBluetoothStateChanged(bluetoothState: Int) { + if (bluetoothState == STATE_ON || bluetoothState == STATE_OFF) { + super.onBluetoothStateChanged(bluetoothState) + trySendWithFailureLogging( + bluetoothState == STATE_ON, + TAG, + "onBluetoothStateChanged" + ) + } + } + } + localBluetoothManager?.eventManager?.registerCallback(listener) + awaitClose { localBluetoothManager?.eventManager?.unregisterCallback(listener) } + } + .stateIn( + coroutineScope, + SharingStarted.WhileSubscribed(replayExpirationMillis = 0), + initialValue = null + ) + + internal var isBluetoothEnabled: Boolean + get() = localBluetoothManager?.bluetoothAdapter?.isEnabled == true + set(value) { + if (isBluetoothEnabled != value) { + localBluetoothManager?.bluetoothAdapter?.apply { + if (value) enable() else disable() + } + } + } + + companion object { + private const val TAG = "BtStateInteractor" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt new file mode 100644 index 000000000000..6815a7325081 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt @@ -0,0 +1,207 @@ +/* + * 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.qs.tiles.dialog.bluetooth + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.Switch +import android.widget.TextView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.android.internal.logging.UiEventLogger +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.res.R +import com.android.systemui.statusbar.phone.SystemUIDialog +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow + +/** Dialog for showing active, connected and saved bluetooth devices. */ +@SysUISingleton +internal class BluetoothTileDialog +constructor( + private val bluetoothToggleInitialValue: Boolean, + private val bluetoothTileDialogCallback: BluetoothTileDialogCallback, + private val uiEventLogger: UiEventLogger, + context: Context, +) : SystemUIDialog(context, DEFAULT_THEME, DEFAULT_DISMISS_ON_DEVICE_LOCK) { + + private val mutableBluetoothStateSwitchedFlow: MutableStateFlow<Boolean> = + MutableStateFlow(bluetoothToggleInitialValue) + internal val bluetoothStateSwitchedFlow + get() = mutableBluetoothStateSwitchedFlow.asStateFlow() + + private val mutableClickedFlow: MutableSharedFlow<Pair<DeviceItem, Int>> = + MutableSharedFlow(extraBufferCapacity = 1) + internal val deviceItemClickedFlow + get() = mutableClickedFlow.asSharedFlow() + + private val deviceItemAdapter: Adapter = Adapter(bluetoothTileDialogCallback) + + private lateinit var toggleView: Switch + private lateinit var doneButton: View + private lateinit var seeAllViewGroup: View + private lateinit var pairNewDeviceViewGroup: View + private lateinit var seeAllText: View + private lateinit var pairNewDeviceText: View + private lateinit var deviceListView: RecyclerView + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_TILE_DIALOG_SHOWN) + + setContentView(LayoutInflater.from(context).inflate(R.layout.bluetooth_tile_dialog, null)) + + toggleView = requireViewById(R.id.bluetooth_toggle) + doneButton = requireViewById(R.id.done_button) + seeAllViewGroup = requireViewById(R.id.see_all_layout_group) + pairNewDeviceViewGroup = requireViewById(R.id.pair_new_device_layout_group) + seeAllText = requireViewById(R.id.see_all_text) + pairNewDeviceText = requireViewById(R.id.pair_new_device_text) + deviceListView = requireViewById<RecyclerView>(R.id.device_list) + + setupToggle() + setupRecyclerView() + + doneButton.setOnClickListener { dismiss() } + seeAllText.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) } + pairNewDeviceText.setOnClickListener { + bluetoothTileDialogCallback.onPairNewDeviceClicked(it) + } + } + + // TODO(b/298124674): use DiffUtil or AsyncListDiffer to avoid updating the whole list + internal fun onDeviceItemUpdated( + deviceItem: List<DeviceItem>, + showSeeAll: Boolean, + showPairNewDevice: Boolean + ) { + seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE + pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE + deviceItemAdapter.refreshDeviceItemList(deviceItem) + } + + internal fun onDeviceItemUpdatedAtPosition(deviceItem: DeviceItem, position: Int) { + deviceItemAdapter.refreshDeviceItem(deviceItem, position) + } + + internal fun onBluetoothStateUpdated(isEnabled: Boolean) { + toggleView.isChecked = isEnabled + } + + private fun setupToggle() { + toggleView.isChecked = bluetoothToggleInitialValue + toggleView.setOnCheckedChangeListener { _, isChecked -> + mutableBluetoothStateSwitchedFlow.value = isChecked + uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_TOGGLE_CLICKED) + } + } + + private fun setupRecyclerView() { + deviceListView.apply { + layoutManager = LinearLayoutManager(context) + adapter = deviceItemAdapter + } + } + + internal inner class Adapter(private val onClickCallback: BluetoothTileDialogCallback) : + RecyclerView.Adapter<Adapter.DeviceItemViewHolder>() { + + private val deviceItem: MutableList<DeviceItem> = mutableListOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DeviceItemViewHolder { + val view = + LayoutInflater.from(parent.context) + .inflate(R.layout.bluetooth_device_item, parent, false) + return DeviceItemViewHolder(view) + } + + override fun getItemCount() = deviceItem.size + + override fun onBindViewHolder(holder: DeviceItemViewHolder, position: Int) { + val item = getItem(position) + holder.bind(item, position, onClickCallback) + } + + internal fun getItem(position: Int) = deviceItem[position] + + internal fun refreshDeviceItemList(updated: List<DeviceItem>) { + deviceItem.clear() + deviceItem.addAll(updated) + notifyDataSetChanged() + } + + internal fun refreshDeviceItem(updated: DeviceItem, position: Int) { + deviceItem[position] = updated + notifyItemChanged(position) + } + + internal inner class DeviceItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + private val container = view.requireViewById<View>(R.id.bluetooth_device_row) + private val deviceView = view.requireViewById<View>(R.id.bluetooth_device) + private val nameView = view.requireViewById<TextView>(R.id.bluetooth_device_name) + private val summaryView = view.requireViewById<TextView>(R.id.bluetooth_device_summary) + private val iconView = view.requireViewById<ImageView>(R.id.bluetooth_device_icon) + private val gearView = view.requireViewById<View>(R.id.gear_icon) + + internal fun bind( + item: DeviceItem, + position: Int, + deviceItemOnClickCallback: BluetoothTileDialogCallback + ) { + container.apply { + isEnabled = item.isEnabled + alpha = item.alpha + background = item.background + } + deviceView.setOnClickListener { + mutableClickedFlow.tryEmit(Pair(item, position)) + uiEventLogger.log(BluetoothTileDialogUiEvent.DEVICE_CLICKED) + } + nameView.text = item.deviceName + summaryView.text = item.connectionSummary + iconView.apply { + item.iconWithDescription?.let { + setImageDrawable(it.first) + contentDescription = it.second + } + } + gearView.setOnClickListener { + deviceItemOnClickCallback.onDeviceItemGearClicked(item, it) + } + } + } + } + + internal companion object { + const val ENABLED_ALPHA = 1.0f + const val DISABLED_ALPHA = 0.3f + const val MAX_DEVICE_ITEM_ENTRY = 3 + const val ACTION_BLUETOOTH_DEVICE_DETAILS = + "com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS" + const val ACTION_PREVIOUSLY_CONNECTED_DEVICE = + "com.android.settings.PREVIOUSLY_CONNECTED_DEVICE" + const val ACTION_PAIR_NEW_DEVICE = "android.settings.BLUETOOTH_PAIRING_SETTINGS" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepository.kt new file mode 100644 index 000000000000..ea51beecc2c1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepository.kt @@ -0,0 +1,45 @@ +/* + * 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.qs.tiles.dialog.bluetooth + +import android.bluetooth.BluetoothAdapter +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject + +/** Repository to get CachedBluetoothDevices for the Bluetooth Dialog. */ +@SysUISingleton +internal class BluetoothTileDialogRepository +@Inject +constructor( + private val localBluetoothManager: LocalBluetoothManager?, + private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter() +) { + internal val cachedDevices: Collection<CachedBluetoothDevice> + get() { + return if ( + localBluetoothManager == null || + bluetoothAdapter == null || + !bluetoothAdapter.isEnabled + ) { + emptyList() + } else { + localBluetoothManager.cachedDeviceManager.cachedDevicesCopy + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt new file mode 100644 index 000000000000..2865ad7a3e83 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt @@ -0,0 +1,34 @@ +/* + * 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.qs.tiles.dialog.bluetooth + +import com.android.internal.logging.UiEvent +import com.android.internal.logging.UiEventLogger + +/** UI Events for the bluetooth tile dialog. */ +enum class BluetoothTileDialogUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum { + @UiEvent(doc = "The bluetooth tile dialog is shown") BLUETOOTH_TILE_DIALOG_SHOWN(1493), + @UiEvent(doc = "The master toggle is clicked") BLUETOOTH_TOGGLE_CLICKED(1494), + @UiEvent(doc = "Pair new device is clicked") PAIR_NEW_DEVICE_CLICKED(1495), + @UiEvent(doc = "See all is clicked") SEE_ALL_CLICKED(1496), + @UiEvent(doc = "Gear icon clicked") DEVICE_GEAR_CLICKED(1497), + @UiEvent(doc = "Device clicked") DEVICE_CLICKED(1498), + @UiEvent(doc = "Connected device clicked to active") CONNECTED_DEVICE_SET_ACTIVE(1499), + @UiEvent(doc = "Saved clicked to connect") SAVED_DEVICE_CONNECT(1500); + + override fun getId() = metricId +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt new file mode 100644 index 000000000000..012484f2f0a6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt @@ -0,0 +1,184 @@ +/* + * 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.qs.tiles.dialog.bluetooth + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.annotation.VisibleForTesting +import com.android.internal.logging.UiEventLogger +import com.android.systemui.animation.DialogLaunchAnimator +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ACTION_BLUETOOTH_DEVICE_DETAILS +import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ACTION_PAIR_NEW_DEVICE +import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ACTION_PREVIOUSLY_CONNECTED_DEVICE +import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.MAX_DEVICE_ITEM_ENTRY +import com.android.systemui.statusbar.phone.SystemUIDialog +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch + +/** ViewModel for Bluetooth Dialog after clicking on the Bluetooth QS tile. */ +@SysUISingleton +internal class BluetoothTileDialogViewModel +@Inject +constructor( + private val deviceItemInteractor: DeviceItemInteractor, + private val bluetoothStateInteractor: BluetoothStateInteractor, + private val dialogLaunchAnimator: DialogLaunchAnimator, + private val activityStarter: ActivityStarter, + private val uiEventLogger: UiEventLogger, + @Application private val coroutineScope: CoroutineScope, + @Main private val mainDispatcher: CoroutineDispatcher, +) : BluetoothTileDialogCallback { + + private var job: Job? = null + + @VisibleForTesting internal var dialog: BluetoothTileDialog? = null + + /** + * Shows the dialog. + * + * @param context The context in which the dialog is displayed. + * @param view The view from which the dialog is shown. + */ + fun showDialog(context: Context, view: View?) { + dismissDialog() + + var updateDeviceItemJob: Job? = null + + job = + coroutineScope.launch(mainDispatcher) { + dialog = createBluetoothTileDialog(context) + view?.let { dialogLaunchAnimator.showFromView(dialog!!, it) } ?: dialog!!.show() + updateDeviceItemJob?.cancel() + updateDeviceItemJob = launch { deviceItemInteractor.updateDeviceItems(context) } + + bluetoothStateInteractor.updateBluetoothStateFlow + .filterNotNull() + .onEach { + dialog!!.onBluetoothStateUpdated(it) + updateDeviceItemJob?.cancel() + updateDeviceItemJob = launch { + deviceItemInteractor.updateDeviceItems(context) + } + } + .launchIn(this) + + deviceItemInteractor.updateDeviceItemsFlow + .onEach { + updateDeviceItemJob?.cancel() + updateDeviceItemJob = launch { + deviceItemInteractor.updateDeviceItems(context) + } + } + .launchIn(this) + + deviceItemInteractor.deviceItemFlow + .filterNotNull() + .onEach { + dialog!!.onDeviceItemUpdated( + it.take(MAX_DEVICE_ITEM_ENTRY), + showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY, + showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled + ) + } + .launchIn(this) + + dialog!! + .bluetoothStateSwitchedFlow + .onEach { bluetoothStateInteractor.isBluetoothEnabled = it } + .launchIn(this) + + dialog!! + .deviceItemClickedFlow + .onEach { + if (deviceItemInteractor.updateDeviceItemOnClick(it.first)) { + dialog!!.onDeviceItemUpdatedAtPosition(it.first, it.second) + } + } + .launchIn(this) + } + } + + private fun createBluetoothTileDialog(context: Context): BluetoothTileDialog { + return BluetoothTileDialog( + bluetoothStateInteractor.isBluetoothEnabled, + this@BluetoothTileDialogViewModel, + uiEventLogger, + context + ) + .apply { SystemUIDialog.registerDismissListener(this) { dismissDialog() } } + } + + override fun onDeviceItemGearClicked(deviceItem: DeviceItem, view: View) { + uiEventLogger.log(BluetoothTileDialogUiEvent.DEVICE_GEAR_CLICKED) + val intent = + Intent(ACTION_BLUETOOTH_DEVICE_DETAILS).apply { + putExtra( + ":settings:show_fragment_args", + Bundle().apply { + putString("device_address", deviceItem.cachedBluetoothDevice.address) + } + ) + } + startSettingsActivity(intent, view) + } + + override fun onSeeAllClicked(view: View) { + uiEventLogger.log(BluetoothTileDialogUiEvent.SEE_ALL_CLICKED) + startSettingsActivity(Intent(ACTION_PREVIOUSLY_CONNECTED_DEVICE), view) + } + + override fun onPairNewDeviceClicked(view: View) { + uiEventLogger.log(BluetoothTileDialogUiEvent.PAIR_NEW_DEVICE_CLICKED) + startSettingsActivity(Intent(ACTION_PAIR_NEW_DEVICE), view) + } + + private fun dismissDialog() { + job?.cancel() + job = null + dialog?.dismiss() + dialog = null + } + + private fun startSettingsActivity(intent: Intent, view: View) { + dialog?.run { + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP + activityStarter.postStartActivityDismissingKeyguard( + intent, + 0, + dialogLaunchAnimator.createActivityLaunchController(view) + ) + } + } +} + +internal interface BluetoothTileDialogCallback { + fun onDeviceItemGearClicked(deviceItem: DeviceItem, view: View) + fun onSeeAllClicked(view: View) + fun onPairNewDeviceClicked(view: View) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt new file mode 100644 index 000000000000..03ae5e88f1b0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt @@ -0,0 +1,54 @@ +/* + * 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. + */ + +/* + * 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.qs.tiles.dialog.bluetooth + +import android.graphics.drawable.Drawable +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ENABLED_ALPHA + +enum class DeviceItemType { + AVAILABLE_MEDIA_BLUETOOTH_DEVICE, + CONNECTED_BLUETOOTH_DEVICE, + SAVED_BLUETOOTH_DEVICE, +} + +data class DeviceItem( + val type: DeviceItemType, + val cachedBluetoothDevice: CachedBluetoothDevice, + val deviceName: String = "", + val connectionSummary: String = "", + val iconWithDescription: Pair<Drawable, String>? = null, + val background: Drawable? = null, + var isEnabled: Boolean = true, + var alpha: Float = ENABLED_ALPHA +) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt new file mode 100644 index 000000000000..a16a9f1f1017 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt @@ -0,0 +1,121 @@ +/* + * 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.qs.tiles.dialog.bluetooth + +import android.bluetooth.BluetoothDevice +import android.content.Context +import android.media.AudioManager +import com.android.settingslib.bluetooth.BluetoothUtils +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.systemui.res.R + +private val backgroundOn = R.drawable.settingslib_switch_bar_bg_on +private val connected = R.string.quick_settings_bluetooth_device_connected +private val saved = R.string.quick_settings_bluetooth_device_saved + +/** Factories to create different types of Bluetooth device items from CachedBluetoothDevice. */ +internal abstract class DeviceItemFactory { + abstract fun isFilterMatched( + cachedDevice: CachedBluetoothDevice, + audioManager: AudioManager? + ): Boolean + + abstract fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem +} + +internal class AvailableMediaDeviceItemFactory : DeviceItemFactory() { + override fun isFilterMatched( + cachedDevice: CachedBluetoothDevice, + audioManager: AudioManager? + ): Boolean { + return BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager) + } + + // TODO(b/298124674): move create() to the abstract class to reduce duplicate code + override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem { + return DeviceItem( + type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedDevice, + deviceName = cachedDevice.name, + connectionSummary = cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() } + ?: context.getString(connected), + iconWithDescription = + BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p -> + Pair(p.first, p.second) + }, + background = context.getDrawable(backgroundOn), + isEnabled = !cachedDevice.isBusy, + alpha = + if (cachedDevice.isBusy) BluetoothTileDialog.DISABLED_ALPHA + else BluetoothTileDialog.ENABLED_ALPHA, + ) + } +} + +internal class ConnectedDeviceItemFactory : DeviceItemFactory() { + override fun isFilterMatched( + cachedDevice: CachedBluetoothDevice, + audioManager: AudioManager? + ): Boolean { + return BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, audioManager) + } + + override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem { + return DeviceItem( + type = DeviceItemType.CONNECTED_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedDevice, + deviceName = cachedDevice.name, + connectionSummary = cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() } + ?: context.getString(connected), + iconWithDescription = + BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p -> + Pair(p.first, p.second) + }, + isEnabled = !cachedDevice.isBusy, + alpha = + if (cachedDevice.isBusy) BluetoothTileDialog.DISABLED_ALPHA + else BluetoothTileDialog.ENABLED_ALPHA, + ) + } +} + +internal class SavedDeviceItemFactory : DeviceItemFactory() { + override fun isFilterMatched( + cachedDevice: CachedBluetoothDevice, + audioManager: AudioManager? + ): Boolean { + return cachedDevice.bondState == BluetoothDevice.BOND_BONDED && !cachedDevice.isConnected + } + + override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem { + return DeviceItem( + type = DeviceItemType.SAVED_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedDevice, + deviceName = cachedDevice.name, + connectionSummary = cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() } + ?: context.getString(saved), + iconWithDescription = + BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p -> + Pair(p.first, p.second) + }, + isEnabled = !cachedDevice.isBusy, + alpha = + if (cachedDevice.isBusy) BluetoothTileDialog.DISABLED_ALPHA + else BluetoothTileDialog.ENABLED_ALPHA, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt new file mode 100644 index 000000000000..fcd0ce6807fd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt @@ -0,0 +1,181 @@ +/* + * 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.qs.tiles.dialog.bluetooth + +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.content.Context +import android.media.AudioManager +import com.android.internal.logging.UiEventLogger +import com.android.settingslib.bluetooth.BluetoothCallback +import com.android.settingslib.bluetooth.BluetoothUtils +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.settingslib.bluetooth.LocalBluetoothManager +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.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.withContext + +/** Holds business logic for the Bluetooth Dialog after clicking on the Bluetooth QS tile. */ +@SysUISingleton +internal class DeviceItemInteractor +@Inject +constructor( + private val bluetoothTileDialogRepository: BluetoothTileDialogRepository, + private val audioManager: AudioManager, + private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter(), + private val localBluetoothManager: LocalBluetoothManager?, + private val uiEventLogger: UiEventLogger, + @Application private val coroutineScope: CoroutineScope, + @Background private val backgroundDispatcher: CoroutineDispatcher, +) { + + private val mutableDeviceItemFlow: MutableStateFlow<List<DeviceItem>?> = MutableStateFlow(null) + internal val deviceItemFlow + get() = mutableDeviceItemFlow.asStateFlow() + + internal val updateDeviceItemsFlow: SharedFlow<Unit> = + conflatedCallbackFlow { + val listener = + object : BluetoothCallback { + override fun onActiveDeviceChanged( + activeDevice: CachedBluetoothDevice?, + bluetoothProfile: Int + ) { + super.onActiveDeviceChanged(activeDevice, bluetoothProfile) + trySendWithFailureLogging(Unit, TAG, "onActiveDeviceChanged") + } + + override fun onConnectionStateChanged( + cachedDevice: CachedBluetoothDevice?, + state: Int + ) { + super.onConnectionStateChanged(cachedDevice, state) + trySendWithFailureLogging(Unit, TAG, "onConnectionStateChanged") + } + + override fun onDeviceAdded(cachedDevice: CachedBluetoothDevice) { + super.onDeviceAdded(cachedDevice) + trySendWithFailureLogging(Unit, TAG, "onDeviceAdded") + } + + override fun onProfileConnectionStateChanged( + cachedDevice: CachedBluetoothDevice, + state: Int, + bluetoothProfile: Int + ) { + super.onProfileConnectionStateChanged( + cachedDevice, + state, + bluetoothProfile + ) + trySendWithFailureLogging(Unit, TAG, "onProfileConnectionStateChanged") + } + } + localBluetoothManager?.eventManager?.registerCallback(listener) + awaitClose { localBluetoothManager?.eventManager?.unregisterCallback(listener) } + } + .shareIn(coroutineScope, SharingStarted.WhileSubscribed(replayExpirationMillis = 0)) + + private var deviceItemFactoryList: List<DeviceItemFactory> = + listOf( + AvailableMediaDeviceItemFactory(), + ConnectedDeviceItemFactory(), + SavedDeviceItemFactory() + ) + + private var displayPriority: List<DeviceItemType> = + listOf( + DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE, + DeviceItemType.CONNECTED_BLUETOOTH_DEVICE, + DeviceItemType.SAVED_BLUETOOTH_DEVICE, + ) + + internal suspend fun updateDeviceItems(context: Context) { + withContext(backgroundDispatcher) { + val mostRecentlyConnectedDevices = bluetoothAdapter?.mostRecentlyConnectedDevices + + mutableDeviceItemFlow.value = + bluetoothTileDialogRepository.cachedDevices + .mapNotNull { cachedDevice -> + deviceItemFactoryList + .firstOrNull { it.isFilterMatched(cachedDevice, audioManager) } + ?.create(context, cachedDevice) + } + .sort(displayPriority, mostRecentlyConnectedDevices) + } + } + + private fun List<DeviceItem>.sort( + displayPriority: List<DeviceItemType>, + mostRecentlyConnectedDevices: List<BluetoothDevice>? + ): List<DeviceItem> { + return this.sortedWith( + compareBy<DeviceItem> { displayPriority.indexOf(it.type) } + .thenBy { + mostRecentlyConnectedDevices?.indexOf(it.cachedBluetoothDevice.device) ?: 0 + } + ) + } + + internal fun updateDeviceItemOnClick(deviceItem: DeviceItem): Boolean { + var isClicked = false + when (deviceItem.type) { + DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> { + if (!BluetoothUtils.isActiveMediaDevice(deviceItem.cachedBluetoothDevice)) { + deviceItem.cachedBluetoothDevice.setActive() + uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE) + isClicked = true + } + } + DeviceItemType.CONNECTED_BLUETOOTH_DEVICE -> {} + DeviceItemType.SAVED_BLUETOOTH_DEVICE -> { + deviceItem.cachedBluetoothDevice.connect() + uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT) + isClicked = true + } + } + if (isClicked) { + deviceItem.isEnabled = false + deviceItem.alpha = BluetoothTileDialog.DISABLED_ALPHA + } + return isClicked + } + + internal fun setDeviceItemFactoryListForTesting(list: List<DeviceItemFactory>) { + deviceItemFactoryList = list + } + + internal fun setDisplayPriorityForTesting(list: List<DeviceItemType>) { + displayPriority = list + } + + companion object { + private const val TAG = "DeviceItemInteractor" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 98e5124f7736..2f1b589899a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -84,7 +84,6 @@ import com.android.keyguard.TrustGrantFlags; import com.android.keyguard.logging.KeyguardLogger; import com.android.settingslib.Utils; import com.android.settingslib.fuelgauge.BatteryStatus; -import com.android.systemui.res.R; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.FaceHelpMessageDeferral; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -103,6 +102,7 @@ import com.android.systemui.keyguard.util.IndicationHelper; import com.android.systemui.log.core.LogLevel; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; @@ -1256,8 +1256,6 @@ public class KeyguardIndicationController { if (biometricSourceType == FACE) { mFaceAcquiredMessageDeferral.reset(); } - mBouncerMessageInteractor.setFaceAcquisitionMessage(null); - mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null); } @Override @@ -1278,8 +1276,6 @@ public class KeyguardIndicationController { } else if (biometricSourceType == FINGERPRINT) { onFingerprintAuthError(msgId, errString); } - mBouncerMessageInteractor.setFaceAcquisitionMessage(null); - mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null); } private void onFaceAuthError(int msgId, String errString) { @@ -1351,8 +1347,6 @@ public class KeyguardIndicationController { showActionToUnlock(); } } - mBouncerMessageInteractor.setFaceAcquisitionMessage(null); - mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index e632214bcb2b..37a4ef168423 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -104,8 +104,9 @@ public class StatusBarStateControllerImpl implements // Record the HISTORY_SIZE most recent states private int mHistoryIndex = 0; private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE]; - // This is used by InteractionJankMonitor to get callback from HWUI. + // These views are used by InteractionJankMonitor to get callback from HWUI. private View mView; + private KeyguardClockSwitch mClockSwitchView; /** * If any of the system bars is hidden. @@ -334,6 +335,7 @@ public class StatusBarStateControllerImpl implements if ((mView == null || !mView.isAttachedToWindow()) && (view != null && view.isAttachedToWindow())) { mView = view; + mClockSwitchView = view.findViewById(R.id.keyguard_clock_container); } mDozeAmountTarget = dozeAmount; if (animated) { @@ -416,21 +418,12 @@ public class StatusBarStateControllerImpl implements /** Returns the id of the currently rendering clock */ public String getClockId() { - if (mView == null) { - return KeyguardClockSwitch.MISSING_CLOCK_ID; - } - - View clockSwitch = mView.findViewById(R.id.keyguard_clock_container); - if (clockSwitch == null) { + if (mClockSwitchView == null) { Log.e(TAG, "Clock container was missing"); return KeyguardClockSwitch.MISSING_CLOCK_ID; } - if (!(clockSwitch instanceof KeyguardClockSwitch)) { - Log.e(TAG, "Clock container was incorrect type: " + clockSwitch); - return KeyguardClockSwitch.MISSING_CLOCK_ID; - } - return ((KeyguardClockSwitch) clockSwitch).getClockId(); + return mClockSwitchView.getClockId(); } private void beginInteractionJankMonitor() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING b/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING index 8849d6e11e26..10e7573a757e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING +++ b/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING @@ -13,7 +13,7 @@ "exclude-annotation": "org.junit.Ignore" }, { - "exclude-annotation": "android.platform.test.annotations.LargeTest" + "exclude-annotation": "androidx.test.filters.LargeTest" }, { "exclude-annotation": "androidx.test.filters.LargeTest" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java index eb31bd3a95fe..2d5afd56da72 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java @@ -50,7 +50,7 @@ public class GroupExpansionManagerImpl implements GroupExpansionManager, Dumpabl * Set of summary keys whose groups are expanded. * NOTE: This should not be modified without notifying listeners, so prefer using * {@code setGroupExpanded} when making changes. - */ + */ private final Set<NotificationEntry> mExpandedGroups = new HashSet<>(); private final FeatureFlags mFeatureFlags; @@ -104,7 +104,18 @@ public class GroupExpansionManagerImpl implements GroupExpansionManager, Dumpabl @Override public void setGroupExpanded(NotificationEntry entry, boolean expanded) { - final NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry); + NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry); + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE) + && entry.getParent() == null) { + if (expanded) { + throw new IllegalArgumentException("Cannot expand group that is not attached"); + } else { + // The entry is no longer attached, but we still want to make sure we don't have + // a stale expansion state. + groupSummary = entry; + } + } + boolean changed; if (expanded) { changed = mExpandedGroups.add(groupSummary); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java index c33e8ab8cdd4..3158782e6fea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java @@ -25,18 +25,18 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.util.List; /** - * Helper that determines the group states (parent, summary, children) of a notification. + * Helper that determines the group states (parent, summary, children) of a notification. This + * generally assumes that the notification is attached (aka its parent is not null). */ public interface GroupMembershipManager { /** - * @return whether a given notification is a top level entry or is the summary in a group which - * has children + * @return whether a given notification is the summary in a group which has children */ boolean isGroupSummary(@NonNull NotificationEntry entry); /** * Get the summary of a specified status bar notification. For an isolated notification this - * returns itself. + * returns null, but if called directly on a summary it returns itself. */ @Nullable NotificationEntry getGroupSummary(@NonNull NotificationEntry entry); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java index a6b855f9b838..cb7935369564 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java @@ -22,7 +22,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.flags.Flags; import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.ListEntry; @@ -38,47 +38,50 @@ import javax.inject.Inject; */ @SysUISingleton public class GroupMembershipManagerImpl implements GroupMembershipManager { - FeatureFlags mFeatureFlags; + FeatureFlagsClassic mFeatureFlags; @Inject - public GroupMembershipManagerImpl(FeatureFlags featureFlags) { + public GroupMembershipManagerImpl(FeatureFlagsClassic featureFlags) { mFeatureFlags = featureFlags; } @Override public boolean isGroupSummary(@NonNull NotificationEntry entry) { - return getGroupSummary(entry) == entry; + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) { + if (entry.getParent() == null) { + // The entry is not attached, so it doesn't count. + return false; + } + // If entry is a summary, its parent is a GroupEntry with summary = entry. + return entry.getParent().getSummary() == entry; + } else { + return getGroupSummary(entry) == entry; + } } @Nullable @Override public NotificationEntry getGroupSummary(@NonNull NotificationEntry entry) { - if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) { - if (!isChildInGroup(entry)) { - return entry.getRepresentativeEntry(); - } - } else { - if (isEntryTopLevel(entry) || entry.getParent() == null) { - return null; - } + if (isTopLevelEntry(entry) || entry.getParent() == null) { + return null; } - - return entry.getParent().getRepresentativeEntry(); + return entry.getParent().getSummary(); } @Override public boolean isChildInGroup(@NonNull NotificationEntry entry) { if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) { - return !isEntryTopLevel(entry) && entry.getParent() != null; + // An entry is a child if it's not a summary or top level entry, but it is attached. + return !isGroupSummary(entry) && !isTopLevelEntry(entry) && entry.getParent() != null; } else { - return !isEntryTopLevel(entry); + return !isTopLevelEntry(entry); } } @Override public boolean isOnlyChildInGroup(@NonNull NotificationEntry entry) { if (entry.getParent() == null) { - return false; + return false; // The entry is not attached. } return !isGroupSummary(entry) && entry.getParent().getChildren().size() == 1; @@ -103,7 +106,7 @@ public class GroupMembershipManagerImpl implements GroupMembershipManager { return null; } - private boolean isEntryTopLevel(@NonNull NotificationEntry entry) { + private boolean isTopLevelEntry(@NonNull NotificationEntry entry) { return entry.getParent() == ROOT_ENTRY; } } 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 88994b9eec04..6ec9dbe003a2 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 @@ -100,7 +100,11 @@ public interface NotificationInterruptStateProvider { /** * The notification is coming from a suspended packages, so FSI is suppressed. */ - NO_FSI_SUSPENDED(false); + NO_FSI_SUSPENDED(false), + /** + * The device is not provisioned, launch FSI. + */ + FSI_NOT_PROVISIONED(true); public final boolean shouldLaunch; 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 0c43da066fdb..3819843aa7b2 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 @@ -45,6 +45,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -75,6 +76,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter private final KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider; private final UiEventLogger mUiEventLogger; private final UserTracker mUserTracker; + private final DeviceProvisionedController mDeviceProvisionedController; @VisibleForTesting protected boolean mUseHeadsUp = false; @@ -121,7 +123,8 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter NotifPipelineFlags flags, KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider, UiEventLogger uiEventLogger, - UserTracker userTracker) { + UserTracker userTracker, + DeviceProvisionedController deviceProvisionedController) { mContentResolver = contentResolver; mPowerManager = powerManager; mBatteryController = batteryController; @@ -163,6 +166,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter headsUpObserver); } headsUpObserver.onChange(true); // set up + mDeviceProvisionedController = deviceProvisionedController; } @Override @@ -334,6 +338,12 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter } } + // The device is not provisioned, launch FSI. + if (!mDeviceProvisionedController.isDeviceProvisioned()) { + return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_NOT_PROVISIONED, + suppressedByDND); + } + // Detect the case determined by b/231322873 to launch FSI while device is in use, // as blocked by the correct implementation, and report the event. return getDecisionGivenSuppression(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt index 4114eb2a7896..a8d59d83d1e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt @@ -71,15 +71,15 @@ class ChannelEditorDialogController @Inject constructor( private var appUid: Int? = null private var packageName: String? = null private var appName: String? = null + private var channel: NotificationChannel? = null private var onSettingsClickListener: NotificationInfo.OnSettingsClickListener? = null // Caller should set this if they care about when we dismiss var onFinishListener: OnChannelEditorDialogFinishedListener? = null - @VisibleForTesting - internal val paddedChannels = mutableListOf<NotificationChannel>() // Channels handed to us from NotificationInfo - private val providedChannels = mutableListOf<NotificationChannel>() + @VisibleForTesting + internal val channelList = mutableListOf<NotificationChannel>() // Map from NotificationChannel to importance private val edits = mutableMapOf<NotificationChannel, Int>() @@ -93,14 +93,14 @@ class ChannelEditorDialogController @Inject constructor( private val channelGroupList = mutableListOf<NotificationChannelGroup>() /** - * Give the controller all of the information it needs to present the dialog + * Give the controller all the information it needs to present the dialog * for a given app. Does a bunch of querying of NoMan, but won't present anything yet */ fun prepareDialogForApp( appName: String, packageName: String, uid: Int, - channels: Set<NotificationChannel>, + channel: NotificationChannel, appIcon: Drawable, onSettingsClickListener: NotificationInfo.OnSettingsClickListener? ) { @@ -110,6 +110,7 @@ class ChannelEditorDialogController @Inject constructor( this.appIcon = appIcon this.appNotificationsEnabled = checkAreAppNotificationsOn() this.onSettingsClickListener = onSettingsClickListener + this.channel = channel // These will always start out the same appNotificationsCurrentlyEnabled = appNotificationsEnabled @@ -117,9 +118,7 @@ class ChannelEditorDialogController @Inject constructor( channelGroupList.clear() channelGroupList.addAll(fetchNotificationChannelGroups()) buildGroupNameLookup() - providedChannels.clear() - providedChannels.addAll(channels) - padToFourChannels(channels) + populateChannelList() initDialog() prepared = true @@ -133,36 +132,26 @@ class ChannelEditorDialogController @Inject constructor( } } - private fun padToFourChannels(channels: Set<NotificationChannel>) { - paddedChannels.clear() - // First, add all of the given channels - paddedChannels.addAll(channels.asSequence().take(4)) - - // Then pad to 4 if we haven't been given that many - paddedChannels.addAll(getDisplayableChannels(channelGroupList.asSequence()) - .filterNot { paddedChannels.contains(it) } - .distinct() - .take(4 - paddedChannels.size)) - - // If we only got one channel and it has the default miscellaneous tag, then we actually - // are looking at an app with a targetSdk <= O, and it doesn't make much sense to show the - // channel - if (paddedChannels.size == 1 && DEFAULT_CHANNEL_ID == paddedChannels[0].id) { - paddedChannels.clear() + private fun populateChannelList() { + channelList.clear() + if (DEFAULT_CHANNEL_ID != channel!!.id) { + channelList.add(0, channel!!) + channelList.addAll(getDisplayableChannels(channelGroupList.asSequence()) + .filterNot { it.id == channel!!.id } + .distinct()) } } private fun getDisplayableChannels( groupList: Sequence<NotificationChannelGroup> ): Sequence<NotificationChannel> { - - // TODO (b/194833441): remove channel level settings when we move to a permission val channels = groupList .flatMap { group -> - group.channels.asSequence().filterNot { channel -> - channel.importance == IMPORTANCE_NONE || + group.channels.asSequence() + .sortedWith(compareBy {group.name?.toString() ?: group.id}) + .filterNot { channel -> channel.isImportanceLockedByCriticalDeviceFunction - } + } } // TODO: sort these by avgSentWeekly, but for now let's just do alphabetical (why not) @@ -196,8 +185,7 @@ class ChannelEditorDialogController @Inject constructor( appNotificationsCurrentlyEnabled = null edits.clear() - paddedChannels.clear() - providedChannels.clear() + channelList.clear() groupNameLookup.clear() } @@ -231,7 +219,7 @@ class ChannelEditorDialogController @Inject constructor( @Suppress("unchecked_cast") private fun fetchNotificationChannelGroups(): List<NotificationChannelGroup> { return try { - noMan.getNotificationChannelGroupsForPackage(packageName!!, appUid!!, false) + noMan.getRecentBlockedNotificationChannelGroupsForPackage(packageName!!, appUid!!) .list as? List<NotificationChannelGroup> ?: listOf() } catch (e: Exception) { Log.e(TAG, "Error fetching channel groups", e) @@ -280,7 +268,6 @@ class ChannelEditorDialogController @Inject constructor( @VisibleForTesting fun launchSettings(sender: View) { - val channel = if (providedChannels.size == 1) providedChannels[0] else null onSettingsClickListener?.onClick(sender, channel, appUid!!) } @@ -301,14 +288,12 @@ class ChannelEditorDialogController @Inject constructor( controller = this@ChannelEditorDialogController appIcon = this@ChannelEditorDialogController.appIcon appName = this@ChannelEditorDialogController.appName - channels = paddedChannels + channels = channelList } setOnShowListener { - // play a highlight animation for the given channels - for (channel in providedChannels) { - listView?.highlightChannel(channel) - } + // play a highlight animation for the given channel + listView?.highlightChannel(channel!!) } findViewById<TextView>(R.id.done_button)?.setOnClickListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt index 2cfd075a17a9..10e67a40ebc9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt @@ -20,6 +20,7 @@ import android.animation.ArgbEvaluator import android.animation.ValueAnimator import android.app.NotificationChannel import android.app.NotificationManager.IMPORTANCE_DEFAULT +import android.app.NotificationManager.IMPORTANCE_LOW import android.app.NotificationManager.IMPORTANCE_NONE import android.app.NotificationManager.IMPORTANCE_UNSPECIFIED import android.content.Context @@ -55,12 +56,14 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a // The first row is for the entire app private lateinit var appControlRow: AppControlView + private lateinit var channelListView: LinearLayout private val channelRows = mutableListOf<ChannelRow>() override fun onFinishInflate() { super.onFinishInflate() appControlRow = requireViewById(R.id.app_control) + channelListView = requireViewById(R.id.scrollView) } /** @@ -102,7 +105,7 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a // Remove any rows for (row in channelRows) { - removeView(row) + channelListView.removeView(row) } channelRows.clear() @@ -122,7 +125,7 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a row.channel = channel channelRows.add(row) - addView(row) + channelListView.addView(row) } private fun updateAppControlRow(enabled: Boolean) { @@ -179,7 +182,9 @@ class ChannelRow(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) { switch = requireViewById(R.id.toggle) switch.setOnCheckedChangeListener { _, b -> channel?.let { - controller.proposeEditForChannel(it, if (b) it.importance else IMPORTANCE_NONE) + controller.proposeEditForChannel(it, + if (b) it.originalImportance.coerceAtLeast(IMPORTANCE_LOW) + else IMPORTANCE_NONE) } } setOnClickListener { switch.toggle() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index fb8024c88191..d18f9919604d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -2658,42 +2658,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } /** - * Returns the number of channels covered by the notification row (including its children if - * it's a summary notification). - */ - public int getNumUniqueChannels() { - return getUniqueChannels().size(); - } - - /** - * Returns the channels covered by the notification row (including its children if - * it's a summary notification). - */ - public ArraySet<NotificationChannel> getUniqueChannels() { - ArraySet<NotificationChannel> channels = new ArraySet<>(); - - channels.add(mEntry.getChannel()); - - // If this is a summary, then add in the children notification channels for the - // same user and pkg. - if (mIsSummaryWithChildren) { - final List<ExpandableNotificationRow> childrenRows = getAttachedChildren(); - final int numChildren = childrenRows.size(); - for (int i = 0; i < numChildren; i++) { - final ExpandableNotificationRow childRow = childrenRows.get(i); - final NotificationChannel childChannel = childRow.getEntry().getChannel(); - final StatusBarNotification childSbn = childRow.getEntry().getSbn(); - if (childSbn.getUser().equals(mEntry.getSbn().getUser()) - && childSbn.getPackageName().equals(mEntry.getSbn().getPackageName())) { - channels.add(childChannel); - } - } - } - - return channels; - } - - /** * If this is a group, update the appearance of the children. */ public void updateChildrenAppearance() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 1dd3739d0e84..6d6566058aed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -403,7 +403,6 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta mChannelEditorDialogController, packageName, row.getEntry().getChannel(), - row.getUniqueChannels(), row.getEntry(), onSettingsClick, onAppSettingsClick, @@ -449,7 +448,6 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta mChannelEditorDialogController, packageName, row.getEntry().getChannel(), - row.getUniqueChannels(), row.getEntry(), onSettingsClick, mDeviceProvisionedController.isDeviceProvisioned(), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index d8f31d4fb8a2..d8ebd4209c7d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -104,8 +104,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private String mAppName; private int mAppUid; private String mDelegatePkg; - private int mNumUniqueChannelsInRow; - private Set<NotificationChannel> mUniqueChannelsInRow; private NotificationChannel mSingleNotificationChannel; private int mStartingChannelImportance; private boolean mWasShownHighPriority; @@ -196,7 +194,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G ChannelEditorDialogController channelEditorDialogController, String pkg, NotificationChannel notificationChannel, - Set<NotificationChannel> uniqueChannelsInRow, NotificationEntry entry, OnSettingsClickListener onSettingsClick, OnAppSettingsClickListener onAppSettingsClick, @@ -213,8 +210,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mChannelEditorDialogController = channelEditorDialogController; mAssistantFeedbackController = assistantFeedbackController; mPackageName = pkg; - mUniqueChannelsInRow = uniqueChannelsInRow; - mNumUniqueChannelsInRow = uniqueChannelsInRow.size(); mEntry = entry; mSbn = entry.getSbn(); mPm = pm; @@ -236,15 +231,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); - if (mNumUniqueChannelsInRow == 0) { - throw new IllegalArgumentException("bindNotification requires at least one channel"); - } else { - // Special behavior for the Default channel if no other channels have been defined. - mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1 - && mSingleNotificationChannel.getId().equals( - NotificationChannel.DEFAULT_CHANNEL_ID) - && numTotalChannels == 1; - } + mIsSingleDefaultChannel = mSingleNotificationChannel.getId().equals( + NotificationChannel.DEFAULT_CHANNEL_ID) && numTotalChannels == 1; mIsAutomaticChosen = getAlertingBehavior() == BEHAVIOR_AUTOMATIC; bindHeader(); @@ -271,11 +259,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G findViewById(R.id.interruptiveness_settings).setVisibility(GONE); ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button); findViewById(R.id.turn_off_notifications).setVisibility(GONE); - } else if (mNumUniqueChannelsInRow > 1) { - findViewById(R.id.non_configurable_call_text).setVisibility(GONE); - findViewById(R.id.non_configurable_text).setVisibility(GONE); - findViewById(R.id.interruptiveness_settings).setVisibility(GONE); - findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE); } else { findViewById(R.id.non_configurable_call_text).setVisibility(GONE); findViewById(R.id.non_configurable_text).setVisibility(GONE); @@ -361,9 +344,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) { final int appUidF = mAppUid; return ((View view) -> { - mOnSettingsClickListener.onClick(view, - mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel, - appUidF); + mOnSettingsClickListener.onClick(view, mSingleNotificationChannel, appUidF); }); } return null; @@ -375,7 +356,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mPresentingChannelEditorDialog = true; mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid, - mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener); + mSingleNotificationChannel, mPkgIcon, mOnSettingsClickListener); mChannelEditorDialogController.setOnFinishListener(() -> { mPresentingChannelEditorDialog = false; mGutsContainer.closeControls(this, false); @@ -392,7 +373,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private void bindName() { final TextView channelName = findViewById(R.id.channel_name); - if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) { + if (mIsSingleDefaultChannel) { channelName.setVisibility(View.GONE); } else { channelName.setText(mSingleNotificationChannel.getName()); @@ -459,7 +440,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER)); bgHandler.post( new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid, - mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null, + mSingleNotificationChannel, mStartingChannelImportance, newImportance, mIsAutomaticChosen)); mOnUserInteractionCallback.onImportanceChanged(mEntry); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java index 06c3b7951db3..53f7d4bde70f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java @@ -57,7 +57,6 @@ public class PartialConversationInfo extends LinearLayout implements private StatusBarNotification mSbn; private boolean mIsDeviceProvisioned; private boolean mIsNonBlockable; - private Set<NotificationChannel> mUniqueChannelsInRow; private Drawable mPkgIcon; private boolean mPresentingChannelEditorDialog = false; @@ -83,7 +82,6 @@ public class PartialConversationInfo extends LinearLayout implements ChannelEditorDialogController channelEditorDialogController, String pkg, NotificationChannel notificationChannel, - Set<NotificationChannel> uniqueChannelsInRow, NotificationEntry entry, NotificationInfo.OnSettingsClickListener onSettingsClick, boolean isDeviceProvisioned, @@ -100,7 +98,6 @@ public class PartialConversationInfo extends LinearLayout implements mIsDeviceProvisioned = isDeviceProvisioned; mIsNonBlockable = isNonBlockable; mChannelEditorDialogController = channelEditorDialogController; - mUniqueChannelsInRow = uniqueChannelsInRow; bindHeader(); bindActions(); @@ -149,7 +146,7 @@ public class PartialConversationInfo extends LinearLayout implements mPresentingChannelEditorDialog = true; mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid, - mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener); + mNotificationChannel, mPkgIcon, mOnSettingsClickListener); mChannelEditorDialogController.setOnFinishListener(() -> { mPresentingChannelEditorDialog = false; mGutsContainer.closeControls(this, false); 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 3a8850408f18..6f69ea8170c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -3429,6 +3429,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { @Override public void setIsLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) { mIsLaunchingActivityOverLockscreen = isLaunchingActivityOverLockscreen; + mKeyguardViewMediator.launchingActivityOverLockscreen(mIsLaunchingActivityOverLockscreen); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt index cd6862113ee9..dc50990d002a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt @@ -71,6 +71,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapNotNull @@ -181,6 +182,7 @@ class MobileConnectionRepositoryImpl( telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback) awaitClose { telephonyManager.unregisterTelephonyCallback(callback) } } + .flowOn(bgDispatcher) .scan(initial = initial) { state, event -> state.applyEvent(event) } .stateIn(scope = scope, started = SharingStarted.WhileSubscribed(), initial) } @@ -358,6 +360,7 @@ class MobileConnectionRepositoryImpl( awaitClose { context.unregisterReceiver(receiver) } } + .flowOn(bgDispatcher) .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName) override val dataEnabled = run { 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 0592afc0fef6..ecb80f28de3e 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 @@ -137,22 +137,24 @@ constructor( ) .stateIn(scope, started = SharingStarted.WhileSubscribed(), null) - private val mobileSubscriptionsChangeEvent: Flow<Unit> = conflatedCallbackFlow { - val callback = - object : SubscriptionManager.OnSubscriptionsChangedListener() { - override fun onSubscriptionsChanged() { - logger.logOnSubscriptionsChanged() - trySend(Unit) - } - } + private val mobileSubscriptionsChangeEvent: Flow<Unit> = + conflatedCallbackFlow { + val callback = + object : SubscriptionManager.OnSubscriptionsChangedListener() { + override fun onSubscriptionsChanged() { + logger.logOnSubscriptionsChanged() + trySend(Unit) + } + } - subscriptionManager.addOnSubscriptionsChangedListener( - bgDispatcher.asExecutor(), - callback, - ) + subscriptionManager.addOnSubscriptionsChangedListener( + bgDispatcher.asExecutor(), + callback, + ) - awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) } - } + awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) } + } + .flowOn(bgDispatcher) /** * State flow that emits the set of mobile data subscriptions, each represented by its own @@ -187,6 +189,7 @@ constructor( telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback) awaitClose { telephonyManager.unregisterTelephonyCallback(callback) } } + .flowOn(bgDispatcher) .distinctUntilChanged() .logDiffsForTable( tableLogger, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java index 01fabcc8bc1e..3008c866d207 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java @@ -159,7 +159,8 @@ public final class DeviceStateRotationLockSettingController // Update the rotation policy, if needed, for this new device state if (shouldBeLocked != isLocked) { - mRotationPolicyWrapper.setRotationLock(shouldBeLocked); + mRotationPolicyWrapper.setRotationLock(shouldBeLocked, + /* caller= */"DeviceStateRotationLockSettingController#readPersistedSetting"); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java index 518a9b3f61ec..e5f72ebdaab1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -40,6 +40,7 @@ import android.os.UserManager; import android.provider.DeviceConfig; import android.provider.Settings; +import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -361,6 +362,7 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio private static final int MSG_ADD_CALLBACK = 3; private static final int MSG_REMOVE_CALLBACK = 4; + @GuardedBy("mSettingsChangeCallbacks") private final ArrayList<LocationChangeCallback> mSettingsChangeCallbacks = new ArrayList<>(); @@ -378,10 +380,14 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio locationActiveChanged(); break; case MSG_ADD_CALLBACK: - mSettingsChangeCallbacks.add((LocationChangeCallback) msg.obj); + synchronized (mSettingsChangeCallbacks) { + mSettingsChangeCallbacks.add((LocationChangeCallback) msg.obj); + } break; case MSG_REMOVE_CALLBACK: - mSettingsChangeCallbacks.remove((LocationChangeCallback) msg.obj); + synchronized (mSettingsChangeCallbacks) { + mSettingsChangeCallbacks.remove((LocationChangeCallback) msg.obj); + } break; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java index 1158324567ff..607f1e562468 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java @@ -24,8 +24,8 @@ public interface RotationLockController extends Listenable, boolean isRotationLockAffordanceVisible(); boolean isRotationLocked(); boolean isCameraRotationEnabled(); - void setRotationLocked(boolean locked); - void setRotationLockedAtAngle(boolean locked, int rotation); + void setRotationLocked(boolean locked, String caller); + void setRotationLockedAtAngle(boolean locked, int rotation, String caller); public interface RotationLockControllerCallback { void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java index 1eeb0ac8b3bb..797aa1f3a3dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java @@ -93,12 +93,12 @@ public final class RotationLockControllerImpl implements RotationLockController return mRotationPolicy.isCameraRotationEnabled(); } - public void setRotationLocked(boolean locked) { - mRotationPolicy.setRotationLock(locked); + public void setRotationLocked(boolean locked, String caller) { + mRotationPolicy.setRotationLock(locked, caller); } - public void setRotationLockedAtAngle(boolean locked, int rotation) { - mRotationPolicy.setRotationLockAtAngle(locked, rotation); + public void setRotationLockedAtAngle(boolean locked, int rotation, String caller) { + mRotationPolicy.setRotationLockAtAngle(locked, rotation, caller); } public boolean isRotationLockAffordanceVisible() { diff --git a/packages/SystemUI/src/com/android/systemui/user/UserModule.java b/packages/SystemUI/src/com/android/systemui/user/UserModule.java index d8ee686ea60f..348670f09836 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserModule.java +++ b/packages/SystemUI/src/com/android/systemui/user/UserModule.java @@ -21,7 +21,6 @@ import android.os.UserHandle; import com.android.settingslib.users.CreateUserDialogController; import com.android.settingslib.users.EditUserInfoController; import com.android.systemui.user.data.repository.UserRepositoryModule; -import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule; import com.android.systemui.user.ui.dialog.UserDialogModule; import dagger.Module; @@ -34,7 +33,6 @@ import dagger.Provides; includes = { UserDialogModule.class, UserRepositoryModule.class, - HeadlessSystemUserModeModule.class, } ) public abstract class UserModule { diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/UserDomainLayerModule.kt b/packages/SystemUI/src/com/android/systemui/user/domain/UserDomainLayerModule.kt new file mode 100644 index 000000000000..4122404c92f7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/user/domain/UserDomainLayerModule.kt @@ -0,0 +1,21 @@ +/* + * 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.user.domain + +import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule +import dagger.Module + +@Module(includes = [HeadlessSystemUserModeModule::class]) object UserDomainLayerModule diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt index e4894992b49f..ce9d6fd752c5 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt @@ -37,7 +37,6 @@ import com.android.internal.logging.UiEventLogger import com.android.internal.util.UserIcons import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.systemui.res.R import com.android.systemui.SystemUISecondaryUserService import com.android.systemui.animation.Expandable import com.android.systemui.broadcast.BroadcastDispatcher @@ -50,6 +49,7 @@ import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.plugins.ActivityStarter import com.android.systemui.qs.user.UserSwitchDialogController +import com.android.systemui.res.R import com.android.systemui.telephony.domain.interactor.TelephonyInteractor import com.android.systemui.user.CreateUserActivity import com.android.systemui.user.data.model.UserSwitcherSettingsModel @@ -62,6 +62,7 @@ import com.android.systemui.user.shared.model.UserModel import com.android.systemui.user.utils.MultiUserActionsEvent import com.android.systemui.user.utils.MultiUserActionsEventHelper import com.android.systemui.util.kotlin.pairwise +import com.android.systemui.utils.UserRestrictionChecker import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -103,6 +104,7 @@ constructor( private val refreshUsersScheduler: RefreshUsersScheduler, private val guestUserInteractor: GuestUserInteractor, private val uiEventLogger: UiEventLogger, + private val userRestrictionChecker: UserRestrictionChecker, ) { /** * Defines interface for classes that can be notified when the state of users on the device is @@ -593,6 +595,7 @@ constructor( ) && // If the user is auto-created is must not be currently resetting. !(isGuestUserAutoCreated && isGuestUserResetting), + userRestrictionChecker = userRestrictionChecker, ) } diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt index 93573fa42c96..80139bd6ac0c 100644 --- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt @@ -22,10 +22,10 @@ import android.content.pm.UserInfo import android.graphics.Bitmap import android.os.UserManager import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin -import com.android.settingslib.RestrictedLockUtilsInternal import com.android.systemui.res.R import com.android.systemui.user.data.source.UserRecord import com.android.systemui.user.shared.model.UserActionModel +import com.android.systemui.utils.UserRestrictionChecker /** * Defines utility functions for helping with legacy data code for users. @@ -68,6 +68,7 @@ object LegacyUserDataHelper { actionType: UserActionModel, isRestricted: Boolean, isSwitchToEnabled: Boolean, + userRestrictionChecker: UserRestrictionChecker, ): UserRecord { return UserRecord( isGuest = actionType == UserActionModel.ENTER_GUEST_MODE, @@ -79,6 +80,7 @@ object LegacyUserDataHelper { getEnforcedAdmin( context = context, selectedUserId = selectedUserId, + userRestrictionChecker = userRestrictionChecker, ), isManageUsers = actionType == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT, ) @@ -103,9 +105,10 @@ object LegacyUserDataHelper { private fun getEnforcedAdmin( context: Context, selectedUserId: Int, + userRestrictionChecker: UserRestrictionChecker ): EnforcedAdmin? { val admin = - RestrictedLockUtilsInternal.checkIfRestrictionEnforced( + userRestrictionChecker.checkIfRestrictionEnforced( context, UserManager.DISALLOW_ADD_USER, selectedUserId, @@ -113,7 +116,7 @@ object LegacyUserDataHelper { ?: return null return if ( - !RestrictedLockUtilsInternal.hasBaseUserRestriction( + !userRestrictionChecker.hasBaseUserRestriction( context, UserManager.DISALLOW_ADD_USER, selectedUserId, diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt index d8de07d185c6..374ebe0f28fb 100644 --- a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt +++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt @@ -28,8 +28,8 @@ import javax.inject.Inject * Testable wrapper interface around RotationPolicy {link com.android.internal.view.RotationPolicy} */ interface RotationPolicyWrapper { - fun setRotationLock(enabled: Boolean) - fun setRotationLockAtAngle(enabled: Boolean, rotation: Int) + fun setRotationLock(enabled: Boolean, caller: String) + fun setRotationLockAtAngle(enabled: Boolean, rotation: Int, caller: String) fun getRotationLockOrientation(): Int fun isRotationLockToggleVisible(): Boolean fun isRotationLocked(): Boolean @@ -44,14 +44,14 @@ class RotationPolicyWrapperImpl @Inject constructor( ) : RotationPolicyWrapper { - override fun setRotationLock(enabled: Boolean) { + override fun setRotationLock(enabled: Boolean, caller: String) { traceSection("RotationPolicyWrapperImpl#setRotationLock") { - RotationPolicy.setRotationLock(context, enabled) + RotationPolicy.setRotationLock(context, enabled, caller) } } - override fun setRotationLockAtAngle(enabled: Boolean, rotation: Int) { - RotationPolicy.setRotationLockAtAngle(context, enabled, rotation) + override fun setRotationLockAtAngle(enabled: Boolean, rotation: Int, caller: String) { + RotationPolicy.setRotationLockAtAngle(context, enabled, rotation, caller) } override fun getRotationLockOrientation(): Int = diff --git a/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt b/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt new file mode 100644 index 000000000000..3f8346b98d39 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt @@ -0,0 +1,25 @@ +package com.android.systemui.utils + +import android.content.Context +import com.android.settingslib.RestrictedLockUtils +import com.android.settingslib.RestrictedLockUtilsInternal +import javax.inject.Inject + +/** Proxy to call [RestrictedLockUtilsInternal] */ +class UserRestrictionChecker @Inject constructor() { + fun checkIfRestrictionEnforced( + context: Context, + userRestriction: String, + userId: Int + ): RestrictedLockUtils.EnforcedAdmin? { + return RestrictedLockUtilsInternal.checkIfRestrictionEnforced( + context, + userRestriction, + userId + ) + } + + fun hasBaseUserRestriction(context: Context, userRestriction: String, userId: Int): Boolean { + return RestrictedLockUtilsInternal.hasBaseUserRestriction(context, userRestriction, userId) + } +} diff --git a/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt b/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt new file mode 100644 index 000000000000..360aa0f89a46 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt @@ -0,0 +1,56 @@ +/* + * 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 + +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main +import dagger.Binds +import dagger.Module +import dagger.Provides +import kotlin.coroutines.ContinuationInterceptor +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.test.TestCoroutineScheduler +import kotlinx.coroutines.test.TestDispatcher +import kotlinx.coroutines.test.TestScope + +@Module(includes = [CoroutineTestScopeModule.Bindings::class]) +class CoroutineTestScopeModule +private constructor( + @get:Provides val scope: TestScope, + @get:Provides val dispatcher: TestDispatcher, + @get:Provides val scheduler: TestCoroutineScheduler = dispatcher.scheduler, +) { + + constructor() : this(TestScope()) + + constructor( + scope: TestScope + ) : this(scope, scope.coroutineContext[ContinuationInterceptor] as TestDispatcher) + + constructor(context: CoroutineContext) : this(TestScope(context)) + + @get:[Provides Application] + val appScope: CoroutineScope = scope.backgroundScope + + @Module + interface Bindings { + @Binds @Main fun bindMainDispatcher(dispatcher: TestDispatcher): CoroutineDispatcher + @Binds @Background fun bindBgDispatcher(dispatcher: TestDispatcher): CoroutineDispatcher + } +} diff --git a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt b/packages/SystemUI/tests/src/com/android/SysUITestModule.kt new file mode 100644 index 000000000000..ea74510a9070 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/SysUITestModule.kt @@ -0,0 +1,42 @@ +/* + * 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 + +import android.content.Context +import com.android.systemui.FakeSystemUiModule +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.qualifiers.Application +import dagger.Module +import dagger.Provides + +@Module( + includes = + [ + TestMocksModule::class, + CoroutineTestScopeModule::class, + FakeSystemUiModule::class, + ] +) +class SysUITestModule { + @Provides fun provideContext(test: SysuiTestCase): Context = test.context + + @Provides @Application fun provideAppContext(test: SysuiTestCase): Context = test.context + + @Provides + fun provideBroadcastDispatcher(test: SysuiTestCase): BroadcastDispatcher = + test.fakeBroadcastDispatcher +} diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt new file mode 100644 index 000000000000..8990583cf9de --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt @@ -0,0 +1,78 @@ +/* + * 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 + +import android.app.ActivityManager +import android.app.admin.DevicePolicyManager +import android.os.UserManager +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.GuestResumeSessionReceiver +import com.android.systemui.demomode.DemoModeController +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.dagger.BroadcastDispatcherLog +import com.android.systemui.log.dagger.SceneFrameworkLog +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.plugins.DarkIconDispatcher +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.NotificationListener +import com.android.systemui.statusbar.NotificationMediaManager +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +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.ConfigurationController +import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.statusbar.policy.SplitShadeStateController +import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.util.mockito.mock +import com.android.wm.shell.bubbles.Bubbles +import dagger.Module +import dagger.Provides +import java.util.Optional + +@Module +data class TestMocksModule( + @get:Provides val activityStarter: ActivityStarter = mock(), + @get:Provides val bubbles: Optional<Bubbles> = Optional.of(mock()), + @get:Provides val configurationController: ConfigurationController = mock(), + @get:Provides val darkIconDispatcher: DarkIconDispatcher = mock(), + @get:Provides val demoModeController: DemoModeController = mock(), + @get:Provides val deviceProvisionedController: DeviceProvisionedController = mock(), + @get:Provides val dozeParameters: DozeParameters = mock(), + @get:Provides val guestResumeSessionReceiver: GuestResumeSessionReceiver = mock(), + @get:Provides val keyguardBypassController: KeyguardBypassController = mock(), + @get:Provides val keyguardSecurityModel: KeyguardSecurityModel = mock(), + @get:Provides val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(), + @get:Provides val notifListener: NotificationListener = mock(), + @get:Provides val notifMediaManager: NotificationMediaManager = mock(), + @get:Provides val screenOffAnimController: ScreenOffAnimationController = mock(), + @get:Provides val splitShadeStateController: SplitShadeStateController = mock(), + @get:Provides val statusBarStateController: StatusBarStateController = mock(), + @get:Provides val statusBarWindowController: StatusBarWindowController = mock(), + @get:Provides val wakeUpCoordinator: NotificationWakeUpCoordinator = mock(), + + // log buffers + @get:[Provides BroadcastDispatcherLog] + val broadcastDispatcherLogger: LogBuffer = mock(), + @get:[Provides SceneFrameworkLog] + val sceneLogger: LogBuffer = mock(), + + // framework mocks + @get:Provides val activityManager: ActivityManager = mock(), + @get:Provides val devicePolicyManager: DevicePolicyManager = mock(), + @get:Provides val userManager: UserManager = mock(), +) diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt index fc7d20aeb356..874053a83654 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt @@ -236,7 +236,8 @@ class AuthenticationInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod( DataLayerAuthenticationMethodModel.Pin ) - assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) + .isEqualTo(AuthenticationResult.SUCCEEDED) assertThat(isThrottled).isFalse() } @@ -246,7 +247,8 @@ class AuthenticationInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod( DataLayerAuthenticationMethodModel.Pin ) - assertThat(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4))).isFalse() + assertThat(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4))) + .isEqualTo(AuthenticationResult.FAILED) } @Test(expected = IllegalArgumentException::class) @@ -267,7 +269,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { overrideCredential(pin) } - assertThat(underTest.authenticate(pin)).isTrue() + assertThat(underTest.authenticate(pin)).isEqualTo(AuthenticationResult.SUCCEEDED) } @Test @@ -282,7 +284,8 @@ class AuthenticationInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod( DataLayerAuthenticationMethodModel.Pin ) - assertThat(underTest.authenticate(List(17) { 9 })).isFalse() + assertThat(underTest.authenticate(List(17) { 9 })) + .isEqualTo(AuthenticationResult.FAILED) } @Test @@ -293,7 +296,8 @@ class AuthenticationInteractorTest : SysuiTestCase() { DataLayerAuthenticationMethodModel.Password ) - assertThat(underTest.authenticate("password".toList())).isTrue() + assertThat(underTest.authenticate("password".toList())) + .isEqualTo(AuthenticationResult.SUCCEEDED) assertThat(isThrottled).isFalse() } @@ -304,7 +308,8 @@ class AuthenticationInteractorTest : SysuiTestCase() { DataLayerAuthenticationMethodModel.Password ) - assertThat(underTest.authenticate("alohomora".toList())).isFalse() + assertThat(underTest.authenticate("alohomora".toList())) + .isEqualTo(AuthenticationResult.FAILED) } @Test @@ -314,7 +319,8 @@ class AuthenticationInteractorTest : SysuiTestCase() { DataLayerAuthenticationMethodModel.Pattern ) - assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN)).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN)) + .isEqualTo(AuthenticationResult.SUCCEEDED) } @Test @@ -327,22 +333,14 @@ class AuthenticationInteractorTest : SysuiTestCase() { assertThat( underTest.authenticate( listOf( - AuthenticationPatternCoordinate( - x = 2, - y = 0, - ), - AuthenticationPatternCoordinate( - x = 2, - y = 1, - ), - AuthenticationPatternCoordinate( - x = 2, - y = 2, - ), + AuthenticationPatternCoordinate(x = 2, y = 0), + AuthenticationPatternCoordinate(x = 2, y = 1), + AuthenticationPatternCoordinate(x = 2, y = 2), + AuthenticationPatternCoordinate(x = 1, y = 2), ) ) ) - .isFalse() + .isEqualTo(AuthenticationResult.FAILED) } @Test @@ -361,7 +359,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { tryAutoConfirm = true ) ) - .isNull() + .isEqualTo(AuthenticationResult.SKIPPED) assertThat(isThrottled).isFalse() } @@ -379,7 +377,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { tryAutoConfirm = true ) ) - .isFalse() + .isEqualTo(AuthenticationResult.FAILED) assertThat(isUnlocked).isFalse() } @@ -397,7 +395,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { tryAutoConfirm = true ) ) - .isFalse() + .isEqualTo(AuthenticationResult.FAILED) assertThat(isUnlocked).isFalse() } @@ -415,7 +413,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { tryAutoConfirm = true ) ) - .isTrue() + .isEqualTo(AuthenticationResult.SUCCEEDED) assertThat(isUnlocked).isTrue() } @@ -433,7 +431,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { tryAutoConfirm = true ) ) - .isNull() + .isEqualTo(AuthenticationResult.SKIPPED) assertThat(isUnlocked).isFalse() } @@ -445,7 +443,8 @@ class AuthenticationInteractorTest : SysuiTestCase() { DataLayerAuthenticationMethodModel.Password ) - assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true)).isNull() + assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true)) + .isEqualTo(AuthenticationResult.SKIPPED) assertThat(isUnlocked).isFalse() } @@ -490,7 +489,8 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) // Correct PIN, but throttled, so doesn't attempt it: - assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isNull() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) + .isEqualTo(AuthenticationResult.SKIPPED) assertThat(isUnlocked).isFalse() assertThat(isThrottled).isTrue() assertThat(throttling) @@ -536,7 +536,8 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) // Correct PIN and no longer throttled so unlocks successfully: - assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) + .isEqualTo(AuthenticationResult.SUCCEEDED) assertThat(isUnlocked).isTrue() assertThat(isThrottled).isFalse() assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt index 8fc63b246c9d..73654609ad7f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt @@ -44,6 +44,7 @@ import com.android.systemui.user.domain.interactor.GuestUserInteractor import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode import com.android.systemui.user.domain.interactor.RefreshUsersScheduler import com.android.systemui.user.domain.interactor.UserInteractor +import com.android.systemui.util.mockito.mock import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -126,6 +127,7 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() { refreshUsersScheduler = refreshUsersScheduler, guestUserInteractor = guestInteractor, uiEventLogger = uiEventLogger, + userRestrictionChecker = mock(), ) shadeInteractor = ShadeInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt index c9c46cbe8420..c825d2ea65ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt @@ -82,6 +82,11 @@ class DisplayStateRepositoryTest : SysuiTestCase() { rearDisplayDeviceStates ) + mContext.orCreateTestableResources.addOverride( + com.android.internal.R.bool.config_reverseDefaultRotation, + false + ) + mContext = spy(mContext) whenever(mContext.display).thenReturn(display) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt deleted file mode 100644 index 8eb274a65bc4..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt +++ /dev/null @@ -1,177 +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.systemui.bouncer.data.factory - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.keyguard.KeyguardSecurityModel -import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN -import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password -import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Pattern -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT -import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT -import com.android.systemui.SysuiTestCase -import com.android.systemui.bouncer.shared.model.BouncerMessageModel -import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository -import com.android.systemui.util.mockito.whenever -import com.google.common.truth.StringSubject -import com.google.common.truth.Truth.assertThat -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.mockito.Mock -import org.mockito.MockitoAnnotations - -@SmallTest -@RunWith(AndroidJUnit4::class) -class BouncerMessageFactoryTest : SysuiTestCase() { - private lateinit var underTest: BouncerMessageFactory - - @Mock private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository - - @Mock private lateinit var securityModel: KeyguardSecurityModel - - private lateinit var testScope: TestScope - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - testScope = TestScope() - biometricSettingsRepository = FakeBiometricSettingsRepository() - underTest = BouncerMessageFactory(biometricSettingsRepository, securityModel) - } - - @Test - fun bouncerMessages_choosesTheRightMessage_basedOnSecurityModeAndFpAuthIsAllowed() = - testScope.runTest { - primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAuthAllowed = false) - .isEqualTo("Enter PIN") - primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAuthAllowed = true) - .isEqualTo("Unlock with PIN or fingerprint") - - primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAuthAllowed = false) - .isEqualTo("Enter password") - primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAuthAllowed = true) - .isEqualTo("Unlock with password or fingerprint") - - primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAuthAllowed = false) - .isEqualTo("Draw pattern") - primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAuthAllowed = true) - .isEqualTo("Unlock with pattern or fingerprint") - } - - @Test - fun bouncerMessages_overridesSecondaryMessageValue() = - testScope.runTest { - val bouncerMessageModel = - bouncerMessageModel( - PIN, - true, - PROMPT_REASON_DEFAULT, - secondaryMessageOverride = "face acquisition message" - )!! - assertThat(context.resources.getString(bouncerMessageModel.message!!.messageResId!!)) - .isEqualTo("Unlock with PIN or fingerprint") - assertThat(bouncerMessageModel.secondaryMessage!!.message!!) - .isEqualTo("face acquisition message") - } - - @Test - fun bouncerMessages_setsPrimaryAndSecondaryMessage_basedOnSecurityModeAndFpAuthIsAllowed() = - testScope.runTest { - primaryMessage( - PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, - mode = PIN, - fpAuthAllowed = true - ) - .isEqualTo("Wrong PIN. Try again.") - secondaryMessage( - PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, - mode = PIN, - fpAuthAllowed = true - ) - .isEqualTo("Or unlock with fingerprint") - - primaryMessage( - PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, - mode = Password, - fpAuthAllowed = true - ) - .isEqualTo("Wrong password. Try again.") - secondaryMessage( - PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, - mode = Password, - fpAuthAllowed = true - ) - .isEqualTo("Or unlock with fingerprint") - - primaryMessage( - PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, - mode = Pattern, - fpAuthAllowed = true - ) - .isEqualTo("Wrong pattern. Try again.") - secondaryMessage( - PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, - mode = Pattern, - fpAuthAllowed = true - ) - .isEqualTo("Or unlock with fingerprint") - } - - private fun primaryMessage( - reason: Int, - mode: KeyguardSecurityModel.SecurityMode, - fpAuthAllowed: Boolean - ): StringSubject { - return assertThat( - context.resources.getString( - bouncerMessageModel(mode, fpAuthAllowed, reason)!!.message!!.messageResId!! - ) - )!! - } - - private fun secondaryMessage( - reason: Int, - mode: KeyguardSecurityModel.SecurityMode, - fpAuthAllowed: Boolean - ): StringSubject { - return assertThat( - context.resources.getString( - bouncerMessageModel(mode, fpAuthAllowed, reason)!!.secondaryMessage!!.messageResId!! - ) - )!! - } - - private fun bouncerMessageModel( - mode: KeyguardSecurityModel.SecurityMode, - fpAuthAllowed: Boolean, - reason: Int, - secondaryMessageOverride: String? = null, - ): BouncerMessageModel? { - whenever(securityModel.getSecurityMode(0)).thenReturn(mode) - biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(fpAuthAllowed) - - return underTest.createFromPromptReason( - reason, - 0, - secondaryMsgOverride = secondaryMessageOverride - ) - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt deleted file mode 100644 index f158b433d5dc..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt +++ /dev/null @@ -1,380 +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.systemui.bouncer.data.repo - -import android.content.pm.UserInfo -import android.hardware.biometrics.BiometricSourceType -import android.testing.TestableLooper -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED -import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST -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.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW -import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT -import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT -import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT -import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN -import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE -import com.android.keyguard.KeyguardSecurityModel -import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN -import com.android.keyguard.KeyguardUpdateMonitor -import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.systemui.res.R -import com.android.systemui.res.R.string.keyguard_enter_pin -import com.android.systemui.res.R.string.kg_prompt_after_dpm_lock -import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_pin -import com.android.systemui.res.R.string.kg_prompt_auth_timeout -import com.android.systemui.res.R.string.kg_prompt_pin_auth_timeout -import com.android.systemui.res.R.string.kg_prompt_reason_restart_pin -import com.android.systemui.res.R.string.kg_prompt_unattended_update -import com.android.systemui.res.R.string.kg_trust_agent_disabled -import com.android.systemui.SysuiTestCase -import com.android.systemui.bouncer.data.factory.BouncerMessageFactory -import com.android.systemui.bouncer.data.repository.BouncerMessageRepository -import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl -import com.android.systemui.bouncer.shared.model.BouncerMessageModel -import com.android.systemui.bouncer.shared.model.Message -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.SystemPropertiesHelper -import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository -import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository -import com.android.systemui.keyguard.data.repository.FakeTrustRepository -import com.android.systemui.keyguard.shared.model.AuthenticationFlags -import com.android.systemui.user.data.repository.FakeUserRepository -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.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -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 -@TestableLooper.RunWithLooper(setAsMainLooper = true) -@RunWith(AndroidJUnit4::class) -class BouncerMessageRepositoryTest : SysuiTestCase() { - - @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor - @Mock private lateinit var securityModel: KeyguardSecurityModel - @Mock private lateinit var systemPropertiesHelper: SystemPropertiesHelper - @Captor - private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback> - - private lateinit var underTest: BouncerMessageRepository - private lateinit var trustRepository: FakeTrustRepository - private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository - private lateinit var userRepository: FakeUserRepository - private lateinit var fingerprintRepository: FakeDeviceEntryFingerprintAuthRepository - private lateinit var testScope: TestScope - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - trustRepository = FakeTrustRepository() - biometricSettingsRepository = FakeBiometricSettingsRepository() - userRepository = FakeUserRepository() - userRepository.setUserInfos(listOf(PRIMARY_USER)) - fingerprintRepository = FakeDeviceEntryFingerprintAuthRepository() - testScope = TestScope() - - biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false) - whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN) - underTest = - BouncerMessageRepositoryImpl( - trustRepository = trustRepository, - biometricSettingsRepository = biometricSettingsRepository, - updateMonitor = updateMonitor, - bouncerMessageFactory = - BouncerMessageFactory(biometricSettingsRepository, securityModel), - userRepository = userRepository, - fingerprintAuthRepository = fingerprintRepository, - systemPropertiesHelper = systemPropertiesHelper - ) - } - - @Test - fun setCustomMessage_propagatesState() = - testScope.runTest { - underTest.setCustomMessage(message("not empty")) - - val customMessage = collectLastValue(underTest.customMessage) - - assertThat(customMessage()).isEqualTo(message("not empty")) - } - - @Test - fun setFaceMessage_propagatesState() = - testScope.runTest { - underTest.setFaceAcquisitionMessage(message("not empty")) - - val faceAcquisitionMessage = collectLastValue(underTest.faceAcquisitionMessage) - - assertThat(faceAcquisitionMessage()).isEqualTo(message("not empty")) - } - - @Test - fun setFpMessage_propagatesState() = - testScope.runTest { - underTest.setFingerprintAcquisitionMessage(message("not empty")) - - val fpAcquisitionMsg = collectLastValue(underTest.fingerprintAcquisitionMessage) - - assertThat(fpAcquisitionMsg()).isEqualTo(message("not empty")) - } - - @Test - fun setPrimaryAuthMessage_propagatesState() = - testScope.runTest { - underTest.setPrimaryAuthMessage(message("not empty")) - - val primaryAuthMessage = collectLastValue(underTest.primaryAuthMessage) - - assertThat(primaryAuthMessage()).isEqualTo(message("not empty")) - } - - @Test - fun biometricAuthMessage_propagatesBiometricAuthMessages() = - testScope.runTest { - userRepository.setSelectedUserInfo(PRIMARY_USER) - val biometricAuthMessage = collectLastValue(underTest.biometricAuthMessage) - runCurrent() - - verify(updateMonitor).registerCallback(updateMonitorCallback.capture()) - - updateMonitorCallback.value.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT) - - assertThat(biometricAuthMessage()) - .isEqualTo(message(R.string.kg_fp_not_recognized, R.string.kg_bio_try_again_or_pin)) - - updateMonitorCallback.value.onBiometricAuthFailed(BiometricSourceType.FACE) - - assertThat(biometricAuthMessage()) - .isEqualTo( - message(R.string.bouncer_face_not_recognized, R.string.kg_bio_try_again_or_pin) - ) - - updateMonitorCallback.value.onBiometricAcquired(BiometricSourceType.FACE, 0) - - assertThat(biometricAuthMessage()).isNull() - } - - @Test - fun onFaceLockout_propagatesState() = - testScope.runTest { - userRepository.setSelectedUserInfo(PRIMARY_USER) - val lockoutMessage = collectLastValue(underTest.biometricLockedOutMessage) - runCurrent() - verify(updateMonitor).registerCallback(updateMonitorCallback.capture()) - - whenever(updateMonitor.isFaceLockedOut).thenReturn(true) - updateMonitorCallback.value.onLockedOutStateChanged(BiometricSourceType.FACE) - - assertThat(lockoutMessage()) - .isEqualTo(message(keyguard_enter_pin, R.string.kg_face_locked_out)) - - whenever(updateMonitor.isFaceLockedOut).thenReturn(false) - updateMonitorCallback.value.onLockedOutStateChanged(BiometricSourceType.FACE) - assertThat(lockoutMessage()).isNull() - } - - @Test - fun onFingerprintLockout_propagatesState() = - testScope.runTest { - userRepository.setSelectedUserInfo(PRIMARY_USER) - val lockedOutMessage = collectLastValue(underTest.biometricLockedOutMessage) - runCurrent() - - fingerprintRepository.setLockedOut(true) - - assertThat(lockedOutMessage()) - .isEqualTo(message(keyguard_enter_pin, R.string.kg_fp_locked_out)) - - fingerprintRepository.setLockedOut(false) - assertThat(lockedOutMessage()).isNull() - } - - @Test - fun onRestartForMainlineUpdate_shouldProvideRelevantMessage() = - testScope.runTest { - whenever(systemPropertiesHelper.get("sys.boot.reason.last")) - .thenReturn("reboot,mainline_update") - userRepository.setSelectedUserInfo(PRIMARY_USER) - biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) - - verifyMessagesForAuthFlag( - STRONG_AUTH_REQUIRED_AFTER_BOOT to - Pair(keyguard_enter_pin, R.string.kg_prompt_after_update_pin), - ) - } - - @Test - fun onAuthFlagsChanged_withTrustNotManagedAndNoBiometrics_isANoop() = - testScope.runTest { - userRepository.setSelectedUserInfo(PRIMARY_USER) - trustRepository.setCurrentUserTrustManaged(false) - biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) - biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) - - verifyMessagesForAuthFlag( - STRONG_AUTH_NOT_REQUIRED to null, - STRONG_AUTH_REQUIRED_AFTER_BOOT to null, - SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null, - STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null, - STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to null, - STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to null, - STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to null, - SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null, - STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to null, - STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to - Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock), - ) - } - - @Test - fun authFlagsChanges_withTrustManaged_providesDifferentMessages() = - testScope.runTest { - userRepository.setSelectedUserInfo(PRIMARY_USER) - biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) - biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) - - trustRepository.setCurrentUserTrustManaged(true) - - verifyMessagesForAuthFlag( - STRONG_AUTH_NOT_REQUIRED to null, - STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null, - STRONG_AUTH_REQUIRED_AFTER_BOOT to - Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin), - STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to - Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout), - STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to - Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock), - SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to - Pair(keyguard_enter_pin, kg_trust_agent_disabled), - SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to - Pair(keyguard_enter_pin, kg_trust_agent_disabled), - STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to - Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin), - STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to - Pair(keyguard_enter_pin, kg_prompt_unattended_update), - STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to - Pair(keyguard_enter_pin, kg_prompt_auth_timeout), - ) - } - - @Test - fun authFlagsChanges_withFaceEnrolled_providesDifferentMessages() = - testScope.runTest { - userRepository.setSelectedUserInfo(PRIMARY_USER) - trustRepository.setCurrentUserTrustManaged(false) - biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) - - biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) - - verifyMessagesForAuthFlag( - STRONG_AUTH_NOT_REQUIRED to null, - STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null, - SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null, - SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null, - STRONG_AUTH_REQUIRED_AFTER_BOOT to - Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin), - STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to - Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout), - STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to - Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock), - STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to - Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin), - STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to - Pair(keyguard_enter_pin, kg_prompt_unattended_update), - STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to - Pair(keyguard_enter_pin, kg_prompt_auth_timeout), - ) - } - - @Test - fun authFlagsChanges_withFingerprintEnrolled_providesDifferentMessages() = - testScope.runTest { - userRepository.setSelectedUserInfo(PRIMARY_USER) - trustRepository.setCurrentUserTrustManaged(false) - biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) - - biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) - - verifyMessagesForAuthFlag( - STRONG_AUTH_NOT_REQUIRED to null, - STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null, - SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null, - SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null, - STRONG_AUTH_REQUIRED_AFTER_BOOT to - Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin), - STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to - Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout), - STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to - Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock), - STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to - Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin), - STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to - Pair(keyguard_enter_pin, kg_prompt_unattended_update), - STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to - Pair(keyguard_enter_pin, kg_prompt_auth_timeout), - ) - } - - private fun TestScope.verifyMessagesForAuthFlag( - vararg authFlagToExpectedMessages: Pair<Int, Pair<Int, Int>?> - ) { - val authFlagsMessage = collectLastValue(underTest.authFlagsMessage) - - authFlagToExpectedMessages.forEach { (flag, messagePair) -> - biometricSettingsRepository.setAuthenticationFlags( - AuthenticationFlags(PRIMARY_USER_ID, flag) - ) - - assertThat(authFlagsMessage()) - .isEqualTo(messagePair?.let { message(it.first, it.second) }) - } - } - - private fun message(primaryResId: Int, secondaryResId: Int): BouncerMessageModel { - return BouncerMessageModel( - message = Message(messageResId = primaryResId, animate = false), - secondaryMessage = Message(messageResId = secondaryResId, animate = false) - ) - } - private fun message(value: String): BouncerMessageModel { - return BouncerMessageModel(message = Message(message = value)) - } - - companion object { - private const val PRIMARY_USER_ID = 0 - private val PRIMARY_USER = - UserInfo( - /* id= */ PRIMARY_USER_ID, - /* name= */ "primary user", - /* flags= */ UserInfo.FLAG_PRIMARY - ) - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index 77d8102fff2e..92c8a395a696 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -17,13 +17,14 @@ package com.android.systemui.bouncer.domain.interactor import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository +import com.android.systemui.authentication.domain.interactor.AuthenticationResult import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.res.R import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel @@ -87,7 +88,8 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) // Wrong input. - assertThat(underTest.authenticate(listOf(9, 8, 7))).isFalse() + assertThat(underTest.authenticate(listOf(9, 8, 7))) + .isEqualTo(AuthenticationResult.FAILED) assertThat(message).isEqualTo(MESSAGE_WRONG_PIN) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -95,7 +97,8 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) // Correct input. - assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) + .isEqualTo(AuthenticationResult.SUCCEEDED) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @@ -115,13 +118,14 @@ class BouncerInteractorTest : SysuiTestCase() { underTest.clearMessage() // Incomplete input. - assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)).isNull() + assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)) + .isEqualTo(AuthenticationResult.SKIPPED) assertThat(message).isEmpty() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) // Wrong 6-digit pin assertThat(underTest.authenticate(listOf(1, 2, 3, 5, 5, 6), tryAutoConfirm = true)) - .isFalse() + .isEqualTo(AuthenticationResult.FAILED) assertThat(message).isEqualTo(MESSAGE_WRONG_PIN) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -132,7 +136,7 @@ class BouncerInteractorTest : SysuiTestCase() { tryAutoConfirm = true ) ) - .isTrue() + .isEqualTo(AuthenticationResult.SUCCEEDED) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @@ -150,7 +154,8 @@ class BouncerInteractorTest : SysuiTestCase() { underTest.clearMessage() // Incomplete input. - assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)).isNull() + assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)) + .isEqualTo(AuthenticationResult.SKIPPED) assertThat(message).isEmpty() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -161,7 +166,7 @@ class BouncerInteractorTest : SysuiTestCase() { tryAutoConfirm = true ) ) - .isNull() + .isEqualTo(AuthenticationResult.SKIPPED) assertThat(message).isEmpty() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } @@ -187,7 +192,8 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD) // Wrong input. - assertThat(underTest.authenticate("alohamora".toList())).isFalse() + assertThat(underTest.authenticate("alohamora".toList())) + .isEqualTo(AuthenticationResult.FAILED) assertThat(message).isEqualTo(MESSAGE_WRONG_PASSWORD) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -195,7 +201,8 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD) // Correct input. - assertThat(underTest.authenticate("password".toList())).isTrue() + assertThat(underTest.authenticate("password".toList())) + .isEqualTo(AuthenticationResult.SUCCEEDED) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @@ -220,8 +227,30 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN) // Wrong input. - assertThat(underTest.authenticate(listOf(AuthenticationPatternCoordinate(1, 2)))) - .isFalse() + val wrongPattern = + listOf( + AuthenticationPatternCoordinate(1, 2), + AuthenticationPatternCoordinate(1, 1), + AuthenticationPatternCoordinate(0, 0), + AuthenticationPatternCoordinate(0, 1), + ) + assertThat(wrongPattern).isNotEqualTo(FakeAuthenticationRepository.PATTERN) + assertThat(wrongPattern.size).isAtLeast(utils.authenticationRepository.minPatternLength) + assertThat(underTest.authenticate(wrongPattern)).isEqualTo(AuthenticationResult.FAILED) + assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN) + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + + underTest.resetMessage() + assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN) + + // Too short input. + val tooShortPattern = + FakeAuthenticationRepository.PATTERN.subList( + 0, + utils.authenticationRepository.minPatternLength - 1 + ) + assertThat(underTest.authenticate(tooShortPattern)) + .isEqualTo(AuthenticationResult.SKIPPED) assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -229,7 +258,8 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN) // Correct input. - assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN)).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN)) + .isEqualTo(AuthenticationResult.SUCCEEDED) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @@ -294,7 +324,8 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { times -> // Wrong PIN. - assertThat(underTest.authenticate(listOf(6, 7, 8, 9))).isFalse() + assertThat(underTest.authenticate(listOf(6, 7, 8, 9))) + .isEqualTo(AuthenticationResult.FAILED) if ( times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1 ) { @@ -317,7 +348,8 @@ class BouncerInteractorTest : SysuiTestCase() { ) // Correct PIN, but throttled, so doesn't change away from the bouncer scene: - assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isNull() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) + .isEqualTo(AuthenticationResult.SKIPPED) assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer) assertTryAgainMessage( message, @@ -347,7 +379,8 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer) // Correct PIN and no longer throttled so changes to the Gone scene: - assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) + .isEqualTo(AuthenticationResult.SUCCEEDED) assertThat(currentScene?.key).isEqualTo(SceneKey.Gone) assertThat(isThrottled).isFalse() assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) @@ -362,7 +395,7 @@ class BouncerInteractorTest : SysuiTestCase() { val bouncerSceneKey = currentScene?.key assertThat(bouncerSceneKey).isEqualTo(SceneKey.Bouncer) - underTest.hide("") + underTest.onImeHidden() assertThat(currentScene?.key).isEqualTo(SceneKey.Lockscreen) } @@ -376,7 +409,7 @@ class BouncerInteractorTest : SysuiTestCase() { val notBouncerSceneKey = currentScene?.key assertThat(notBouncerSceneKey).isNotEqualTo(SceneKey.Bouncer) - underTest.hide("") + underTest.onImeHidden() assertThat(currentScene?.key).isEqualTo(notBouncerSceneKey) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt index c1286a166421..cc4eca546e17 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt @@ -17,187 +17,216 @@ package com.android.systemui.bouncer.domain.interactor import android.content.pm.UserInfo +import android.os.Handler import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.internal.widget.LockPatternUtils import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN -import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown -import com.android.systemui.res.R.string.kg_unlock_with_pin_or_fp +import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase -import com.android.systemui.bouncer.data.factory.BouncerMessageFactory -import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository +import com.android.systemui.biometrics.data.repository.FaceSensorInfo +import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository +import com.android.systemui.biometrics.shared.model.SensorStrength +import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.shared.model.BouncerMessageModel -import com.android.systemui.bouncer.shared.model.Message -import com.android.systemui.coroutines.FlowValue +import com.android.systemui.bouncer.ui.BouncerView +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.flags.SystemPropertiesHelper +import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.data.repository.FakeTrustRepository +import com.android.systemui.keyguard.shared.model.AuthenticationFlags +import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown +import com.android.systemui.res.R.string.kg_trust_agent_disabled +import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.KotlinArgumentCaptor 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.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.eq import org.mockito.Mock +import org.mockito.Mockito import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidJUnit4::class) class BouncerMessageInteractorTest : SysuiTestCase() { + private val countDownTimerCallback = KotlinArgumentCaptor(CountDownTimerCallback::class.java) + private val repository = BouncerMessageRepositoryImpl() + private val userRepository = FakeUserRepository() + private val fakeTrustRepository = FakeTrustRepository() + private val fakeFacePropertyRepository = FakeFacePropertyRepository() + private val bouncerRepository = FakeKeyguardBouncerRepository() + private val fakeDeviceEntryFingerprintAuthRepository = + FakeDeviceEntryFingerprintAuthRepository() + private val fakeDeviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository() + private val biometricSettingsRepository: FakeBiometricSettingsRepository = + FakeBiometricSettingsRepository() + @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor @Mock private lateinit var securityModel: KeyguardSecurityModel - @Mock private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository @Mock private lateinit var countDownTimerUtil: CountDownTimerUtil - private lateinit var countDownTimerCallback: KotlinArgumentCaptor<CountDownTimerCallback> - private lateinit var underTest: BouncerMessageInteractor - private lateinit var repository: FakeBouncerMessageRepository - private lateinit var userRepository: FakeUserRepository + @Mock private lateinit var systemPropertiesHelper: SystemPropertiesHelper + @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + + private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor private lateinit var testScope: TestScope - private lateinit var bouncerMessage: FlowValue<BouncerMessageModel?> + private lateinit var underTest: BouncerMessageInteractor @Before fun setUp() { MockitoAnnotations.initMocks(this) - repository = FakeBouncerMessageRepository() - userRepository = FakeUserRepository() userRepository.setUserInfos(listOf(PRIMARY_USER)) testScope = TestScope() - countDownTimerCallback = KotlinArgumentCaptor(CountDownTimerCallback::class.java) - biometricSettingsRepository = FakeBiometricSettingsRepository() - allowTestableLooperAsMainThread() whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN) biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true) + overrideResource(kg_trust_agent_disabled, "Trust agent is unavailable") } suspend fun TestScope.init() { userRepository.setSelectedUserInfo(PRIMARY_USER) - val featureFlags = FakeFeatureFlags() - featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) + val featureFlags = FakeFeatureFlags().apply { set(Flags.REVAMPED_BOUNCER_MESSAGES, true) } + primaryBouncerInteractor = + PrimaryBouncerInteractor( + bouncerRepository, + Mockito.mock(BouncerView::class.java), + Mockito.mock(Handler::class.java), + Mockito.mock(KeyguardStateController::class.java), + Mockito.mock(KeyguardSecurityModel::class.java), + Mockito.mock(PrimaryBouncerCallbackInteractor::class.java), + Mockito.mock(FalsingCollector::class.java), + Mockito.mock(DismissCallbackRegistry::class.java), + context, + keyguardUpdateMonitor, + fakeTrustRepository, + testScope.backgroundScope, + ) underTest = BouncerMessageInteractor( repository = repository, - factory = BouncerMessageFactory(biometricSettingsRepository, securityModel), userRepository = userRepository, countDownTimerUtil = countDownTimerUtil, - featureFlags = featureFlags + featureFlags = featureFlags, + updateMonitor = updateMonitor, + biometricSettingsRepository = biometricSettingsRepository, + applicationScope = this.backgroundScope, + trustRepository = fakeTrustRepository, + systemPropertiesHelper = systemPropertiesHelper, + primaryBouncerInteractor = primaryBouncerInteractor, + facePropertyRepository = fakeFacePropertyRepository, + deviceEntryFingerprintAuthRepository = fakeDeviceEntryFingerprintAuthRepository, + faceAuthRepository = fakeDeviceEntryFaceAuthRepository, + securityModel = securityModel ) - bouncerMessage = collectLastValue(underTest.bouncerMessage) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true) + fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false) + bouncerRepository.setPrimaryShow(true) + runCurrent() } @Test - fun onIncorrectSecurityInput_setsTheBouncerModelInTheRepository() = + fun onIncorrectSecurityInput_providesTheAppropriateValueForBouncerMessage() = testScope.runTest { init() + val bouncerMessage by collectLastValue(underTest.bouncerMessage) underTest.onPrimaryAuthIncorrectAttempt() - assertThat(repository.primaryAuthMessage).isNotNull() - assertThat( - context.resources.getString( - repository.primaryAuthMessage.value!!.message!!.messageResId!! - ) - ) - .isEqualTo("Wrong PIN. Try again.") + assertThat(bouncerMessage).isNotNull() + assertThat(primaryResMessage(bouncerMessage)).isEqualTo("Wrong PIN. Try again.") } @Test fun onUserStartsPrimaryAuthInput_clearsAllSetBouncerMessages() = testScope.runTest { init() - repository.setCustomMessage(message("not empty")) - repository.setFaceAcquisitionMessage(message("not empty")) - repository.setFingerprintAcquisitionMessage(message("not empty")) - repository.setPrimaryAuthMessage(message("not empty")) + val bouncerMessage by collectLastValue(underTest.bouncerMessage) + underTest.onPrimaryAuthIncorrectAttempt() + assertThat(primaryResMessage(bouncerMessage)).isEqualTo("Wrong PIN. Try again.") underTest.onPrimaryBouncerUserInput() - assertThat(repository.customMessage.value).isNull() - assertThat(repository.faceAcquisitionMessage.value).isNull() - assertThat(repository.fingerprintAcquisitionMessage.value).isNull() - assertThat(repository.primaryAuthMessage.value).isNull() + assertThat(primaryResMessage(bouncerMessage)) + .isEqualTo("Unlock with PIN or fingerprint") } @Test - fun onBouncerBeingHidden_clearsAllSetBouncerMessages() = - testScope.runTest { - init() - repository.setCustomMessage(message("not empty")) - repository.setFaceAcquisitionMessage(message("not empty")) - repository.setFingerprintAcquisitionMessage(message("not empty")) - repository.setPrimaryAuthMessage(message("not empty")) - - underTest.onBouncerBeingHidden() - - assertThat(repository.customMessage.value).isNull() - assertThat(repository.faceAcquisitionMessage.value).isNull() - assertThat(repository.fingerprintAcquisitionMessage.value).isNull() - assertThat(repository.primaryAuthMessage.value).isNull() - } - - @Test - fun setCustomMessage_setsRepositoryValue() = + fun setCustomMessage_propagateValue() = testScope.runTest { init() + val bouncerMessage by collectLastValue(underTest.bouncerMessage) underTest.setCustomMessage("not empty") - val customMessage = repository.customMessage - assertThat(customMessage.value!!.message!!.messageResId) - .isEqualTo(kg_unlock_with_pin_or_fp) - assertThat(customMessage.value!!.secondaryMessage!!.message).isEqualTo("not empty") + assertThat(primaryResMessage(bouncerMessage)) + .isEqualTo("Unlock with PIN or fingerprint") + assertThat(bouncerMessage?.secondaryMessage?.message).isEqualTo("not empty") underTest.setCustomMessage(null) - assertThat(customMessage.value).isNull() + assertThat(primaryResMessage(bouncerMessage)) + .isEqualTo("Unlock with PIN or fingerprint") + assertThat(bouncerMessage?.secondaryMessage?.message).isNull() } @Test - fun setFaceMessage_setsRepositoryValue() = + fun setFaceMessage_propagateValue() = testScope.runTest { init() + val bouncerMessage by collectLastValue(underTest.bouncerMessage) underTest.setFaceAcquisitionMessage("not empty") - val faceAcquisitionMessage = repository.faceAcquisitionMessage - - assertThat(faceAcquisitionMessage.value!!.message!!.messageResId) - .isEqualTo(kg_unlock_with_pin_or_fp) - assertThat(faceAcquisitionMessage.value!!.secondaryMessage!!.message) - .isEqualTo("not empty") + assertThat(primaryResMessage(bouncerMessage)) + .isEqualTo("Unlock with PIN or fingerprint") + assertThat(bouncerMessage?.secondaryMessage?.message).isEqualTo("not empty") underTest.setFaceAcquisitionMessage(null) - assertThat(faceAcquisitionMessage.value).isNull() + assertThat(primaryResMessage(bouncerMessage)) + .isEqualTo("Unlock with PIN or fingerprint") + assertThat(bouncerMessage?.secondaryMessage?.message).isNull() } @Test - fun setFingerprintMessage_setsRepositoryValue() = + fun setFingerprintMessage_propagateValue() = testScope.runTest { init() + val bouncerMessage by collectLastValue(underTest.bouncerMessage) underTest.setFingerprintAcquisitionMessage("not empty") - val fingerprintAcquisitionMessage = repository.fingerprintAcquisitionMessage - - assertThat(fingerprintAcquisitionMessage.value!!.message!!.messageResId) - .isEqualTo(kg_unlock_with_pin_or_fp) - assertThat(fingerprintAcquisitionMessage.value!!.secondaryMessage!!.message) - .isEqualTo("not empty") + assertThat(primaryResMessage(bouncerMessage)) + .isEqualTo("Unlock with PIN or fingerprint") + assertThat(bouncerMessage?.secondaryMessage?.message).isEqualTo("not empty") underTest.setFingerprintAcquisitionMessage(null) - assertThat(fingerprintAcquisitionMessage.value).isNull() + assertThat(primaryResMessage(bouncerMessage)) + .isEqualTo("Unlock with PIN or fingerprint") + assertThat(bouncerMessage?.secondaryMessage?.message).isNull() } @Test fun onPrimaryAuthLockout_startsTimerForSpecifiedNumberOfSeconds() = testScope.runTest { init() + val bouncerMessage by collectLastValue(underTest.bouncerMessage) underTest.onPrimaryAuthLockedOut(3) @@ -206,7 +235,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() { countDownTimerCallback.value.onTick(2000L) - val primaryMessage = repository.primaryAuthMessage.value!!.message!! + val primaryMessage = bouncerMessage!!.message!! assertThat(primaryMessage.messageResId!!) .isEqualTo(kg_too_many_failed_attempts_countdown) assertThat(primaryMessage.formatterArgs).isEqualTo(mapOf(Pair("count", 2))) @@ -216,10 +245,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() { fun onPrimaryAuthLockout_timerComplete_resetsRepositoryMessages() = testScope.runTest { init() - repository.setCustomMessage(message("not empty")) - repository.setFaceAcquisitionMessage(message("not empty")) - repository.setFingerprintAcquisitionMessage(message("not empty")) - repository.setPrimaryAuthMessage(message("not empty")) + val bouncerMessage by collectLastValue(underTest.bouncerMessage) underTest.onPrimaryAuthLockedOut(3) @@ -228,59 +254,269 @@ class BouncerMessageInteractorTest : SysuiTestCase() { countDownTimerCallback.value.onFinish() - assertThat(repository.customMessage.value).isNull() - assertThat(repository.faceAcquisitionMessage.value).isNull() - assertThat(repository.fingerprintAcquisitionMessage.value).isNull() - assertThat(repository.primaryAuthMessage.value).isNull() + assertThat(primaryResMessage(bouncerMessage)) + .isEqualTo("Unlock with PIN or fingerprint") + assertThat(bouncerMessage?.secondaryMessage?.message).isNull() + } + + @Test + fun onFaceLockout_propagatesState() = + testScope.runTest { + init() + val lockoutMessage by collectLastValue(underTest.bouncerMessage) + + fakeDeviceEntryFaceAuthRepository.setLockedOut(true) + runCurrent() + + assertThat(primaryResMessage(lockoutMessage)) + .isEqualTo("Unlock with PIN or fingerprint") + assertThat(secondaryResMessage(lockoutMessage)) + .isEqualTo("Can’t unlock with face. Too many attempts.") + + fakeDeviceEntryFaceAuthRepository.setLockedOut(false) + runCurrent() + + assertThat(primaryResMessage(lockoutMessage)) + .isEqualTo("Unlock with PIN or fingerprint") + assertThat(lockoutMessage?.secondaryMessage?.message).isNull() } @Test - fun bouncerMessage_hasPriorityOrderOfMessages() = + fun onFaceLockout_whenItIsClass3_propagatesState() = testScope.runTest { init() - repository.setBiometricAuthMessage(message("biometric message")) - repository.setFaceAcquisitionMessage(message("face acquisition message")) - repository.setFingerprintAcquisitionMessage(message("fingerprint acquisition message")) - repository.setPrimaryAuthMessage(message("primary auth message")) - repository.setAuthFlagsMessage(message("auth flags message")) - repository.setBiometricLockedOutMessage(message("biometrics locked out")) - repository.setCustomMessage(message("custom message")) + val lockoutMessage by collectLastValue(underTest.bouncerMessage) + fakeFacePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG)) + fakeDeviceEntryFaceAuthRepository.setLockedOut(true) + runCurrent() + + assertThat(primaryResMessage(lockoutMessage)).isEqualTo("Enter PIN") + assertThat(secondaryResMessage(lockoutMessage)) + .isEqualTo("PIN is required after too many attempts") + + fakeDeviceEntryFaceAuthRepository.setLockedOut(false) + runCurrent() - assertThat(bouncerMessage()).isEqualTo(message("primary auth message")) + assertThat(primaryResMessage(lockoutMessage)) + .isEqualTo("Unlock with PIN or fingerprint") + assertThat(lockoutMessage?.secondaryMessage?.message).isNull() + } + + @Test + fun onFingerprintLockout_propagatesState() = + testScope.runTest { + init() + val lockedOutMessage by collectLastValue(underTest.bouncerMessage) - repository.setPrimaryAuthMessage(null) + fakeDeviceEntryFingerprintAuthRepository.setLockedOut(true) + runCurrent() - assertThat(bouncerMessage()).isEqualTo(message("biometric message")) + assertThat(primaryResMessage(lockedOutMessage)).isEqualTo("Enter PIN") + assertThat(secondaryResMessage(lockedOutMessage)) + .isEqualTo("PIN is required after too many attempts") - repository.setBiometricAuthMessage(null) + fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false) + runCurrent() - assertThat(bouncerMessage()).isEqualTo(message("fingerprint acquisition message")) + assertThat(primaryResMessage(lockedOutMessage)) + .isEqualTo("Unlock with PIN or fingerprint") + assertThat(lockedOutMessage?.secondaryMessage?.message).isNull() + } - repository.setFingerprintAcquisitionMessage(null) + @Test + fun onRestartForMainlineUpdate_shouldProvideRelevantMessage() = + testScope.runTest { + init() + whenever(systemPropertiesHelper.get("sys.boot.reason.last")) + .thenReturn("reboot,mainline_update") + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) - assertThat(bouncerMessage()).isEqualTo(message("face acquisition message")) + verifyMessagesForAuthFlag( + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to + Pair("Enter PIN", "Device updated. Enter PIN to continue.") + ) + } - repository.setFaceAcquisitionMessage(null) + @Test + fun onAuthFlagsChanged_withTrustNotManagedAndNoBiometrics_isANoop() = + testScope.runTest { + init() + fakeTrustRepository.setTrustUsuallyManaged(false) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) + + val defaultMessage = Pair("Enter PIN", null) + + verifyMessagesForAuthFlag( + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to + defaultMessage, + LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to + defaultMessage, + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to + defaultMessage, + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to + defaultMessage, + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to + defaultMessage, + LockPatternUtils.StrongAuthTracker + .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to defaultMessage, + LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to + defaultMessage, + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to + defaultMessage, + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to + Pair("Enter PIN", "For added security, device was locked by work policy") + ) + } - assertThat(bouncerMessage()).isEqualTo(message("custom message")) + @Test + fun authFlagsChanges_withTrustManaged_providesDifferentMessages() = + testScope.runTest { + init() - repository.setCustomMessage(null) + userRepository.setSelectedUserInfo(PRIMARY_USER) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) + + fakeTrustRepository.setCurrentUserTrustManaged(true) + fakeTrustRepository.setTrustUsuallyManaged(true) + + val defaultMessage = Pair("Enter PIN", null) + + verifyMessagesForAuthFlag( + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED to defaultMessage, + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to + Pair("Enter PIN", "PIN is required after device restarts"), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to + Pair("Enter PIN", "Added security required. PIN not used for a while."), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to + Pair("Enter PIN", "For added security, device was locked by work policy"), + LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to + Pair("Enter PIN", "Trust agent is unavailable"), + LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to + Pair("Enter PIN", "Trust agent is unavailable"), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to + Pair("Enter PIN", "PIN is required after lockdown"), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to + Pair("Enter PIN", "Update will install when device not in use"), + LockPatternUtils.StrongAuthTracker + .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to + Pair( + "Enter PIN", + "Added security required. Device wasn’t unlocked for a while." + ), + ) + } - assertThat(bouncerMessage()).isEqualTo(message("auth flags message")) + @Test + fun authFlagsChanges_withFaceEnrolled_providesDifferentMessages() = + testScope.runTest { + init() + userRepository.setSelectedUserInfo(PRIMARY_USER) + fakeTrustRepository.setTrustUsuallyManaged(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) + + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) + val defaultMessage = Pair("Enter PIN", null) + + verifyMessagesForAuthFlag( + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED to defaultMessage, + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to + defaultMessage, + LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to + defaultMessage, + LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to + defaultMessage, + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to + Pair("Enter PIN", "PIN is required after device restarts"), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to + Pair("Enter PIN", "Added security required. PIN not used for a while."), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to + Pair("Enter PIN", "For added security, device was locked by work policy"), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to + Pair("Enter PIN", "PIN is required after lockdown"), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to + Pair("Enter PIN", "Update will install when device not in use"), + LockPatternUtils.StrongAuthTracker + .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to + Pair( + "Enter PIN", + "Added security required. Device wasn’t unlocked for a while." + ), + ) + } - repository.setAuthFlagsMessage(null) + @Test + fun authFlagsChanges_withFingerprintEnrolled_providesDifferentMessages() = + testScope.runTest { + init() + userRepository.setSelectedUserInfo(PRIMARY_USER) + fakeTrustRepository.setCurrentUserTrustManaged(false) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) - assertThat(bouncerMessage()).isEqualTo(message("biometrics locked out")) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true) - repository.setBiometricLockedOutMessage(null) + verifyMessagesForAuthFlag( + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED to + Pair("Unlock with PIN or fingerprint", null) + ) - // sets the default message if everything else is null - assertThat(bouncerMessage()!!.message!!.messageResId) - .isEqualTo(kg_unlock_with_pin_or_fp) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false) + + verifyMessagesForAuthFlag( + LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to + Pair("Enter PIN", null), + LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to + Pair("Enter PIN", null), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to + Pair("Enter PIN", "PIN is required after device restarts"), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to + Pair("Enter PIN", "Added security required. PIN not used for a while."), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to + Pair("Enter PIN", "For added security, device was locked by work policy"), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to + Pair("Enter PIN", "PIN is required after lockdown"), + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to + Pair("Enter PIN", "Update will install when device not in use"), + LockPatternUtils.StrongAuthTracker + .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to + Pair( + "Enter PIN", + "Added security required. Device wasn’t unlocked for a while." + ), + ) } - private fun message(value: String): BouncerMessageModel { - return BouncerMessageModel(message = Message(message = value)) + private fun primaryResMessage(bouncerMessage: BouncerMessageModel?) = + resString(bouncerMessage?.message?.messageResId) + + private fun secondaryResMessage(bouncerMessage: BouncerMessageModel?) = + resString(bouncerMessage?.secondaryMessage?.messageResId) + + private fun resString(msgResId: Int?): String? = + msgResId?.let { context.resources.getString(it) } + + private fun TestScope.verifyMessagesForAuthFlag( + vararg authFlagToExpectedMessages: Pair<Int, Pair<String, String?>> + ) { + val authFlagsMessage by collectLastValue(underTest.bouncerMessage) + + authFlagToExpectedMessages.forEach { (flag, messagePair) -> + biometricSettingsRepository.setAuthenticationFlags( + AuthenticationFlags(PRIMARY_USER_ID, flag) + ) + runCurrent() + + assertThat(primaryResMessage(authFlagsMessage)).isEqualTo(messagePair.first) + if (messagePair.second == null) { + assertThat(authFlagsMessage?.secondaryMessage?.messageResId).isEqualTo(0) + assertThat(authFlagsMessage?.secondaryMessage?.message).isNull() + } else { + assertThat(authFlagsMessage?.secondaryMessage?.messageResId).isNotEqualTo(0) + assertThat(secondaryResMessage(authFlagsMessage)).isEqualTo(messagePair.second) + } + } } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt index 9011c2f296c3..2f7dde02fdce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt @@ -45,7 +45,7 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { private val underTest = PinBouncerViewModel( applicationContext = context, - applicationScope = testScope.backgroundScope, + viewModelScope = testScope.backgroundScope, interactor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt index 2c96bcc9dd33..da2534d6fb14 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt @@ -46,7 +46,7 @@ class BouncerViewModelTest : SysuiTestCase() { private val testScope = utils.testScope private val authenticationInteractor = utils.authenticationInteractor( - repository = utils.authenticationRepository(), + repository = utils.authenticationRepository, ) private val bouncerInteractor = utils.bouncerInteractor( @@ -66,7 +66,8 @@ class BouncerViewModelTest : SysuiTestCase() { authMethodsToTest().forEach { authMethod -> utils.authenticationRepository.setAuthenticationMethod(authMethod) - val job = underTest.authMethod.onEach { authMethodViewModel = it }.launchIn(this) + val job = + underTest.authMethodViewModel.onEach { authMethodViewModel = it }.launchIn(this) runCurrent() if (authMethod.isSecure) { @@ -86,22 +87,43 @@ class BouncerViewModelTest : SysuiTestCase() { } @Test - fun authMethod_reusesInstances() = + fun authMethodChanged_doesNotReuseInstances() = testScope.runTest { val seen = mutableMapOf<DomainLayerAuthenticationMethodModel, AuthMethodBouncerViewModel>() val authMethodViewModel: AuthMethodBouncerViewModel? by - collectLastValue(underTest.authMethod) + collectLastValue(underTest.authMethodViewModel) + // First pass, populate our "seen" map: authMethodsToTest().forEach { authMethod -> utils.authenticationRepository.setAuthenticationMethod(authMethod) authMethodViewModel?.let { seen[authMethod] = it } } - // Second pass, assert same instances are reused: + // Second pass, assert same instances are not reused: + authMethodsToTest().forEach { authMethod -> + utils.authenticationRepository.setAuthenticationMethod(authMethod) + authMethodViewModel?.let { + assertThat(it.authenticationMethod).isEqualTo(authMethod) + assertThat(it).isNotSameInstanceAs(seen[authMethod]) + } + } + } + + @Test + fun authMethodUnchanged_reusesInstances() = + testScope.runTest { authMethodsToTest().forEach { authMethod -> utils.authenticationRepository.setAuthenticationMethod(authMethod) - authMethodViewModel?.let { assertThat(it).isSameInstanceAs(seen[authMethod]) } + val firstInstance: AuthMethodBouncerViewModel? = + collectLastValue(underTest.authMethodViewModel).invoke() + + utils.authenticationRepository.setAuthenticationMethod(authMethod) + val secondInstance: AuthMethodBouncerViewModel? = + collectLastValue(underTest.authMethodViewModel).invoke() + + firstInstance?.let { assertThat(it.authenticationMethod).isEqualTo(authMethod) } + assertThat(secondInstance).isSameInstanceAs(firstInstance) } } @@ -136,7 +158,7 @@ class BouncerViewModelTest : SysuiTestCase() { testScope.runTest { val isInputEnabled by collectLastValue( - underTest.authMethod.flatMapLatest { authViewModel -> + underTest.authMethodViewModel.flatMapLatest { authViewModel -> authViewModel?.isInputEnabled ?: emptyFlow() } ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt index 3375184c1cf6..c1b33542267b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt @@ -17,10 +17,11 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainAuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.res.R import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel @@ -28,6 +29,7 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -59,7 +61,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { ) private val underTest = PasswordBouncerViewModel( - applicationScope = testScope.backgroundScope, + viewModelScope = testScope.backgroundScope, interactor = bouncerInteractor, isInputEnabled = MutableStateFlow(true).asStateFlow(), ) @@ -76,19 +78,13 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password - ) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - - underTest.onShown() + lockDeviceAndOpenPasswordBouncer() assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD) assertThat(password).isEqualTo("") assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + assertThat(underTest.authenticationMethod) + .isEqualTo(DomainAuthenticationMethodModel.Password) } @Test @@ -97,15 +93,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password - ) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() - runCurrent() + lockDeviceAndOpenPasswordBouncer() underTest.onPasswordInputChanged("password") @@ -118,16 +106,9 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { fun onAuthenticateKeyPressed_whenCorrect() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.desiredScene) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password - ) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() - underTest.onPasswordInputChanged("password") + lockDeviceAndOpenPasswordBouncer() + underTest.onPasswordInputChanged("password") underTest.onAuthenticateKeyPressed() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) @@ -139,16 +120,9 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password - ) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() - underTest.onPasswordInputChanged("wrong") + lockDeviceAndOpenPasswordBouncer() + underTest.onPasswordInputChanged("wrong") underTest.onAuthenticateKeyPressed() assertThat(password).isEqualTo("") @@ -185,14 +159,9 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password - ) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() + lockDeviceAndOpenPasswordBouncer() + + // Enter the wrong password: underTest.onPasswordInputChanged("wrong") underTest.onAuthenticateKeyPressed() assertThat(password).isEqualTo("") @@ -213,14 +182,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(sceneInteractor.desiredScene) val password by collectLastValue(underTest.password) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password - ) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() + lockDeviceAndOpenPasswordBouncer() // The user types a password. underTest.onPasswordInputChanged("password") @@ -243,6 +205,18 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } + private fun TestScope.lockDeviceAndOpenPasswordBouncer() { + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Password) + utils.authenticationRepository.setUnlocked(false) + sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") + sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") + + assertThat(collectLastValue(sceneInteractor.desiredScene).invoke()) + .isEqualTo(SceneModel(SceneKey.Bouncer)) + underTest.onShown() + runCurrent() + } + companion object { private const val ENTER_YOUR_PASSWORD = "Enter your password" private const val WRONG_PASSWORD = "Wrong password" diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt index 102cfe21838e..bf109d9b2b61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt @@ -17,12 +17,13 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainAuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate as Point import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.res.R import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel @@ -64,7 +65,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { private val underTest = PatternBouncerViewModel( applicationContext = context, - applicationScope = testScope.backgroundScope, + viewModelScope = testScope.backgroundScope, interactor = bouncerInteractor, isInputEnabled = MutableStateFlow(true).asStateFlow(), ) @@ -85,14 +86,14 @@ class PatternBouncerViewModelTest : SysuiTestCase() { val message by collectLastValue(bouncerViewModel.message) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) - transitionToPatternBouncer() - - underTest.onShown() + lockDeviceAndOpenPatternBouncer() assertThat(message?.text).isEqualTo(ENTER_YOUR_PATTERN) assertThat(selectedDots).isEmpty() assertThat(currentDot).isNull() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + assertThat(underTest.authenticationMethod) + .isEqualTo(DomainAuthenticationMethodModel.Pattern) } @Test @@ -102,9 +103,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { val message by collectLastValue(bouncerViewModel.message) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) - transitionToPatternBouncer() - underTest.onShown() - runCurrent() + lockDeviceAndOpenPatternBouncer() underTest.onDragStart() @@ -120,8 +119,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.desiredScene) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) - transitionToPatternBouncer() - underTest.onShown() + lockDeviceAndOpenPatternBouncer() underTest.onDragStart() assertThat(currentDot).isNull() CORRECT_PATTERN.forEachIndexed { index, coordinate -> @@ -158,8 +156,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { val message by collectLastValue(bouncerViewModel.message) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) - transitionToPatternBouncer() - underTest.onShown() + lockDeviceAndOpenPatternBouncer() underTest.onDragStart() CORRECT_PATTERN.subList(0, 3).forEach { coordinate -> dragToCoordinate(coordinate) } @@ -175,8 +172,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { fun onDrag_shouldIncludeDotsThatWereSkippedOverAlongTheSameRow() = testScope.runTest { val selectedDots by collectLastValue(underTest.selectedDots) - transitionToPatternBouncer() - underTest.onShown() + lockDeviceAndOpenPatternBouncer() /* * Pattern setup, coordinates are (column, row) @@ -202,8 +198,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { fun onDrag_shouldIncludeDotsThatWereSkippedOverAlongTheSameColumn() = testScope.runTest { val selectedDots by collectLastValue(underTest.selectedDots) - transitionToPatternBouncer() - underTest.onShown() + lockDeviceAndOpenPatternBouncer() /* * Pattern setup, coordinates are (column, row) @@ -229,8 +224,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { fun onDrag_shouldIncludeDotsThatWereSkippedOverAlongTheDiagonal() = testScope.runTest { val selectedDots by collectLastValue(underTest.selectedDots) - transitionToPatternBouncer() - underTest.onShown() + lockDeviceAndOpenPatternBouncer() /* * Pattern setup @@ -258,8 +252,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { fun onDrag_shouldNotIncludeDotIfItIsNotOnTheLine() = testScope.runTest { val selectedDots by collectLastValue(underTest.selectedDots) - transitionToPatternBouncer() - underTest.onShown() + lockDeviceAndOpenPatternBouncer() /* * Pattern setup @@ -287,8 +280,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { fun onDrag_shouldNotIncludeSkippedOverDotsIfTheyAreAlreadySelected() = testScope.runTest { val selectedDots by collectLastValue(underTest.selectedDots) - transitionToPatternBouncer() - underTest.onShown() + lockDeviceAndOpenPatternBouncer() /* * Pattern setup @@ -315,20 +307,10 @@ class PatternBouncerViewModelTest : SysuiTestCase() { @Test fun onDragEnd_whenPatternTooShort() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) - val selectedDots by collectLastValue(underTest.selectedDots) - val currentDot by collectLastValue(underTest.currentDot) val throttlingDialogMessage by collectLastValue(bouncerViewModel.throttlingDialogMessage) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pattern - ) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() + lockDeviceAndOpenPatternBouncer() // Enter a pattern that's too short more than enough times that would normally trigger // throttling if the pattern were not too short and wrong: @@ -337,7 +319,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { underTest.onDragStart() CORRECT_PATTERN.subList( 0, - authenticationInteractor.minPatternLength - 1, + utils.authenticationRepository.minPatternLength - 1, ) .forEach { coordinate -> underTest.onDrag( @@ -362,10 +344,10 @@ class PatternBouncerViewModelTest : SysuiTestCase() { val message by collectLastValue(bouncerViewModel.message) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) - transitionToPatternBouncer() - underTest.onShown() + lockDeviceAndOpenPatternBouncer() + underTest.onDragStart() - CORRECT_PATTERN.subList(2, 7).forEach { coordinate -> dragToCoordinate(coordinate) } + CORRECT_PATTERN.subList(2, 7).forEach(::dragToCoordinate) underTest.onDragEnd() assertThat(selectedDots).isEmpty() assertThat(currentDot).isNull() @@ -373,7 +355,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) // Enter the correct pattern: - CORRECT_PATTERN.forEach { coordinate -> dragToCoordinate(coordinate) } + CORRECT_PATTERN.forEach(::dragToCoordinate) underTest.onDragEnd() @@ -382,7 +364,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { private fun dragOverCoordinates(vararg coordinatesDragged: Point) { underTest.onDragStart() - coordinatesDragged.forEach { dragToCoordinate(it) } + coordinatesDragged.forEach(::dragToCoordinate) } private fun dragToCoordinate(coordinate: Point) { @@ -394,13 +376,15 @@ class PatternBouncerViewModelTest : SysuiTestCase() { ) } - private fun TestScope.transitionToPatternBouncer() { + private fun TestScope.lockDeviceAndOpenPatternBouncer() { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pattern) utils.authenticationRepository.setUnlocked(false) sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") assertThat(collectLastValue(sceneInteractor.desiredScene).invoke()) .isEqualTo(SceneModel(SceneKey.Bouncer)) + underTest.onShown() + runCurrent() } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index 35238ce00057..2576204c247f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -17,11 +17,12 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainAuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.res.R import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel @@ -30,6 +31,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -62,7 +64,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { private val underTest = PinBouncerViewModel( applicationContext = context, - applicationScope = testScope.backgroundScope, + viewModelScope = testScope.backgroundScope, interactor = bouncerInteractor, isInputEnabled = MutableStateFlow(true).asStateFlow(), ) @@ -90,6 +92,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(message?.text).isEqualTo(ENTER_YOUR_PIN) assertThat(pin).isEmpty() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + assertThat(underTest.authenticationMethod) + .isEqualTo(DomainAuthenticationMethodModel.Pin) } @Test @@ -120,14 +124,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") + lockDeviceAndOpenPinBouncer() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() - runCurrent() underTest.onPinButtonClicked(1) assertThat(pin).hasSize(1) @@ -141,15 +139,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onPinEdit() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() + lockDeviceAndOpenPinBouncer() underTest.onPinButtonClicked(1) underTest.onPinButtonClicked(2) @@ -168,18 +159,13 @@ class PinBouncerViewModelTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") + lockDeviceAndOpenPinBouncer() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() - runCurrent() underTest.onPinButtonClicked(1) underTest.onPinButtonClicked(2) underTest.onPinButtonClicked(3) underTest.onPinButtonClicked(4) + runCurrent() underTest.onBackspaceButtonLongPressed() @@ -192,13 +178,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { fun onAuthenticateButtonClicked_whenCorrect() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.desiredScene) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") + lockDeviceAndOpenPinBouncer() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> underTest.onPinButtonClicked(digit) } @@ -214,13 +195,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") + lockDeviceAndOpenPinBouncer() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() underTest.onPinButtonClicked(1) underTest.onPinButtonClicked(2) underTest.onPinButtonClicked(3) @@ -240,13 +216,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") + lockDeviceAndOpenPinBouncer() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() underTest.onPinButtonClicked(1) underTest.onPinButtonClicked(2) underTest.onPinButtonClicked(3) @@ -272,14 +243,9 @@ class PinBouncerViewModelTest : SysuiTestCase() { fun onAutoConfirm_whenCorrect() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.desiredScene) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) utils.authenticationRepository.setAutoConfirmEnabled(true) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") + lockDeviceAndOpenPinBouncer() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> underTest.onPinButtonClicked(digit) } @@ -293,14 +259,9 @@ class PinBouncerViewModelTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) utils.authenticationRepository.setAutoConfirmEnabled(true) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") + lockDeviceAndOpenPinBouncer() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() FakeAuthenticationRepository.DEFAULT_PIN.dropLast(1).forEach { digit -> underTest.onPinButtonClicked(digit) } @@ -318,13 +279,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(sceneInteractor.desiredScene) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") - sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.onShown() + lockDeviceAndOpenPinBouncer() // The user types a PIN. FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> @@ -401,6 +356,18 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden) } + private fun TestScope.lockDeviceAndOpenPinBouncer() { + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setUnlocked(false) + sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") + sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") + + assertThat(collectLastValue(sceneInteractor.desiredScene).invoke()) + .isEqualTo(SceneModel(SceneKey.Bouncer)) + underTest.onShown() + runCurrent() + } + companion object { private const val ENTER_YOUR_PIN = "Enter your pin" private const val WRONG_PIN = "Wrong pin" diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt index 12fa4c85c34d..477f4555ea65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt @@ -176,7 +176,7 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes !isFeatureEnabled -> KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice::class .java - hasServiceInfos && hasFavorites -> + hasServiceInfos && (hasFavorites || hasPanels) -> KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java else -> KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt index 29d75001148a..7f784d88da6d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogcatEchoTracker import com.android.systemui.user.data.repository.FakeUserRepository +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope @@ -260,4 +261,19 @@ class TrustRepositoryTest : SysuiTestCase() { listener.value.onIsActiveUnlockRunningChanged(true, users[0].id) assertThat(isCurrentUserActiveUnlockRunning).isTrue() } + + @Test + fun isTrustUsuallyManaged_providesTheValueForCurrentUser() = + testScope.runTest { + runCurrent() + val trustUsuallyManaged by collectLastValue(underTest.isCurrentUserTrustUsuallyManaged) + whenever(trustManager.isTrustUsuallyManaged(users[0].id)).thenReturn(true) + whenever(trustManager.isTrustUsuallyManaged(users[1].id)).thenReturn(false) + + userRepository.setSelectedUserInfo(users[0]) + + assertThat(trustUsuallyManaged).isTrue() + userRepository.setSelectedUserInfo(users[1]) + assertThat(trustUsuallyManaged).isFalse() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt index 49f536e26ce6..74b3fce12790 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt @@ -85,6 +85,7 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { fun testCarouselScroll_shortScroll() { whenever(mediaCarousel.isLayoutRtl).thenReturn(false) whenever(mediaCarousel.relativeScrollX).thenReturn(300) + whenever(mediaCarousel.scrollX).thenReturn(300) mediaCarousel.touchListener?.onTouchEvent(motionEventUp) executor.runAllReady() @@ -96,6 +97,7 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { fun testCarouselScroll_shortScroll_isRTL() { whenever(mediaCarousel.isLayoutRtl).thenReturn(true) whenever(mediaCarousel.relativeScrollX).thenReturn(300) + whenever(mediaCarousel.scrollX).thenReturn(carouselWidth - 300) mediaCarousel.touchListener?.onTouchEvent(motionEventUp) executor.runAllReady() @@ -107,6 +109,7 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { fun testCarouselScroll_longScroll() { whenever(mediaCarousel.isLayoutRtl).thenReturn(false) whenever(mediaCarousel.relativeScrollX).thenReturn(600) + whenever(mediaCarousel.scrollX).thenReturn(600) mediaCarousel.touchListener?.onTouchEvent(motionEventUp) executor.runAllReady() @@ -118,6 +121,7 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { fun testCarouselScroll_longScroll_isRTL() { whenever(mediaCarousel.isLayoutRtl).thenReturn(true) whenever(mediaCarousel.relativeScrollX).thenReturn(600) + whenever(mediaCarousel.scrollX).thenReturn(carouselWidth - 600) mediaCarousel.touchListener?.onTouchEvent(motionEventUp) executor.runAllReady() diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt index 435a1f1327d9..5eda263be7a4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt @@ -57,10 +57,7 @@ class PowerInteractorTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - repository = - FakePowerRepository( - initialInteractive = true, - ) + repository = FakePowerRepository() underTest = PowerInteractor( repository, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java index c041cb6d0b1f..f8a98afef978 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java @@ -25,6 +25,8 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.SysuiTestCase; +import com.android.systemui.flags.FakeFeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.qs.QSHost; import org.junit.Before; @@ -46,10 +48,13 @@ public class TileAdapterTest extends SysuiTestCase { @Before public void setup() throws Exception { + FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags(); + fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false); + MockitoAnnotations.initMocks(this); TestableLooper.get(this).runWithLooper(() -> mTileAdapter = - new TileAdapter(mContext, mQSHost, new UiEventLoggerFake())); + new TileAdapter(mContext, mQSHost, new UiEventLoggerFake(), fakeFeatureFlags)); } @Test 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 623a8e0f7437..82ee99a29427 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 @@ -14,6 +14,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake +import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QSTile @@ -22,6 +23,7 @@ import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialogViewModel import com.android.systemui.statusbar.policy.BluetoothController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -50,6 +52,8 @@ class BluetoothTileTest : SysuiTestCase() { @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var bluetoothController: BluetoothController @Mock private lateinit var uiEventLogger: QsEventLogger + @Mock private lateinit var featureFlags: FeatureFlagsClassic + @Mock private lateinit var bluetoothTileDialogViewModel: BluetoothTileDialogViewModel private lateinit var testableLooper: TestableLooper private lateinit var tile: FakeBluetoothTile @@ -73,6 +77,8 @@ class BluetoothTileTest : SysuiTestCase() { activityStarter, qsLogger, bluetoothController, + featureFlags, + bluetoothTileDialogViewModel ) tile.initialize() @@ -220,6 +226,8 @@ class BluetoothTileTest : SysuiTestCase() { activityStarter: ActivityStarter, qsLogger: QSLogger, bluetoothController: BluetoothController, + featureFlags: FeatureFlagsClassic, + bluetoothTileDialogViewModel: BluetoothTileDialogViewModel ) : BluetoothTile( qsHost, @@ -232,6 +240,8 @@ class BluetoothTileTest : SysuiTestCase() { activityStarter, qsLogger, bluetoothController, + featureFlags, + bluetoothTileDialogViewModel ) { var restrictionChecked: String? = null 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 c4cf3425ed76..0552ced1d678 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 @@ -17,6 +17,8 @@ package com.android.systemui.qs.tiles import android.os.Handler +import android.provider.Settings +import android.safetycenter.SafetyCenterManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest @@ -68,6 +70,8 @@ class CameraToggleTileTest : SysuiTestCase() { private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var uiEventLogger: QsEventLoggerFake + @Mock + private lateinit var safetyCenterManager: SafetyCenterManager private lateinit var testableLooper: TestableLooper private lateinit var tile: CameraToggleTile @@ -89,7 +93,8 @@ class CameraToggleTileTest : SysuiTestCase() { activityStarter, qsLogger, privacyController, - keyguardStateController) + keyguardStateController, + safetyCenterManager) } @After @@ -117,4 +122,13 @@ class CameraToggleTileTest : SysuiTestCase() { assertThat(state.icon) .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_camera_access_icon_off)) } + + @Test + fun testLongClickIntent() { + whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true) + assertThat(tile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_CONTROLS) + + whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false) + assertThat(tile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_SETTINGS) + } } 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 bce4c06d320f..3bf59ca62024 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 @@ -172,6 +172,9 @@ class DeviceControlsTileTest : SysuiTestCase() { @Test fun testNotAvailableControls() { featureEnabled = false + + // Destroy previous tile + tile.destroy() tile = createTile() assertThat(tile.isAvailable).isFalse() 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 a0c107340dcd..954d30edf143 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 @@ -226,6 +226,10 @@ public class DreamTileTest extends SysuiTestCase { assertTrue(supportedTileOnlySystemUser.isAvailable()); when(mUserTracker.getUserInfo()).thenReturn(nonMainUserInfo); assertFalse(supportedTileOnlySystemUser.isAvailable()); + + destroyTile(unsupportedTile); + destroyTile(supportedTileAllUsers); + destroyTile(supportedTileOnlySystemUser); } @Test @@ -250,6 +254,8 @@ public class DreamTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked), dockedTile.getState().icon); + + destroyTile(dockedTile); } private void setScreensaverEnabled(boolean enabled) { @@ -257,6 +263,11 @@ public class DreamTileTest extends SysuiTestCase { DEFAULT_USER); } + private void destroyTile(QSTileImpl<?> tile) { + tile.destroy(); + mTestableLooper.processAllMessages(); + } + private DreamTile constructTileForTest(boolean dreamSupported, boolean dreamOnlyEnabledForSystemUser) { return new DreamTile( 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 3511bb31c443..0fcfdb6f318f 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 @@ -17,6 +17,8 @@ package com.android.systemui.qs.tiles import android.os.Handler +import android.provider.Settings +import android.safetycenter.SafetyCenterManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest @@ -68,6 +70,8 @@ class MicrophoneToggleTileTest : SysuiTestCase() { private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var uiEventLogger: QsEventLogger + @Mock + private lateinit var safetyCenterManager: SafetyCenterManager private lateinit var testableLooper: TestableLooper private lateinit var tile: MicrophoneToggleTile @@ -90,7 +94,8 @@ class MicrophoneToggleTileTest : SysuiTestCase() { activityStarter, qsLogger, privacyController, - keyguardStateController) + keyguardStateController, + safetyCenterManager) } @After @@ -116,4 +121,13 @@ class MicrophoneToggleTileTest : SysuiTestCase() { assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_mic_access_off)) } + + @Test + fun testLongClickIntent() { + whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true) + assertThat(tile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_CONTROLS) + + whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false) + assertThat(tile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_SETTINGS) + } } 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 df6993d11447..440270b6ebfa 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 @@ -216,7 +216,7 @@ public class RotationLockTileTest extends SysuiTestCase { public void testSecondaryString_rotationResolverDisabled_isEmpty() { mTestableResources.addOverride(com.android.internal.R.bool.config_allowRotationResolver, false); - mLockTile = new RotationLockTile( + RotationLockTile otherTile = new RotationLockTile( mHost, mUiEventLogger, mTestableLooper.getLooper(), @@ -232,10 +232,12 @@ public class RotationLockTileTest extends SysuiTestCase { new FakeSettings() ); - mLockTile.refreshState(); + otherTile.refreshState(); mTestableLooper.processAllMessages(); - assertEquals("", mLockTile.getState().secondaryLabel.toString()); + assertEquals("", otherTile.getState().secondaryLabel.toString()); + + destroyTile(otherTile); } @Test @@ -258,6 +260,12 @@ public class RotationLockTileTest extends SysuiTestCase { assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on)); } + + private void destroyTile(QSTileImpl<?> tile) { + tile.destroy(); + mTestableLooper.processAllMessages(); + } + private void enableAutoRotation() { when(mRotationPolicyWrapper.isRotationLocked()).thenReturn(false); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt new file mode 100644 index 000000000000..fc2b7a64957d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt @@ -0,0 +1,95 @@ +/* + * 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.qs.tiles.dialog.bluetooth + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.settingslib.bluetooth.LocalBluetoothAdapter +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class BluetoothStateInteractorTest : SysuiTestCase() { + @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + private val testScope = TestScope() + + private lateinit var bluetoothStateInteractor: BluetoothStateInteractor + + @Mock private lateinit var bluetoothAdapter: LocalBluetoothAdapter + @Mock private lateinit var localBluetoothManager: LocalBluetoothManager + + @Before + fun setUp() { + bluetoothStateInteractor = + BluetoothStateInteractor(localBluetoothManager, testScope.backgroundScope) + `when`(localBluetoothManager.bluetoothAdapter).thenReturn(bluetoothAdapter) + } + + @Test + fun testGet_isBluetoothEnabled() { + testScope.runTest { + `when`(bluetoothAdapter.isEnabled).thenReturn(true) + + assertThat(bluetoothStateInteractor.isBluetoothEnabled).isTrue() + } + } + + @Test + fun testGet_isBluetoothDisabled() { + testScope.runTest { + `when`(bluetoothAdapter.isEnabled).thenReturn(false) + + assertThat(bluetoothStateInteractor.isBluetoothEnabled).isFalse() + } + } + + @Test + fun testSet_bluetoothEnabled() { + testScope.runTest { + `when`(bluetoothAdapter.isEnabled).thenReturn(false) + + bluetoothStateInteractor.isBluetoothEnabled = true + verify(bluetoothAdapter).enable() + } + } + + @Test + fun testSet_bluetoothNoChange() { + testScope.runTest { + `when`(bluetoothAdapter.isEnabled).thenReturn(false) + + bluetoothStateInteractor.isBluetoothEnabled = false + verify(bluetoothAdapter, never()).enable() + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepositoryTest.kt new file mode 100644 index 000000000000..da8f60a9b926 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepositoryTest.kt @@ -0,0 +1,79 @@ +/* + * 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.qs.tiles.dialog.bluetooth + +import android.bluetooth.BluetoothAdapter +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class BluetoothTileDialogRepositoryTest : SysuiTestCase() { + + @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var localBluetoothManager: LocalBluetoothManager + + @Mock private lateinit var bluetoothAdapter: BluetoothAdapter + + @Mock private lateinit var cachedDeviceManager: CachedBluetoothDeviceManager + + @Mock private lateinit var cachedDevicesCopy: Collection<CachedBluetoothDevice> + + private lateinit var repository: BluetoothTileDialogRepository + + @Before + fun setUp() { + `when`(localBluetoothManager.cachedDeviceManager).thenReturn(cachedDeviceManager) + `when`(cachedDeviceManager.cachedDevicesCopy).thenReturn(cachedDevicesCopy) + + repository = BluetoothTileDialogRepository(localBluetoothManager, bluetoothAdapter) + } + + @Test + fun testCachedDevices_bluetoothOff_emptyList() { + `when`(bluetoothAdapter.isEnabled).thenReturn(false) + + val result = repository.cachedDevices + + assertThat(result).isEmpty() + } + + @Test + fun testCachedDevices_bluetoothOn_returnDevice() { + `when`(bluetoothAdapter.isEnabled).thenReturn(true) + + val result = repository.cachedDevices + + assertThat(result).isEqualTo(cachedDevicesCopy) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt new file mode 100644 index 000000000000..89fa55b319ba --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt @@ -0,0 +1,183 @@ +/* + * 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.qs.tiles.dialog.bluetooth + +import android.graphics.drawable.Drawable +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.test.filters.SmallTest +import com.android.internal.logging.UiEventLogger +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.systemui.SysuiTestCase +import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.DISABLED_ALPHA +import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ENABLED_ALPHA +import com.android.systemui.res.R +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class BluetoothTileDialogTest : SysuiTestCase() { + companion object { + const val DEVICE_NAME = "device" + const val DEVICE_CONNECTION_SUMMARY = "active" + const val ENABLED = true + } + + @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice + + @Mock private lateinit var bluetoothTileDialogCallback: BluetoothTileDialogCallback + + @Mock private lateinit var drawable: Drawable + + @Mock private lateinit var uiEventLogger: UiEventLogger + + private lateinit var icon: Pair<Drawable, String> + private lateinit var bluetoothTileDialog: BluetoothTileDialog + private lateinit var deviceItem: DeviceItem + + @Before + fun setUp() { + bluetoothTileDialog = + BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext) + icon = Pair(drawable, DEVICE_NAME) + deviceItem = + DeviceItem( + type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = icon, + background = null + ) + `when`(cachedBluetoothDevice.isBusy).thenReturn(false) + } + + @Test + fun testShowDialog_createRecyclerViewWithAdapter() { + bluetoothTileDialog.show() + + val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list) + + assertThat(bluetoothTileDialog.isShowing).isTrue() + assertThat(recyclerView).isNotNull() + assertThat(recyclerView?.visibility).isEqualTo(VISIBLE) + assertThat(recyclerView?.adapter).isNotNull() + assertThat(recyclerView?.layoutManager is LinearLayoutManager).isTrue() + } + + @Test + fun testShowDialog_displayBluetoothDevice() { + bluetoothTileDialog = + BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext) + bluetoothTileDialog.show() + bluetoothTileDialog.onDeviceItemUpdated( + listOf(deviceItem), + showSeeAll = false, + showPairNewDevice = false + ) + + val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list) + val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter + assertThat(adapter.itemCount).isEqualTo(1) + assertThat(adapter.getItem(0).deviceName).isEqualTo(DEVICE_NAME) + assertThat(adapter.getItem(0).connectionSummary).isEqualTo(DEVICE_CONNECTION_SUMMARY) + assertThat(adapter.getItem(0).iconWithDescription).isEqualTo(icon) + } + + @Test + fun testDeviceItemViewHolder_cachedDeviceNotBusy() { + deviceItem.isEnabled = true + deviceItem.alpha = ENABLED_ALPHA + + val view = + LayoutInflater.from(mContext).inflate(R.layout.bluetooth_device_item, null, false) + val viewHolder = + BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext) + .Adapter(bluetoothTileDialogCallback) + .DeviceItemViewHolder(view) + viewHolder.bind(deviceItem, 0, bluetoothTileDialogCallback) + val container = view.requireViewById<View>(R.id.bluetooth_device) + val deviceView = view.requireViewById<View>(R.id.bluetooth_device) + + assertThat(container).isNotNull() + assertThat(container.isEnabled).isTrue() + assertThat(container.alpha).isEqualTo(ENABLED_ALPHA) + assertThat(deviceView.hasOnClickListeners()).isTrue() + } + + @Test + fun testDeviceItemViewHolder_cachedDeviceBusy() { + deviceItem.isEnabled = false + deviceItem.alpha = DISABLED_ALPHA + + val view = + LayoutInflater.from(mContext).inflate(R.layout.bluetooth_device_item, null, false) + val viewHolder = + BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext) + .Adapter(bluetoothTileDialogCallback) + .DeviceItemViewHolder(view) + viewHolder.bind(deviceItem, 0, bluetoothTileDialogCallback) + val container = view.requireViewById<View>(R.id.bluetooth_device_row) + val deviceView = view.requireViewById<View>(R.id.bluetooth_device) + + assertThat(container).isNotNull() + assertThat(container.isEnabled).isFalse() + assertThat(container.alpha).isEqualTo(DISABLED_ALPHA) + assertThat(deviceView.hasOnClickListeners()).isTrue() + } + + @Test + fun testOnDeviceUpdated_hideSeeAll_showPairNew() { + bluetoothTileDialog = + BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext) + bluetoothTileDialog.show() + bluetoothTileDialog.onDeviceItemUpdated( + listOf(deviceItem), + showSeeAll = false, + showPairNewDevice = true + ) + + val seeAllLayout = bluetoothTileDialog.requireViewById<View>(R.id.see_all_layout_group) + val pairNewLayout = + bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_layout_group) + val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list) + val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter + + assertThat(seeAllLayout).isNotNull() + assertThat(seeAllLayout.visibility).isEqualTo(GONE) + assertThat(pairNewLayout).isNotNull() + assertThat(pairNewLayout.visibility).isEqualTo(VISIBLE) + assertThat(adapter.itemCount).isEqualTo(1) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt new file mode 100644 index 000000000000..7157cce8e607 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt @@ -0,0 +1,171 @@ +/* + * 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.qs.tiles.dialog.bluetooth + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.View +import android.widget.LinearLayout +import androidx.test.filters.SmallTest +import com.android.internal.logging.UiEventLogger +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.DialogLaunchAnimator +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.nullable +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.test.TestCoroutineScheduler +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class BluetoothTileDialogViewModelTest : SysuiTestCase() { + + @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + private val fakeSystemClock = FakeSystemClock() + private val backgroundExecutor = FakeExecutor(fakeSystemClock) + + private lateinit var bluetoothTileDialogViewModel: BluetoothTileDialogViewModel + + @Mock private lateinit var bluetoothStateInteractor: BluetoothStateInteractor + + @Mock private lateinit var deviceItemInteractor: DeviceItemInteractor + + @Mock private lateinit var activityStarter: ActivityStarter + + @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator + + @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice + + @Mock private lateinit var deviceItem: DeviceItem + + @Mock private lateinit var uiEventLogger: UiEventLogger + + private lateinit var scheduler: TestCoroutineScheduler + private lateinit var dispatcher: CoroutineDispatcher + private lateinit var testScope: TestScope + + @Before + fun setUp() { + scheduler = TestCoroutineScheduler() + dispatcher = UnconfinedTestDispatcher(scheduler) + testScope = TestScope(dispatcher) + bluetoothTileDialogViewModel = + BluetoothTileDialogViewModel( + deviceItemInteractor, + bluetoothStateInteractor, + dialogLaunchAnimator, + activityStarter, + uiEventLogger, + testScope.backgroundScope, + dispatcher, + ) + `when`(deviceItemInteractor.deviceItemFlow).thenReturn(MutableStateFlow(null).asStateFlow()) + `when`(bluetoothStateInteractor.updateBluetoothStateFlow) + .thenReturn(MutableStateFlow(null).asStateFlow()) + `when`(deviceItemInteractor.updateDeviceItemsFlow) + .thenReturn(MutableStateFlow(Unit).asStateFlow()) + `when`(bluetoothStateInteractor.isBluetoothEnabled).thenReturn(true) + } + + @Test + fun testShowDialog_noAnimation() { + testScope.runTest { + bluetoothTileDialogViewModel.showDialog(context, null) + + assertThat(bluetoothTileDialogViewModel.dialog).isNotNull() + verify(dialogLaunchAnimator, never()).showFromView(any(), any(), any(), any()) + assertThat(bluetoothTileDialogViewModel.dialog?.isShowing).isTrue() + verify(uiEventLogger).log(BluetoothTileDialogUiEvent.BLUETOOTH_TILE_DIALOG_SHOWN) + } + } + + @Test + fun testShowDialog_animated() { + testScope.runTest { + bluetoothTileDialogViewModel.showDialog(mContext, LinearLayout(mContext)) + + assertThat(bluetoothTileDialogViewModel.dialog).isNotNull() + verify(dialogLaunchAnimator).showFromView(any(), any(), nullable(), anyBoolean()) + } + } + + @Test + fun testShowDialog_animated_callInBackgroundThread() { + testScope.runTest { + backgroundExecutor.execute { + bluetoothTileDialogViewModel.showDialog(mContext, LinearLayout(mContext)) + + assertThat(bluetoothTileDialogViewModel.dialog).isNotNull() + verify(dialogLaunchAnimator).showFromView(any(), any(), nullable(), anyBoolean()) + } + } + } + + @Test + fun testShowDialog_fetchDeviceItem() { + testScope.runTest { + bluetoothTileDialogViewModel.showDialog(context, null) + + assertThat(bluetoothTileDialogViewModel.dialog).isNotNull() + verify(deviceItemInteractor).deviceItemFlow + } + } + + @Test + fun testShowDialog_withBluetoothStateValue() { + testScope.runTest { + bluetoothTileDialogViewModel.showDialog(context, null) + + assertThat(bluetoothTileDialogViewModel.dialog).isNotNull() + verify(bluetoothStateInteractor).updateBluetoothStateFlow + } + } + + @Test + fun testStartSettingsActivity_activityLaunched_dialogDismissed() { + `when`(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice) + bluetoothTileDialogViewModel.showDialog(context, null) + + val clickedView = View(context) + bluetoothTileDialogViewModel.onPairNewDeviceClicked(clickedView) + + verify(uiEventLogger).log(BluetoothTileDialogUiEvent.PAIR_NEW_DEVICE_CLICKED) + verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt(), nullable()) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt new file mode 100644 index 000000000000..34519023e316 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt @@ -0,0 +1,87 @@ +/* + * 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.qs.tiles.dialog.bluetooth + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class DeviceItemFactoryTest : SysuiTestCase() { + + @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var cachedDevice: CachedBluetoothDevice + + private val availableMediaDeviceItemFactory = AvailableMediaDeviceItemFactory() + private val connectedDeviceItemFactory = ConnectedDeviceItemFactory() + private val savedDeviceItemFactory = SavedDeviceItemFactory() + + @Before + fun setup() { + `when`(cachedDevice.name).thenReturn(DEVICE_NAME) + `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY) + } + + @Test + fun testAvailableMediaDeviceItemFactory_createFromCachedDevice() { + val deviceItem = availableMediaDeviceItemFactory.create(context, cachedDevice) + + assertDeviceItem(deviceItem, DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE) + } + + @Test + fun testConnectedDeviceItemFactory_createFromCachedDevice() { + val deviceItem = connectedDeviceItemFactory.create(context, cachedDevice) + + assertDeviceItem(deviceItem, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) + } + + @Test + fun testSavedDeviceItemFactory_createFromCachedDevice() { + val deviceItem = savedDeviceItemFactory.create(context, cachedDevice) + + assertDeviceItem(deviceItem, DeviceItemType.SAVED_BLUETOOTH_DEVICE) + assertThat(deviceItem.background).isNull() + } + + private fun assertDeviceItem(deviceItem: DeviceItem?, deviceItemType: DeviceItemType) { + assertThat(deviceItem).isNotNull() + assertThat(deviceItem!!.type).isEqualTo(deviceItemType) + assertThat(deviceItem.cachedBluetoothDevice).isEqualTo(cachedDevice) + assertThat(deviceItem.deviceName).isEqualTo(DEVICE_NAME) + assertThat(deviceItem.connectionSummary).isEqualTo(CONNECTION_SUMMARY) + } + + companion object { + const val DEVICE_NAME = "DeviceName" + const val CONNECTION_SUMMARY = "ConnectionSummary" + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt new file mode 100644 index 000000000000..07a95ae330c6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt @@ -0,0 +1,221 @@ +/* + * 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.qs.tiles.dialog.bluetooth + +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.content.Context +import android.media.AudioManager +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.internal.logging.UiEventLogger +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class DeviceItemInteractorTest : SysuiTestCase() { + + @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var bluetoothTileDialogRepository: BluetoothTileDialogRepository + + @Mock private lateinit var cachedDevice1: CachedBluetoothDevice + + @Mock private lateinit var cachedDevice2: CachedBluetoothDevice + + @Mock private lateinit var device1: BluetoothDevice + + @Mock private lateinit var device2: BluetoothDevice + + @Mock private lateinit var deviceItem1: DeviceItem + + @Mock private lateinit var deviceItem2: DeviceItem + + @Mock private lateinit var audioManager: AudioManager + + @Mock private lateinit var adapter: BluetoothAdapter + + @Mock private lateinit var localBluetoothManager: LocalBluetoothManager + + @Mock private lateinit var uiEventLogger: UiEventLogger + + private lateinit var interactor: DeviceItemInteractor + + private lateinit var dispatcher: CoroutineDispatcher + + private lateinit var testScope: TestScope + + @Before + fun setUp() { + dispatcher = StandardTestDispatcher() + testScope = TestScope(dispatcher) + interactor = + DeviceItemInteractor( + bluetoothTileDialogRepository, + audioManager, + adapter, + localBluetoothManager, + uiEventLogger, + testScope.backgroundScope, + dispatcher + ) + + `when`(deviceItem1.cachedBluetoothDevice).thenReturn(cachedDevice1) + `when`(deviceItem2.cachedBluetoothDevice).thenReturn(cachedDevice2) + `when`(cachedDevice1.device).thenReturn(device1) + `when`(cachedDevice2.device).thenReturn(device2) + `when`(bluetoothTileDialogRepository.cachedDevices) + .thenReturn(listOf(cachedDevice1, cachedDevice2)) + } + + @Test + fun testUpdateDeviceItems_noCachedDevice_returnEmpty() { + testScope.runTest { + `when`(bluetoothTileDialogRepository.cachedDevices).thenReturn(emptyList()) + interactor.setDeviceItemFactoryListForTesting( + listOf(createFactory({ true }, deviceItem1)) + ) + + interactor.updateDeviceItems(mContext) + + assertThat(interactor.deviceItemFlow.value).isEmpty() + } + } + + @Test + fun testUpdateDeviceItems_hasCachedDevice_filterNotMatch_returnEmpty() { + testScope.runTest { + `when`(bluetoothTileDialogRepository.cachedDevices).thenReturn(listOf(cachedDevice1)) + interactor.setDeviceItemFactoryListForTesting( + listOf(createFactory({ false }, deviceItem1)) + ) + + interactor.updateDeviceItems(mContext) + + assertThat(interactor.deviceItemFlow.value).isEmpty() + } + } + + @Test + fun testUpdateDeviceItems_hasCachedDevice_filterMatch_returnDeviceItem() { + testScope.runTest { + `when`(bluetoothTileDialogRepository.cachedDevices).thenReturn(listOf(cachedDevice1)) + interactor.setDeviceItemFactoryListForTesting( + listOf(createFactory({ true }, deviceItem1)) + ) + + interactor.updateDeviceItems(mContext) + + assertThat(interactor.deviceItemFlow.value).hasSize(1) + assertThat(interactor.deviceItemFlow.value!![0]).isEqualTo(deviceItem1) + } + } + + @Test + fun testUpdateDeviceItems_hasCachedDevice_filterMatch_returnMultipleDeviceItem() { + testScope.runTest { + `when`(adapter.mostRecentlyConnectedDevices).thenReturn(null) + interactor.setDeviceItemFactoryListForTesting( + listOf(createFactory({ false }, deviceItem1), createFactory({ true }, deviceItem2)) + ) + + interactor.updateDeviceItems(mContext) + + assertThat(interactor.deviceItemFlow.value).hasSize(2) + assertThat(interactor.deviceItemFlow.value!![0]).isEqualTo(deviceItem2) + assertThat(interactor.deviceItemFlow.value!![1]).isEqualTo(deviceItem2) + } + } + + @Test + fun testUpdateDeviceItems_sortByDisplayPriority() { + testScope.runTest { + `when`(adapter.mostRecentlyConnectedDevices).thenReturn(null) + interactor.setDeviceItemFactoryListForTesting( + listOf( + createFactory({ cachedDevice -> cachedDevice.device == device1 }, deviceItem1), + createFactory({ cachedDevice -> cachedDevice.device == device2 }, deviceItem2) + ) + ) + interactor.setDisplayPriorityForTesting( + listOf( + DeviceItemType.SAVED_BLUETOOTH_DEVICE, + DeviceItemType.CONNECTED_BLUETOOTH_DEVICE + ) + ) + `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) + `when`(deviceItem2.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE) + + interactor.updateDeviceItems(mContext) + + assertThat(interactor.deviceItemFlow.value).isEqualTo(listOf(deviceItem2, deviceItem1)) + } + } + + @Test + fun testUpdateDeviceItems_sameType_sortByRecentlyConnected() { + testScope.runTest { + `when`(adapter.mostRecentlyConnectedDevices).thenReturn(listOf(device2, device1)) + interactor.setDeviceItemFactoryListForTesting( + listOf( + createFactory({ cachedDevice -> cachedDevice.device == device1 }, deviceItem1), + createFactory({ cachedDevice -> cachedDevice.device == device2 }, deviceItem2) + ) + ) + interactor.setDisplayPriorityForTesting( + listOf(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) + ) + `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) + `when`(deviceItem2.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) + + interactor.updateDeviceItems(mContext) + + assertThat(interactor.deviceItemFlow.value).isEqualTo(listOf(deviceItem2, deviceItem1)) + } + } + + private fun createFactory( + isFilterMatchFunc: (CachedBluetoothDevice) -> Boolean, + deviceItem: DeviceItem + ): DeviceItemFactory { + return object : DeviceItemFactory() { + override fun isFilterMatched( + cachedDevice: CachedBluetoothDevice, + audioManager: AudioManager? + ) = isFilterMatchFunc(cachedDevice) + + override fun create(context: Context, cachedDevice: CachedBluetoothDevice) = deviceItem + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 6b918c6ba15b..85bd92bf3b12 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -76,6 +76,7 @@ import org.junit.runners.JUnit4 * being used when the state is as required (e.g. cannot unlock an already unlocked device, cannot * put to sleep a device that's already asleep, etc.). */ +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class SceneFrameworkIntegrationTest : SysuiTestCase() { @@ -481,7 +482,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { bouncerSceneJob = if (to.key == SceneKey.Bouncer) { testScope.backgroundScope.launch { - bouncerViewModel.authMethod.collect { + bouncerViewModel.authMethodViewModel.collect { // Do nothing. Need this to turn this otherwise cold flow, hot. } } @@ -556,7 +557,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { assertWithMessage("Cannot enter PIN when not on the Bouncer scene!") .that(getCurrentSceneInUi()) .isEqualTo(SceneKey.Bouncer) - val authMethodViewModel by collectLastValue(bouncerViewModel.authMethod) + val authMethodViewModel by collectLastValue(bouncerViewModel.authMethodViewModel) assertWithMessage("Cannot enter PIN when not using a PIN authentication method!") .that(authMethodViewModel) .isInstanceOf(PinBouncerViewModel::class.java) @@ -613,11 +614,12 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { private fun TestScope.dismissIme( showImeBeforeDismissing: Boolean = true, ) { - if (showImeBeforeDismissing) { - bouncerViewModel.authMethod.value?.onImeVisibilityChanged(true) + bouncerViewModel.authMethodViewModel.value?.apply { + if (showImeBeforeDismissing) { + onImeVisibilityChanged(true) + } + onImeVisibilityChanged(false) + runCurrent() } - - bouncerViewModel.authMethod.value?.onImeVisibilityChanged(false) - runCurrent() } } 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 223b1c431b2d..96510721baa5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade +import android.os.Handler import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.KeyEvent @@ -24,29 +25,42 @@ import android.view.ViewGroup import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardMessageAreaController import com.android.keyguard.KeyguardSecurityContainerController +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.LockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.back.domain.interactor.BackActionInteractor -import com.android.systemui.bouncer.data.factory.BouncerMessageFactory -import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository +import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository +import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.flags.SystemPropertiesHelper import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor +import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.KeyguardUnlockAnimationController +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel import com.android.systemui.log.BouncerLogger import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.res.R import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.statusbar.DragDownHelper import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -62,6 +76,7 @@ import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.phone.PhoneStatusBarViewController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.user.data.repository.FakeUserRepository @@ -201,11 +216,35 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { featureFlags, fakeClock, BouncerMessageInteractor( - FakeBouncerMessageRepository(), - mock(BouncerMessageFactory::class.java), - FakeUserRepository(), - CountDownTimerUtil(), - featureFlags + repository = BouncerMessageRepositoryImpl(), + userRepository = FakeUserRepository(), + countDownTimerUtil = mock(CountDownTimerUtil::class.java), + featureFlags = featureFlags, + updateMonitor = mock(KeyguardUpdateMonitor::class.java), + biometricSettingsRepository = FakeBiometricSettingsRepository(), + applicationScope = testScope.backgroundScope, + trustRepository = FakeTrustRepository(), + systemPropertiesHelper = mock(SystemPropertiesHelper::class.java), + primaryBouncerInteractor = + PrimaryBouncerInteractor( + FakeKeyguardBouncerRepository(), + mock(BouncerView::class.java), + mock(Handler::class.java), + mock(KeyguardStateController::class.java), + mock(KeyguardSecurityModel::class.java), + mock(PrimaryBouncerCallbackInteractor::class.java), + mock(FalsingCollector::class.java), + mock(DismissCallbackRegistry::class.java), + context, + mock(KeyguardUpdateMonitor::class.java), + FakeTrustRepository(), + testScope.backgroundScope, + ), + facePropertyRepository = FakeFacePropertyRepository(), + deviceEntryFingerprintAuthRepository = + FakeDeviceEntryFingerprintAuthRepository(), + faceAuthRepository = FakeDeviceEntryFaceAuthRepository(), + securityModel = mock(KeyguardSecurityModel::class.java), ), BouncerLogger(logcatLogBuffer("BouncerLog")), keyEventInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index e8170163d616..00230202d941 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.shade +import android.os.Handler import android.os.SystemClock import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper @@ -23,28 +24,41 @@ import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardMessageAreaController import com.android.keyguard.KeyguardSecurityContainerController +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.LockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.back.domain.interactor.BackActionInteractor -import com.android.systemui.bouncer.data.factory.BouncerMessageFactory -import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository +import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository +import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.flags.SystemPropertiesHelper import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor +import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.KeyguardUnlockAnimationController +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel import com.android.systemui.log.BouncerLogger import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.res.R import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.statusbar.DragDownHelper import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -60,6 +74,7 @@ import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.user.data.repository.FakeUserRepository @@ -203,11 +218,35 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { featureFlags, FakeSystemClock(), BouncerMessageInteractor( - FakeBouncerMessageRepository(), - Mockito.mock(BouncerMessageFactory::class.java), - FakeUserRepository(), - CountDownTimerUtil(), - featureFlags + repository = BouncerMessageRepositoryImpl(), + userRepository = FakeUserRepository(), + countDownTimerUtil = Mockito.mock(CountDownTimerUtil::class.java), + featureFlags = featureFlags, + updateMonitor = Mockito.mock(KeyguardUpdateMonitor::class.java), + biometricSettingsRepository = FakeBiometricSettingsRepository(), + applicationScope = testScope.backgroundScope, + trustRepository = FakeTrustRepository(), + systemPropertiesHelper = Mockito.mock(SystemPropertiesHelper::class.java), + primaryBouncerInteractor = + PrimaryBouncerInteractor( + FakeKeyguardBouncerRepository(), + Mockito.mock(BouncerView::class.java), + Mockito.mock(Handler::class.java), + Mockito.mock(KeyguardStateController::class.java), + Mockito.mock(KeyguardSecurityModel::class.java), + Mockito.mock(PrimaryBouncerCallbackInteractor::class.java), + Mockito.mock(FalsingCollector::class.java), + Mockito.mock(DismissCallbackRegistry::class.java), + context, + Mockito.mock(KeyguardUpdateMonitor::class.java), + FakeTrustRepository(), + testScope.backgroundScope, + ), + facePropertyRepository = FakeFacePropertyRepository(), + deviceEntryFingerprintAuthRepository = + FakeDeviceEntryFingerprintAuthRepository(), + faceAuthRepository = FakeDeviceEntryFaceAuthRepository(), + securityModel = Mockito.mock(KeyguardSecurityModel::class.java), ), BouncerLogger(logcatLogBuffer("BouncerLog")), Mockito.mock(KeyEventInteractor::class.java), diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt index d018cbbfbc24..2be1c09843c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt @@ -25,7 +25,6 @@ import android.os.UserManager import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue @@ -35,6 +34,7 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.res.R import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags import com.android.systemui.scene.shared.model.ObservableTransitionState @@ -54,6 +54,7 @@ import com.android.systemui.user.domain.interactor.GuestUserInteractor import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode import com.android.systemui.user.domain.interactor.RefreshUsersScheduler import com.android.systemui.user.domain.interactor.UserInteractor +import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -153,6 +154,7 @@ class ShadeInteractorTest : SysuiTestCase() { refreshUsersScheduler = refreshUsersScheduler, guestUserInteractor = guestInteractor, uiEventLogger = uiEventLogger, + userRestrictionChecker = mock(), ) underTest = ShadeInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt index ac2aec6e0c86..0a10b2c85ebe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Test import org.mockito.Mockito.never @@ -126,6 +127,22 @@ class GroupExpansionManagerTest : SysuiTestCase() { } @Test + fun testExpandUnattachedEntry() { + featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true) + + // First, expand the entry when it is attached. + gem.setGroupExpanded(summary1, true) + assertThat(gem.isGroupExpanded(summary1)).isTrue() + + // Un-attach it, and un-expand it. + NotificationEntryBuilder.setNewParent(summary1, null) + gem.setGroupExpanded(summary1, false) + + // Expanding again should throw. + assertThrows(IllegalArgumentException::class.java) { gem.setGroupExpanded(summary1, true) } + } + + @Test fun testSyncWithPipeline() { featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true) gem.attach(pipeline) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt index 37ec0e09f841..c1ffa641c6a4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt @@ -58,6 +58,35 @@ class GroupMembershipManagerTest : SysuiTestCase() { val noParentEntry = NotificationEntryBuilder().setParent(null).build() assertThat(gmm.isChildInGroup(noParentEntry)).isFalse() } + @Test + fun testIsChildInGroup_summary_old() { + featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, false) + + val groupKey = "group" + val summary = + NotificationEntryBuilder() + .setGroup(mContext, groupKey) + .setGroupSummary(mContext, true) + .build() + GroupEntryBuilder().setKey(groupKey).setSummary(summary).build() + + assertThat(gmm.isChildInGroup(summary)).isTrue() + } + + @Test + fun testIsChildInGroup_summary_new() { + featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true) + + val groupKey = "group" + val summary = + NotificationEntryBuilder() + .setGroup(mContext, groupKey) + .setGroupSummary(mContext, true) + .build() + GroupEntryBuilder().setKey(groupKey).setSummary(summary).build() + + assertThat(gmm.isChildInGroup(summary)).isFalse() + } @Test fun testIsChildInGroup_child() { @@ -67,40 +96,78 @@ class GroupMembershipManagerTest : SysuiTestCase() { } @Test - fun testIsGroupSummary() { + fun testIsGroupSummary_topLevelEntry() { featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true) - val entry = NotificationEntryBuilder().setGroupSummary(mContext, true).build() - assertThat(gmm.isGroupSummary(entry)).isTrue() + val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build() + assertThat(gmm.isGroupSummary(entry)).isFalse() } @Test - fun testGetGroupSummary() { + fun testIsGroupSummary_summary() { featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true) + val groupKey = "group" val summary = NotificationEntryBuilder() - .setGroup(mContext, "group") + .setGroup(mContext, groupKey) .setGroupSummary(mContext, true) .build() - val groupEntry = - GroupEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).setSummary(summary).build() - val entry = - NotificationEntryBuilder().setGroup(mContext, "group").setParent(groupEntry).build() + GroupEntryBuilder().setKey(groupKey).setSummary(summary).build() - assertThat(gmm.getGroupSummary(entry)).isEqualTo(summary) + assertThat(gmm.isGroupSummary(summary)).isTrue() } @Test - fun testGetGroupSummary_isSummary_old() { - featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, false) - val entry = NotificationEntryBuilder().setGroupSummary(mContext, true).build() + fun testIsGroupSummary_child() { + featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true) + + val groupKey = "group" + val summary = + NotificationEntryBuilder() + .setGroup(mContext, groupKey) + .setGroupSummary(mContext, true) + .build() + val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build() + GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build() + + assertThat(gmm.isGroupSummary(entry)).isFalse() + } + + @Test + fun testGetGroupSummary_topLevelEntry() { + featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true) + val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build() assertThat(gmm.getGroupSummary(entry)).isNull() } @Test - fun testGetGroupSummary_isSummary_new() { + fun testGetGroupSummary_summary() { + featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true) + + val groupKey = "group" + val summary = + NotificationEntryBuilder() + .setGroup(mContext, groupKey) + .setGroupSummary(mContext, true) + .build() + GroupEntryBuilder().setKey(groupKey).setSummary(summary).build() + + assertThat(gmm.getGroupSummary(summary)).isEqualTo(summary) + } + + @Test + fun testGetGroupSummary_child() { featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true) - val entry = NotificationEntryBuilder().setGroupSummary(mContext, true).build() - assertThat(gmm.getGroupSummary(entry)).isEqualTo(entry) + + val groupKey = "group" + val summary = + NotificationEntryBuilder() + .setGroup(mContext, groupKey) + .setGroupSummary(mContext, true) + .build() + val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build() + GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build() + + assertThat(gmm.getGroupSummary(entry)).isEqualTo(summary) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt index b8792a8aeacd..126e0e82b210 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt @@ -18,26 +18,19 @@ package com.android.systemui.statusbar.notification.icon.ui.viewbinder import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.TestMocksModule import com.android.systemui.SysuiTestCase -import com.android.systemui.demomode.DemoModeController -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.plugins.DarkIconDispatcher -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.statusbar.NotificationListener -import com.android.systemui.statusbar.NotificationMediaManager -import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator -import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider -import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel -import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel -import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel +import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FakeFeatureFlagsClassicModule +import com.android.systemui.flags.Flags import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.NotificationIconContainer -import com.android.systemui.statusbar.phone.ScreenOffAnimationController -import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.user.domain.UserDomainLayerModule import com.android.systemui.util.mockito.whenever -import com.android.wm.shell.bubbles.Bubbles -import java.util.Optional +import dagger.BindsInstance +import dagger.Component import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before @@ -51,50 +44,32 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) class NotificationIconAreaControllerViewBinderWrapperImplTest : SysuiTestCase() { - @Mock private lateinit var notifListener: NotificationListener - @Mock private lateinit var statusBarStateController: StatusBarStateController - @Mock private lateinit var wakeUpCoordinator: NotificationWakeUpCoordinator - @Mock private lateinit var keyguardBypassController: KeyguardBypassController - @Mock private lateinit var notifMediaManager: NotificationMediaManager + @Mock private lateinit var dozeParams: DozeParameters - @Mock private lateinit var sectionStyleProvider: SectionStyleProvider - @Mock private lateinit var darkIconDispatcher: DarkIconDispatcher - @Mock private lateinit var statusBarWindowController: StatusBarWindowController - @Mock private lateinit var screenOffAnimController: ScreenOffAnimationController - @Mock private lateinit var bubbles: Bubbles - @Mock private lateinit var demoModeController: DemoModeController @Mock private lateinit var aodIcons: NotificationIconContainer - @Mock private lateinit var featureFlags: FeatureFlags - - private val shelfViewModel = NotificationIconContainerShelfViewModel() - private val statusBarViewModel = NotificationIconContainerStatusBarViewModel() - private val aodViewModel = NotificationIconContainerAlwaysOnDisplayViewModel() - private lateinit var underTest: NotificationIconAreaControllerViewBinderWrapperImpl + private lateinit var testComponent: TestComponent + private val underTest + get() = testComponent.underTest @Before fun setup() { MockitoAnnotations.initMocks(this) - underTest = - NotificationIconAreaControllerViewBinderWrapperImpl( - mContext, - statusBarStateController, - wakeUpCoordinator, - keyguardBypassController, - notifMediaManager, - notifListener, - dozeParams, - sectionStyleProvider, - Optional.of(bubbles), - demoModeController, - darkIconDispatcher, - featureFlags, - statusBarWindowController, - screenOffAnimController, - shelfViewModel, - statusBarViewModel, - aodViewModel, - ) + allowTestableLooperAsMainThread() + + testComponent = + DaggerNotificationIconAreaControllerViewBinderWrapperImplTest_TestComponent.factory() + .create( + test = this, + featureFlags = + FakeFeatureFlagsClassicModule { + set(Flags.FACE_AUTH_REFACTOR, value = false) + }, + mocks = + TestMocksModule( + dozeParameters = dozeParams, + ), + ) } @Test @@ -117,4 +92,27 @@ class NotificationIconAreaControllerViewBinderWrapperImplTest : SysuiTestCase() verify(aodIcons).translationY = 0f verify(aodIcons).alpha = 1.0f } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + BiometricsDomainLayerModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val underTest: NotificationIconAreaControllerViewBinderWrapperImpl + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + mocks: TestMocksModule, + featureFlags: FakeFeatureFlagsClassicModule, + ): TestComponent + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java index f05436f66d82..50ce265b67d1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java @@ -71,6 +71,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent; import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -117,6 +118,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { PendingIntent mPendingIntent; @Mock UserTracker mUserTracker; + @Mock + DeviceProvisionedController mDeviceProvisionedController; private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider; @@ -141,7 +144,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { mFlags, mKeyguardNotificationVisibilityProvider, mUiEventLoggerFake, - mUserTracker); + mUserTracker, + mDeviceProvisionedController); mNotifInterruptionStateProvider.mUseHeadsUp = true; } @@ -694,6 +698,25 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } @Test + public void testShouldFullscreen_suppressedInterruptionsWhenNotProvisioned() { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + when(mStatusBarStateController.isDreaming()).thenReturn(false); + when(mPowerManager.isScreenOn()).thenReturn(true); + when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(false); + mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions); + + assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) + .isEqualTo(FullScreenIntentDecision.FSI_NOT_PROVISIONED); + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isTrue(); + verify(mLogger, never()).logNoFullscreen(any(), any()); + verify(mLogger, never()).logNoFullscreenWarning(any(), any()); + verify(mLogger).logFullscreen(entry, "FSI_NOT_PROVISIONED"); + } + + @Test public void testShouldNotFullScreen_willHun() throws RemoteException { NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); when(mPowerManager.isInteractive()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt index 64d025628754..7dcbd8084594 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt @@ -73,14 +73,14 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() { controller = ChannelEditorDialogController(mContext, mockNoMan, dialogBuilder) channel1 = NotificationChannel(TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT) - channel2 = NotificationChannel(TEST_CHANNEL2, TEST_CHANNEL_NAME2, IMPORTANCE_DEFAULT) + channel2 = NotificationChannel(TEST_CHANNEL2, TEST_CHANNEL_NAME2, IMPORTANCE_NONE) channelDefault = NotificationChannel( NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT) group = NotificationChannelGroup(TEST_GROUP_ID, TEST_GROUP_NAME) - `when`(mockNoMan.getNotificationChannelGroupsForPackage( - eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())) + `when`(mockNoMan.getRecentBlockedNotificationChannelGroupsForPackage( + eq(TEST_PACKAGE_NAME), eq(TEST_UID))) .thenReturn(ParceledListSlice(listOf(group))) `when`(mockNoMan.areNotificationsEnabledForPackage(eq(TEST_PACKAGE_NAME), eq(TEST_UID))) @@ -89,11 +89,13 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() { @Test fun testPrepareDialogForApp_noExtraChannels() { + channel1.group = group.id + channel2.group = group.id group.channels = listOf(channel1, channel2) controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, - setOf(channel1, channel2), appIcon, clickListener) + channel1, appIcon, clickListener) - assertEquals(2, controller.paddedChannels.size) + assertEquals(2, controller.channelList.size) } @Test @@ -101,39 +103,41 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() { group.addChannel(channelDefault) controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, - setOf(channelDefault), appIcon, clickListener) + channelDefault, appIcon, clickListener) assertEquals("No channels should be shown when there is only the miscellaneous channel", - 0, controller.paddedChannels.size) + 0, controller.channelList.size) } @Test - fun testPrepareDialogForApp_noProvidedChannels_noException() { - group.channels = listOf() - - controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, - setOf(), appIcon, clickListener) - } - - @Test - fun testPrepareDialogForApp_retrievesUpTo4Channels() { + fun testPrepareDialogForApp_AddsAllChannelsAllGroups() { + val group2 = NotificationChannelGroup("two", "group two") val channel3 = NotificationChannel("test_channel_3", "Test channel 3", IMPORTANCE_DEFAULT) + channel3.group = group2.id val channel4 = NotificationChannel("test_channel_4", "Test channel 4", IMPORTANCE_DEFAULT) + channel4.group = group.id + val channel5 = NotificationChannel("test_channel_5", "Test channel 5", IMPORTANCE_DEFAULT) + channel5.group = group.id - group.channels = listOf(channel1, channel2, channel3, channel4) + `when`(mockNoMan.getRecentBlockedNotificationChannelGroupsForPackage( + eq(TEST_PACKAGE_NAME), eq(TEST_UID))) + .thenReturn(ParceledListSlice(listOf(group, group2))) + + group.channels = listOf(channel1, channel2, channel4, channel5) + group2.channels = listOf(channel3) controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, - setOf(channel1), appIcon, clickListener) + channel1, appIcon, clickListener) - assertEquals("ChannelEditorDialog should fetch enough channels to show 4", - 4, controller.paddedChannels.size) + assertEquals("ChannelEditorDialog should show all channels", + 5, controller.channelList.size) } @Test fun testApply_demoteChannel() { group.channels = listOf(channel1, channel2) controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, - setOf(channel1, channel2), appIcon, clickListener) + channel1, appIcon, clickListener) // propose an adjustment of channel1 controller.proposeEditForChannel(channel1, IMPORTANCE_NONE) @@ -145,14 +149,33 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() { // Channel 2 shouldn't have changed assertEquals("Proposed edits should take effect after apply", + IMPORTANCE_NONE, channel2.importance) + } + + @Test + fun testApply_promoteChannel() { + group.channels = listOf(channel1, channel2) + controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, + channel1, appIcon, clickListener) + + // propose an adjustment of channel1 + controller.proposeEditForChannel(channel2, IMPORTANCE_DEFAULT) + + controller.apply() + + assertEquals("Proposed edits should take effect after apply", IMPORTANCE_DEFAULT, channel2.importance) + + // Channel 1 shouldn't have changed + assertEquals("Proposed edits should take effect after apply", + IMPORTANCE_DEFAULT, channel1.importance) } @Test fun testApply_demoteApp() { group.channels = listOf(channel1, channel2) controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, - setOf(channel1, channel2), appIcon, clickListener) + channel1, appIcon, clickListener) controller.proposeSetAppNotificationsEnabled(false) controller.apply() @@ -168,7 +191,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() { .thenReturn(false) controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, - setOf(channel1, channel2), appIcon, clickListener) + channel1, appIcon, clickListener) controller.proposeSetAppNotificationsEnabled(true) controller.apply() @@ -181,7 +204,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() { // GIVEN editor dialog group.channels = listOf(channel1, channel2) controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, - setOf(channel1, channel2), appIcon, null) + channel1, appIcon, null) // WHEN user taps settings // Pass in any old view, it should never actually be used @@ -197,7 +220,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() { group.channels = listOf(channel1, channel2) controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, - setOf(channel1, channel2), appIcon, null) + channel1, appIcon, null) // WHEN the user proposes a change controller.proposeEditForChannel(channel1, IMPORTANCE_NONE) @@ -214,7 +237,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() { group.channels = listOf(channel1, channel2) controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID, - setOf(channel1, channel2), appIcon, null) + channel1, appIcon, null) // WHEN the user proposes a change controller.proposeEditForChannel(channel1, IMPORTANCE_NONE) @@ -236,7 +259,6 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() { const val TEST_PACKAGE_NAME = "test_package" const val TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME const val TEST_UID = 1 - const val MULTIPLE_CHANNEL_COUNT = 2 const val TEST_CHANNEL = "test_channel" const val TEST_CHANNEL_NAME = "Test Channel Name" const val TEST_CHANNEL2 = "test_channel2" diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index ac8b42afd4b2..e37314301e28 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -481,33 +481,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { } @Test - public void testGetNumUniqueChildren_defaultChannel() throws Exception { - ExpandableNotificationRow groupRow = mNotificationTestHelper.createGroup(); - - assertEquals(1, groupRow.getNumUniqueChannels()); - } - - @Test - public void testGetNumUniqueChildren_multiChannel() throws Exception { - ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); - - List<ExpandableNotificationRow> childRows = - group.getChildrenContainer().getAttachedChildren(); - // Give each child a unique channel id/name. - int i = 0; - for (ExpandableNotificationRow childRow : childRows) { - modifyRanking(childRow.getEntry()) - .setChannel( - new NotificationChannel( - "id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT)) - .build(); - i++; - } - - assertEquals(3, group.getNumUniqueChannels()); - } - - @Test public void testIconScrollXAfterTranslationAndReset() throws Exception { ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 9e0f83c9fc53..0f1e63f4c0df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -462,7 +462,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(mChannelEditorDialogController), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), - anySet(), eq(entry), any(NotificationInfo.OnSettingsClickListener.class), any(NotificationInfo.OnAppSettingsClickListener.class), @@ -496,7 +495,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(mChannelEditorDialogController), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), - anySet(), eq(entry), any(NotificationInfo.OnSettingsClickListener.class), any(NotificationInfo.OnAppSettingsClickListener.class), @@ -528,7 +526,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(mChannelEditorDialogController), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), - anySet(), eq(entry), any(NotificationInfo.OnSettingsClickListener.class), any(NotificationInfo.OnAppSettingsClickListener.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index f0b4dd46654a..b59385cd5261 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -92,7 +92,6 @@ public class NotificationInfoTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test_package"; private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME; private static final int TEST_UID = 1; - private static final int MULTIPLE_CHANNEL_COUNT = 2; private static final String TEST_CHANNEL = "test_channel"; private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME"; @@ -100,8 +99,6 @@ public class NotificationInfoTest extends SysuiTestCase { private NotificationInfo mNotificationInfo; private NotificationChannel mNotificationChannel; private NotificationChannel mDefaultNotificationChannel; - private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>(); - private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>(); private StatusBarNotification mSbn; private NotificationEntry mEntry; private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake(); @@ -162,11 +159,9 @@ public class NotificationInfoTest extends SysuiTestCase { // Some test channels. mNotificationChannel = new NotificationChannel( TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW); - mNotificationChannelSet.add(mNotificationChannel); mDefaultNotificationChannel = new NotificationChannel( NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_LOW); - mDefaultNotificationChannelSet.add(mDefaultNotificationChannel); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, new Notification(), UserHandle.getUserHandleForUid(TEST_UID), null, 0); mEntry = new NotificationEntryBuilder().setSbn(mSbn).build(); @@ -185,7 +180,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -212,7 +206,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -235,7 +228,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -267,7 +259,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, entry, null, null, @@ -291,7 +282,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -320,7 +310,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -344,7 +333,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -367,7 +355,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mDefaultNotificationChannel, - mDefaultNotificationChannelSet, mEntry, null, null, @@ -394,7 +381,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mDefaultNotificationChannel, - mDefaultNotificationChannelSet, mEntry, null, null, @@ -417,7 +403,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -441,7 +426,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); @@ -470,7 +454,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -494,7 +477,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); @@ -519,7 +501,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -536,7 +517,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, (View v, NotificationChannel c, int appUid) -> { }, null, @@ -551,86 +531,6 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test - public void testOnClickListenerPassesNullChannelForBundle() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mOnUserInteractionCallback, - mChannelEditorDialogController, - TEST_PACKAGE_NAME, mNotificationChannel, - createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), - mEntry, - (View v, NotificationChannel c, int appUid) -> { - assertEquals(null, c); - latch.countDown(); - }, - null, - mUiEventLogger, - true, - true, - true, - mAssistantFeedbackController, - mMetricsLogger); - - mNotificationInfo.findViewById(R.id.info).performClick(); - // Verify that listener was triggered. - assertEquals(0, latch.getCount()); - } - - @Test - @UiThreadTest - public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels() - throws Exception { - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mOnUserInteractionCallback, - mChannelEditorDialogController, - TEST_PACKAGE_NAME, - mNotificationChannel, - createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), - mEntry, - null, - null, - mUiEventLogger, - true, - false, - true, - mAssistantFeedbackController, - mMetricsLogger); - final TextView channelNameView = - mNotificationInfo.findViewById(R.id.channel_name); - assertEquals(GONE, channelNameView.getVisibility()); - } - - @Test - @UiThreadTest - public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception { - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mOnUserInteractionCallback, - mChannelEditorDialogController, - TEST_PACKAGE_NAME, - mNotificationChannel, - createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), - mEntry, - null, - null, - mUiEventLogger, - true, - false, - true, - mAssistantFeedbackController, - mMetricsLogger); - assertEquals(GONE, mNotificationInfo.findViewById( - R.id.interruptiveness_settings).getVisibility()); - assertEquals(VISIBLE, mNotificationInfo.findViewById( - R.id.non_configurable_multichannel_text).getVisibility()); - } - - @Test public void testBindNotification_whenAppUnblockable() throws Exception { mNotificationInfo.bindNotification( mMockPackageManager, @@ -639,7 +539,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -683,7 +582,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -727,7 +625,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -755,7 +652,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -778,7 +674,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -803,7 +698,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -825,7 +719,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -847,7 +740,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -869,7 +761,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -893,7 +784,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -918,7 +808,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -946,7 +835,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -974,7 +862,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1003,7 +890,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1031,7 +917,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1067,7 +952,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1096,7 +980,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1138,7 +1021,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1176,7 +1058,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1209,7 +1090,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1246,7 +1126,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1285,7 +1164,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1317,7 +1195,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1356,7 +1233,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1386,7 +1262,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1418,7 +1293,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1454,7 +1328,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1488,7 +1361,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1522,7 +1394,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1549,7 +1420,6 @@ public class NotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, null, @@ -1562,21 +1432,4 @@ public class NotificationInfoTest extends SysuiTestCase { assertFalse(mNotificationInfo.willBeRemoved()); } - - private Set<NotificationChannel> createMultipleChannelSet(int howMany) { - Set<NotificationChannel> multiChannelSet = new HashSet<>(); - for (int i = 0; i < howMany; i++) { - if (i == 0) { - multiChannelSet.add(mNotificationChannel); - continue; - } - - NotificationChannel channel = new NotificationChannel( - TEST_CHANNEL, TEST_CHANNEL_NAME + i, IMPORTANCE_LOW); - - multiChannelSet.add(channel); - } - - return multiChannelSet; - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java index e42ce2789e0b..ccedd364ef67 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java @@ -83,8 +83,6 @@ public class PartialConversationInfoTest extends SysuiTestCase { private PartialConversationInfo mInfo; private NotificationChannel mNotificationChannel; private NotificationChannel mDefaultNotificationChannel; - private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>(); - private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>(); private StatusBarNotification mSbn; private NotificationEntry mEntry; @@ -144,11 +142,9 @@ public class PartialConversationInfoTest extends SysuiTestCase { // Some test channels. mNotificationChannel = new NotificationChannel( TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW); - mNotificationChannelSet.add(mNotificationChannel); mDefaultNotificationChannel = new NotificationChannel( NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_LOW); - mDefaultNotificationChannelSet.add(mDefaultNotificationChannel); Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) .setContentTitle(new SpannableString("title")) .build(); @@ -166,7 +162,6 @@ public class PartialConversationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, true, @@ -185,7 +180,6 @@ public class PartialConversationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, true, @@ -202,7 +196,6 @@ public class PartialConversationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, true, @@ -228,7 +221,6 @@ public class PartialConversationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, entry, null, true, @@ -247,7 +239,6 @@ public class PartialConversationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); @@ -271,7 +262,6 @@ public class PartialConversationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); @@ -294,7 +284,6 @@ public class PartialConversationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, true, @@ -311,7 +300,6 @@ public class PartialConversationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); @@ -330,7 +318,6 @@ public class PartialConversationInfoTest extends SysuiTestCase { mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, - mNotificationChannelSet, mEntry, null, 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 5c3dde59596e..0b171b2a23e4 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 @@ -364,7 +364,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mock(NotifPipelineFlags.class), mock(KeyguardNotificationVisibilityProvider.class), mock(UiEventLogger.class), - mUserTracker); + mUserTracker, + mDeviceProvisionedController); mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class)); mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class)); @@ -1169,7 +1170,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { NotifPipelineFlags flags, KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider, UiEventLogger uiEventLogger, - UserTracker userTracker) { + UserTracker userTracker, + DeviceProvisionedController deviceProvisionedController) { super( contentResolver, powerManager, @@ -1183,7 +1185,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { flags, keyguardNotificationVisibilityProvider, uiEventLogger, - userTracker + userTracker, + deviceProvisionedController ); mUseHeadsUp = true; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java index c8f28bc17926..4ccbd1b739f3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java @@ -65,7 +65,7 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock); - private final RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy(); + private final FakeRotationPolicy mFakeRotationPolicy = new FakeRotationPolicy(); private DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController; private DeviceStateManager.DeviceStateCallback mDeviceStateCallback; private DeviceStateRotationLockSettingsManager mSettingsManager; @@ -324,13 +324,21 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase private boolean mRotationLock; - @Override public void setRotationLock(boolean enabled) { - mRotationLock = enabled; + setRotationLock(enabled, /* caller= */ "FakeRotationPolicy"); } @Override + public void setRotationLock(boolean enabled, String caller) { + mRotationLock = enabled; + } + public void setRotationLockAtAngle(boolean enabled, int rotation) { + setRotationLockAtAngle(enabled, rotation, /* caller= */ "FakeRotationPolicy"); + } + + @Override + public void setRotationLockAtAngle(boolean enabled, int rotation, String caller) { mRotationLock = enabled; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt index bbc49c859821..af941d03f191 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt @@ -34,7 +34,6 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.GuestResetOrExitSessionReceiver import com.android.systemui.GuestResumeSessionReceiver -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Text @@ -45,6 +44,7 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.plugins.ActivityStarter import com.android.systemui.qs.user.UserSwitchDialogController +import com.android.systemui.res.R import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.telephony.data.repository.FakeTelephonyRepository import com.android.systemui.telephony.domain.interactor.TelephonyInteractor @@ -1120,6 +1120,7 @@ class UserInteractorTest : SysuiTestCase() { ), uiEventLogger = uiEventLogger, featureFlags = featureFlags, + userRestrictionChecker = mock(), ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt index 2433e123a309..a8db368d4150 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt @@ -267,6 +267,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { refreshUsersScheduler = refreshUsersScheduler, guestUserInteractor = guestUserInteractor, uiEventLogger = uiEventLogger, + userRestrictionChecker = mock(), ) ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt index 8c88f95d73a5..6932f5ed4b30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt @@ -45,6 +45,7 @@ import com.android.systemui.user.domain.interactor.UserInteractor import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper import com.android.systemui.user.shared.model.UserActionModel import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -176,6 +177,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { refreshUsersScheduler = refreshUsersScheduler, guestUserInteractor = guestUserInteractor, uiEventLogger = uiEventLogger, + userRestrictionChecker = mock(), ), guestUserInteractor = guestUserInteractor, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt index 94ed608f4844..e59e4759fc7b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt @@ -15,104 +15,119 @@ */ package com.android.systemui.wmshell -import android.content.ContentResolver import android.content.Context +import android.content.Intent import android.content.SharedPreferences +import android.content.pm.ShortcutInfo +import android.content.res.Resources +import android.os.UserHandle import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.core.content.edit import androidx.test.filters.SmallTest import com.android.systemui.model.SysUiStateTest import com.android.wm.shell.bubbles.Bubble import com.android.wm.shell.bubbles.BubbleEducationController import com.android.wm.shell.bubbles.PREF_MANAGED_EDUCATION import com.android.wm.shell.bubbles.PREF_STACK_EDUCATION +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors.directExecutor import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.anyBoolean -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.anyString -import org.mockito.Mockito @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class BubbleEducationControllerTest : SysUiStateTest() { - private val sharedPrefsEditor = Mockito.mock(SharedPreferences.Editor::class.java) - private val sharedPrefs = Mockito.mock(SharedPreferences::class.java) - private val context = Mockito.mock(Context::class.java) + + private lateinit var sharedPrefs: SharedPreferences private lateinit var sut: BubbleEducationController @Before fun setUp() { - Mockito.`when`(context.packageName).thenReturn("packageName") - Mockito.`when`(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs) - Mockito.`when`(context.contentResolver) - .thenReturn(Mockito.mock(ContentResolver::class.java)) - Mockito.`when`(sharedPrefs.edit()).thenReturn(sharedPrefsEditor) - sut = BubbleEducationController(context) + sharedPrefs = mContext.getSharedPreferences(mContext.packageName, Context.MODE_PRIVATE) + sharedPrefs.edit { + remove(PREF_STACK_EDUCATION) + remove(PREF_MANAGED_EDUCATION) + } + sut = BubbleEducationController(mContext) } @Test fun testSeenStackEducation_read() { - Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true) + sharedPrefs.edit { putBoolean(PREF_STACK_EDUCATION, true) } assertEquals(sut.hasSeenStackEducation, true) - Mockito.verify(sharedPrefs).getBoolean(PREF_STACK_EDUCATION, false) } @Test fun testSeenStackEducation_write() { sut.hasSeenStackEducation = true - Mockito.verify(sharedPrefsEditor).putBoolean(PREF_STACK_EDUCATION, true) + assertThat(sharedPrefs.getBoolean(PREF_STACK_EDUCATION, false)).isTrue() } @Test fun testSeenManageEducation_read() { - Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true) + sharedPrefs.edit { putBoolean(PREF_MANAGED_EDUCATION, true) } assertEquals(sut.hasSeenManageEducation, true) - Mockito.verify(sharedPrefs).getBoolean(PREF_MANAGED_EDUCATION, false) } @Test fun testSeenManageEducation_write() { sut.hasSeenManageEducation = true - Mockito.verify(sharedPrefsEditor).putBoolean(PREF_MANAGED_EDUCATION, true) + assertThat(sharedPrefs.getBoolean(PREF_MANAGED_EDUCATION, false)).isTrue() } @Test fun testShouldShowStackEducation() { - val bubble = Mockito.mock(Bubble::class.java) // When bubble is null assertEquals(sut.shouldShowStackEducation(null), false) + var bubble = createFakeBubble(isConversational = false) // When bubble is not conversation - Mockito.`when`(bubble.isConversation).thenReturn(false) assertEquals(sut.shouldShowStackEducation(bubble), false) // When bubble is conversation and has seen stack edu - Mockito.`when`(bubble.isConversation).thenReturn(true) - Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true) + bubble = createFakeBubble(isConversational = true) + sharedPrefs.edit { putBoolean(PREF_STACK_EDUCATION, true) } assertEquals(sut.shouldShowStackEducation(bubble), false) // When bubble is conversation and has not seen stack edu - Mockito.`when`(bubble.isConversation).thenReturn(true) - Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(false) + sharedPrefs.edit { remove(PREF_STACK_EDUCATION) } assertEquals(sut.shouldShowStackEducation(bubble), true) } @Test fun testShouldShowManageEducation() { - val bubble = Mockito.mock(Bubble::class.java) // When bubble is null assertEquals(sut.shouldShowManageEducation(null), false) + var bubble = createFakeBubble(isConversational = false) // When bubble is not conversation - Mockito.`when`(bubble.isConversation).thenReturn(false) assertEquals(sut.shouldShowManageEducation(bubble), false) // When bubble is conversation and has seen stack edu - Mockito.`when`(bubble.isConversation).thenReturn(true) - Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true) + bubble = createFakeBubble(isConversational = true) + sharedPrefs.edit { putBoolean(PREF_MANAGED_EDUCATION, true) } assertEquals(sut.shouldShowManageEducation(bubble), false) // When bubble is conversation and has not seen stack edu - Mockito.`when`(bubble.isConversation).thenReturn(true) - Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(false) + sharedPrefs.edit { remove(PREF_MANAGED_EDUCATION) } assertEquals(sut.shouldShowManageEducation(bubble), true) } + + private fun createFakeBubble(isConversational: Boolean): Bubble { + return if (isConversational) { + val shortcutInfo = ShortcutInfo.Builder(mContext, "fakeId").build() + Bubble( + "key", + shortcutInfo, + /* desiredHeight= */ 6, + Resources.ID_NULL, + "title", + /* taskId= */ 0, + "locus", + /* isDismissable= */ true, + directExecutor() + ) {} + } else { + val intent = Intent(Intent.ACTION_VIEW).setPackage(mContext.packageName) + Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index d8511e8f38e2..65b8b555cf2b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -126,6 +126,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -391,7 +392,8 @@ public class BubblesTest extends SysuiTestCase { mock(NotifPipelineFlags.class), mock(KeyguardNotificationVisibilityProvider.class), mock(UiEventLogger.class), - mock(UserTracker.class) + mock(UserTracker.class), + mock(DeviceProvisionedController.class) ); mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java index 4e14bbf6ac1f..0df235dd2416 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.notification.interruption.KeyguardNotifica import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -48,7 +49,8 @@ public class TestableNotificationInterruptStateProviderImpl NotifPipelineFlags flags, KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider, UiEventLogger uiEventLogger, - UserTracker userTracker) { + UserTracker userTracker, + DeviceProvisionedController deviceProvisionedController) { super(contentResolver, powerManager, ambientDisplayConfiguration, @@ -61,7 +63,8 @@ public class TestableNotificationInterruptStateProviderImpl flags, keyguardNotificationVisibilityProvider, uiEventLogger, - userTracker); + userTracker, + deviceProvisionedController); mUseHeadsUp = true; } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt new file mode 100644 index 000000000000..0e594964bb30 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt @@ -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 com.android.systemui + +import com.android.systemui.data.FakeSystemUiDataLayerModule +import com.android.systemui.flags.FakeFeatureFlagsClassicModule +import com.android.systemui.log.FakeUiEventLoggerModule +import com.android.systemui.scene.FakeSceneModule +import com.android.systemui.settings.FakeSettingsModule +import com.android.systemui.util.concurrency.FakeExecutorModule +import dagger.Module + +@Module( + includes = + [ + FakeExecutorModule::class, + FakeFeatureFlagsClassicModule::class, + FakeSettingsModule::class, + FakeSceneModule::class, + FakeSystemUiDataLayerModule::class, + FakeUiEventLoggerModule::class, + ] +) +object FakeSystemUiModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java index aa88a46d670c..cd009dff27ff 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java @@ -193,7 +193,7 @@ public abstract class SysuiTestCase { return null; } - protected FakeBroadcastDispatcher getFakeBroadcastDispatcher() { + public FakeBroadcastDispatcher getFakeBroadcastDispatcher() { return mFakeBroadcastDispatcher; } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt index 60291eece70b..3fdeb302dc34 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt @@ -29,6 +29,8 @@ class FakeDisplayStateRepository : DisplayStateRepository { private val _currentRotation = MutableStateFlow<DisplayRotation>(DisplayRotation.ROTATION_0) override val currentRotation: StateFlow<DisplayRotation> = _currentRotation.asStateFlow() + override val isReverseDefaultRotation = false + fun setIsInRearDisplayMode(isInRearDisplayMode: Boolean) { _isInRearDisplayMode.value = isInRearDisplayMode } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerDataLayerModule.kt new file mode 100644 index 000000000000..42c02c49d6a9 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerDataLayerModule.kt @@ -0,0 +1,20 @@ +/* + * 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.bouncer.data.repository + +import dagger.Module + +@Module(includes = [FakeKeyguardBouncerRepositoryModule::class]) object FakeBouncerDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerMessageRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerMessageRepository.kt deleted file mode 100644 index d9b926d80589..000000000000 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerMessageRepository.kt +++ /dev/null @@ -1,83 +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.systemui.bouncer.data.repository - -import com.android.systemui.bouncer.shared.model.BouncerMessageModel -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow - -class FakeBouncerMessageRepository : BouncerMessageRepository { - private val _primaryAuthMessage = MutableStateFlow<BouncerMessageModel?>(null) - override val primaryAuthMessage: StateFlow<BouncerMessageModel?> - get() = _primaryAuthMessage - - private val _faceAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null) - override val faceAcquisitionMessage: StateFlow<BouncerMessageModel?> - get() = _faceAcquisitionMessage - private val _fingerprintAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null) - override val fingerprintAcquisitionMessage: StateFlow<BouncerMessageModel?> - get() = _fingerprintAcquisitionMessage - private val _customMessage = MutableStateFlow<BouncerMessageModel?>(null) - override val customMessage: StateFlow<BouncerMessageModel?> - get() = _customMessage - private val _biometricAuthMessage = MutableStateFlow<BouncerMessageModel?>(null) - override val biometricAuthMessage: StateFlow<BouncerMessageModel?> - get() = _biometricAuthMessage - private val _authFlagsMessage = MutableStateFlow<BouncerMessageModel?>(null) - override val authFlagsMessage: StateFlow<BouncerMessageModel?> - get() = _authFlagsMessage - - private val _biometricLockedOutMessage = MutableStateFlow<BouncerMessageModel?>(null) - override val biometricLockedOutMessage: Flow<BouncerMessageModel?> - get() = _biometricLockedOutMessage - - override fun setPrimaryAuthMessage(value: BouncerMessageModel?) { - _primaryAuthMessage.value = value - } - - override fun setFaceAcquisitionMessage(value: BouncerMessageModel?) { - _faceAcquisitionMessage.value = value - } - - override fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?) { - _fingerprintAcquisitionMessage.value = value - } - - override fun setCustomMessage(value: BouncerMessageModel?) { - _customMessage.value = value - } - - fun setBiometricAuthMessage(value: BouncerMessageModel?) { - _biometricAuthMessage.value = value - } - - fun setAuthFlagsMessage(value: BouncerMessageModel?) { - _authFlagsMessage.value = value - } - - fun setBiometricLockedOutMessage(value: BouncerMessageModel?) { - _biometricLockedOutMessage.value = value - } - - override fun clearMessage() { - _primaryAuthMessage.value = null - _faceAcquisitionMessage.value = null - _fingerprintAcquisitionMessage.value = null - _customMessage.value = null - } -} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt index b45c198b5515..f84481c55664 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt @@ -2,6 +2,10 @@ package com.android.systemui.bouncer.data.repository import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -10,7 +14,8 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow /** Fake implementation of [KeyguardBouncerRepository] */ -class FakeKeyguardBouncerRepository : KeyguardBouncerRepository { +@SysUISingleton +class FakeKeyguardBouncerRepository @Inject constructor() : KeyguardBouncerRepository { private val _primaryBouncerShow = MutableStateFlow(false) override val primaryBouncerShow = _primaryBouncerShow.asStateFlow() private val _primaryBouncerShowingSoon = MutableStateFlow(false) @@ -112,3 +117,8 @@ class FakeKeyguardBouncerRepository : KeyguardBouncerRepository { _sideFpsShowing.value = isShowing } } + +@Module +interface FakeKeyguardBouncerRepositoryModule { + @Binds fun bindFake(fake: FakeKeyguardBouncerRepository): KeyguardBouncerRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/FakeCommonDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/FakeCommonDataLayerModule.kt new file mode 100644 index 000000000000..1f4cb652822d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/FakeCommonDataLayerModule.kt @@ -0,0 +1,21 @@ +/* + * 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.common.ui.data + +import com.android.systemui.common.ui.data.repository.FakeConfigurationRepositoryModule +import dagger.Module + +@Module(includes = [FakeConfigurationRepositoryModule::class]) object FakeCommonDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt index 72cdbbc97896..10b284a03a59 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt @@ -16,13 +16,18 @@ package com.android.systemui.common.ui.data.repository +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -class FakeConfigurationRepository : ConfigurationRepository { +@SysUISingleton +class FakeConfigurationRepository @Inject constructor() : ConfigurationRepository { private val _onAnyConfigurationChange = MutableSharedFlow<Unit>() override val onAnyConfigurationChange: Flow<Unit> = _onAnyConfigurationChange.asSharedFlow() @@ -45,3 +50,8 @@ class FakeConfigurationRepository : ConfigurationRepository { throw IllegalStateException("Don't use for tests") } } + +@Module +interface FakeConfigurationRepositoryModule { + @Binds fun bindFake(fake: FakeConfigurationRepository): ConfigurationRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt new file mode 100644 index 000000000000..f866932309bb --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.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.data + +import com.android.systemui.bouncer.data.repository.FakeBouncerDataLayerModule +import com.android.systemui.common.ui.data.FakeCommonDataLayerModule +import com.android.systemui.keyguard.data.FakeKeyguardDataLayerModule +import com.android.systemui.power.data.FakePowerDataLayerModule +import com.android.systemui.shade.data.repository.FakeShadeDataLayerModule +import com.android.systemui.statusbar.data.FakeStatusBarDataLayerModule +import com.android.systemui.telephony.data.FakeTelephonyDataLayerModule +import com.android.systemui.user.data.FakeUserDataLayerModule +import dagger.Module + +@Module( + includes = + [ + FakeCommonDataLayerModule::class, + FakeBouncerDataLayerModule::class, + FakeKeyguardDataLayerModule::class, + FakePowerDataLayerModule::class, + FakeShadeDataLayerModule::class, + FakeStatusBarDataLayerModule::class, + FakeTelephonyDataLayerModule::class, + FakeUserDataLayerModule::class, + ] +) +object FakeSystemUiDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt index 43c9c99744c5..32469b6cfc30 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt @@ -16,6 +16,9 @@ package com.android.systemui.flags +import dagger.Binds +import dagger.Module +import dagger.Provides import java.io.PrintWriter class FakeFeatureFlagsClassic : FakeFeatureFlags() @@ -159,3 +162,20 @@ open class FakeFeatureFlags : FeatureFlagsClassic { ?: error("Flag ${flagName(flagName)} was accessed as int but not specified.") } } + +@Module(includes = [FakeFeatureFlagsClassicModule.Bindings::class]) +class FakeFeatureFlagsClassicModule( + @get:Provides val fakeFeatureFlagsClassic: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(), +) { + + constructor( + block: FakeFeatureFlagsClassic.() -> Unit + ) : this(FakeFeatureFlagsClassic().apply(block)) + + @Module + interface Bindings { + @Binds fun bindFake(fake: FakeFeatureFlagsClassic): FeatureFlagsClassic + @Binds fun bindClassic(classic: FeatureFlagsClassic): FeatureFlags + @Binds fun bindFakeClassic(fake: FakeFeatureFlagsClassic): FakeFeatureFlags + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt new file mode 100644 index 000000000000..abf72af0e1d5 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt @@ -0,0 +1,31 @@ +/* + * 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 + +import com.android.systemui.keyguard.data.repository.FakeCommandQueueModule +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepositoryModule +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepositoryModule +import dagger.Module + +@Module( + includes = + [ + FakeCommandQueueModule::class, + FakeKeyguardRepositoryModule::class, + FakeKeyguardTransitionRepositoryModule::class, + ] +) +object FakeKeyguardDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt index fe941179830a..3a59f6a8784f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt @@ -18,11 +18,17 @@ package com.android.systemui.keyguard.data.repository import android.content.Context +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.settings.DisplayTracker import com.android.systemui.statusbar.CommandQueue +import dagger.Binds +import dagger.Module +import javax.inject.Inject import org.mockito.Mockito.mock -class FakeCommandQueue : CommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java)) { +@SysUISingleton +class FakeCommandQueue @Inject constructor() : + CommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java)) { private val callbacks = mutableListOf<Callbacks>() override fun addCallback(callback: Callbacks) { @@ -39,3 +45,8 @@ class FakeCommandQueue : CommandQueue(mock(Context::class.java), mock(DisplayTra fun callbackCount(): Int = callbacks.size } + +@Module +interface FakeCommandQueueModule { + @Binds fun bindFake(fake: FakeCommandQueue): CommandQueue +} 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 dae8644466fb..a5f5d52ef56c 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 @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.data.repository import android.graphics.Point import com.android.systemui.common.shared.model.Position +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import com.android.systemui.keyguard.shared.model.DismissAction @@ -31,6 +32,9 @@ import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.WakeSleepReason import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.keyguard.shared.model.WakefulnessState +import dagger.Binds +import dagger.Module +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -38,7 +42,8 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow /** Fake implementation of [KeyguardRepository] */ -class FakeKeyguardRepository : KeyguardRepository { +@SysUISingleton +class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { private val _deferKeyguardDone: MutableSharedFlow<KeyguardDone> = MutableSharedFlow() override val keyguardDone: Flow<KeyguardDone> = _deferKeyguardDone @@ -280,3 +285,8 @@ class FakeKeyguardRepository : KeyguardRepository { ) } } + +@Module +interface FakeKeyguardRepositoryModule { + @Binds fun bindFake(fake: FakeKeyguardRepository): KeyguardRepository +} 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 dd513db03d34..e160548fed78 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 @@ -18,16 +18,21 @@ package com.android.systemui.keyguard.data.repository import android.annotation.FloatRange +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import dagger.Binds +import dagger.Module import java.util.UUID +import javax.inject.Inject import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow /** Fake implementation of [KeyguardTransitionRepository] */ -class FakeKeyguardTransitionRepository : KeyguardTransitionRepository { +@SysUISingleton +class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitionRepository { private val _transitions = MutableSharedFlow<TransitionStep>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) @@ -47,3 +52,8 @@ class FakeKeyguardTransitionRepository : KeyguardTransitionRepository { state: TransitionState ) = Unit } + +@Module +interface FakeKeyguardTransitionRepositoryModule { + @Binds fun bindFake(fake: FakeKeyguardTransitionRepository): KeyguardTransitionRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt index 9d98f9409ed9..482126d4bcca 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt @@ -25,6 +25,9 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow class FakeTrustRepository : TrustRepository { + private val _isTrustUsuallyManaged = MutableStateFlow(false) + override val isCurrentUserTrustUsuallyManaged: StateFlow<Boolean> + get() = _isTrustUsuallyManaged private val _isCurrentUserTrusted = MutableStateFlow(false) override val isCurrentUserTrusted: Flow<Boolean> get() = _isCurrentUserTrusted @@ -55,4 +58,8 @@ class FakeTrustRepository : TrustRepository { fun setRequestDismissKeyguard(trustModel: TrustModel) { _requestDismissKeyguard.value = trustModel } + + fun setTrustUsuallyManaged(value: Boolean) { + _isTrustUsuallyManaged.value = value + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt index ceab8e9e52ee..945aaede0087 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt @@ -50,6 +50,7 @@ import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode import com.android.systemui.user.domain.interactor.RefreshUsersScheduler import com.android.systemui.user.domain.interactor.UserInteractor import com.android.systemui.util.time.FakeSystemClock +import com.android.systemui.utils.UserRestrictionChecker import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.test.TestScope import org.mockito.Mockito.mock @@ -137,6 +138,7 @@ object KeyguardDismissInteractorFactory { refreshUsersScheduler = mock(RefreshUsersScheduler::class.java), guestUserInteractor = mock(GuestUserInteractor::class.java), uiEventLogger = mock(UiEventLogger::class.java), + userRestrictionChecker = mock(UserRestrictionChecker::class.java), ) return WithDependencies( trustRepository = trustRepository, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/FakeUiEventLoggerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/FakeUiEventLoggerModule.kt new file mode 100644 index 000000000000..3eb909eec463 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/FakeUiEventLoggerModule.kt @@ -0,0 +1,34 @@ +/* + * 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.log + +import com.android.internal.logging.UiEventLogger +import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import dagger.Provides + +@Module +interface FakeUiEventLoggerModule { + + @Binds fun bindFake(fake: UiEventLoggerFake): UiEventLogger + + @Module + companion object { + @Provides @SysUISingleton fun provideFake(): UiEventLoggerFake = UiEventLoggerFake() + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/FakePowerDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/FakePowerDataLayerModule.kt new file mode 100644 index 000000000000..7cbfa2467c76 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/FakePowerDataLayerModule.kt @@ -0,0 +1,21 @@ +/* + * 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.power.data + +import com.android.systemui.power.data.repository.FakePowerRepositoryModule +import dagger.Module + +@Module(includes = [FakePowerRepositoryModule::class]) object FakePowerDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt index b92d946b3750..5ab8204edde4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt @@ -18,15 +18,18 @@ package com.android.systemui.power.data.repository import android.os.PowerManager +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -class FakePowerRepository( - initialInteractive: Boolean = true, -) : PowerRepository { +@SysUISingleton +class FakePowerRepository @Inject constructor() : PowerRepository { - private val _isInteractive = MutableStateFlow(initialInteractive) + private val _isInteractive = MutableStateFlow(true) override val isInteractive: Flow<Boolean> = _isInteractive.asStateFlow() var lastWakeWhy: String? = null @@ -47,3 +50,8 @@ class FakePowerRepository( userTouchRegistered = true } } + +@Module +interface FakePowerRepositoryModule { + @Binds fun bindFake(fake: FakePowerRepository): PowerRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/FakeSceneModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/FakeSceneModule.kt new file mode 100644 index 000000000000..5d22a6e10a26 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/FakeSceneModule.kt @@ -0,0 +1,23 @@ +/* + * 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.scene + +import com.android.systemui.scene.shared.flag.FakeSceneContainerFlagsModule +import com.android.systemui.scene.shared.model.FakeSceneContainerConfigModule +import dagger.Module + +@Module(includes = [FakeSceneContainerConfigModule::class, FakeSceneContainerFlagsModule::class]) +object FakeSceneModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index 1620dc278950..69c89e8f4af6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -206,6 +206,7 @@ class SceneTestUtils( return BouncerViewModel( applicationContext = context, applicationScope = applicationScope(), + mainDispatcher = testDispatcher, bouncerInteractor = bouncerInteractor, authenticationInteractor = authenticationInteractor, flags = sceneContainerFlags, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt index 01a1ecea9997..bae5257a98bd 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt @@ -16,6 +16,10 @@ package com.android.systemui.scene.shared.flag +import dagger.Binds +import dagger.Module +import dagger.Provides + class FakeSceneContainerFlags( var enabled: Boolean = false, ) : SceneContainerFlags { @@ -28,3 +32,13 @@ class FakeSceneContainerFlags( return "" } } + +@Module(includes = [FakeSceneContainerFlagsModule.Bindings::class]) +class FakeSceneContainerFlagsModule( + @get:Provides val sceneContainerFlags: FakeSceneContainerFlags = FakeSceneContainerFlags(), +) { + @Module + interface Bindings { + @Binds fun bindFake(fake: FakeSceneContainerFlags): SceneContainerFlags + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneContainerConfigModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneContainerConfigModule.kt new file mode 100644 index 000000000000..b4fc948cd2e0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneContainerConfigModule.kt @@ -0,0 +1,36 @@ +/* + * 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.scene.shared.model + +import dagger.Module +import dagger.Provides + +@Module +data class FakeSceneContainerConfigModule( + @get:Provides + val sceneContainerConfig: SceneContainerConfig = + SceneContainerConfig( + sceneKeys = + listOf( + SceneKey.QuickSettings, + SceneKey.Shade, + SceneKey.Lockscreen, + SceneKey.Bouncer, + SceneKey.Gone, + ), + initialSceneKey = SceneKey.Lockscreen, + ), +) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeSettingsModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeSettingsModule.kt new file mode 100644 index 000000000000..c9a416ed3c95 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeSettingsModule.kt @@ -0,0 +1,20 @@ +/* + * 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.settings + +import dagger.Module + +@Module(includes = [FakeUserTrackerModule::class]) object FakeSettingsModule 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 f5f924dfce4b..4307ff980be4 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,9 @@ import android.content.pm.UserInfo import android.os.UserHandle import android.test.mock.MockContentResolver import com.android.systemui.util.mockito.mock +import dagger.Binds +import dagger.Module +import dagger.Provides import java.util.concurrent.Executor /** A fake [UserTracker] to be used in tests. */ @@ -84,3 +87,13 @@ class FakeUserTracker( callbacks.forEach { it.onProfilesChanged(_userProfiles) } } } + +@Module(includes = [FakeUserTrackerModule.Bindings::class]) +class FakeUserTrackerModule( + @get:Provides val fakeUserTracker: FakeUserTracker = FakeUserTracker() +) { + @Module + interface Bindings { + @Binds fun bindFake(fake: FakeUserTracker): UserTracker + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeDataLayerModule.kt new file mode 100644 index 000000000000..d90e2ea7f386 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeDataLayerModule.kt @@ -0,0 +1,20 @@ +/* + * 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.data.repository + +import dagger.Module + +@Module(includes = [FakeShadeRepositoryModule::class]) object FakeShadeDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt index 8b721b2b5f35..3c49c58580cc 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt @@ -17,12 +17,17 @@ package com.android.systemui.shade.data.repository +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.shade.domain.model.ShadeModel +import dagger.Binds +import dagger.Module +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow /** Fake implementation of [ShadeRepository] */ -class FakeShadeRepository : ShadeRepository { +@SysUISingleton +class FakeShadeRepository @Inject constructor() : ShadeRepository { private val _shadeModel = MutableStateFlow(ShadeModel()) override val shadeModel: Flow<ShadeModel> = _shadeModel @@ -89,3 +94,8 @@ class FakeShadeRepository : ShadeRepository { _legacyShadeExpansion.value = expandedFraction } } + +@Module +interface FakeShadeRepositoryModule { + @Binds fun bindFake(fake: FakeShadeRepository): ShadeRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt new file mode 100644 index 000000000000..1bec82b0875b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt @@ -0,0 +1,29 @@ +/* + * 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.data + +import com.android.systemui.statusbar.disableflags.data.FakeStatusBarDisableFlagsDataLayerModule +import com.android.systemui.statusbar.pipeline.data.FakeStatusBarPipelineDataLayerModule +import dagger.Module + +@Module( + includes = + [ + FakeStatusBarDisableFlagsDataLayerModule::class, + FakeStatusBarPipelineDataLayerModule::class, + ] +) +object FakeStatusBarDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/FakeStatusBarDisableFlagsDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/FakeStatusBarDisableFlagsDataLayerModule.kt new file mode 100644 index 000000000000..1ffb1b98a3a3 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/FakeStatusBarDisableFlagsDataLayerModule.kt @@ -0,0 +1,22 @@ +/* + * 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.disableflags.data + +import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepositoryModule +import dagger.Module + +@Module(includes = [FakeDisableFlagsRepositoryModule::class]) +object FakeStatusBarDisableFlagsDataLayerModule diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt index b66231c63bc6..466a3eb83e95 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt @@ -14,9 +14,19 @@ package com.android.systemui.statusbar.disableflags.data.repository +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel +import dagger.Binds +import dagger.Module +import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow -class FakeDisableFlagsRepository : DisableFlagsRepository { +@SysUISingleton +class FakeDisableFlagsRepository @Inject constructor() : DisableFlagsRepository { override val disableFlags = MutableStateFlow(DisableFlagsModel()) } + +@Module +interface FakeDisableFlagsRepositoryModule { + @Binds fun bindFake(fake: FakeDisableFlagsRepository): DisableFlagsRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/data/FakeStatusBarPipelineDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/data/FakeStatusBarPipelineDataLayerModule.kt new file mode 100644 index 000000000000..21a52a6f7f85 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/data/FakeStatusBarPipelineDataLayerModule.kt @@ -0,0 +1,22 @@ +/* + * 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.pipeline.data + +import com.android.systemui.statusbar.pipeline.mobile.data.FakeStatusBarPipelineMobileDataLayerModule +import dagger.Module + +@Module(includes = [FakeStatusBarPipelineMobileDataLayerModule::class]) +object FakeStatusBarPipelineDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/FakeStatusBarPipelineMobileDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/FakeStatusBarPipelineMobileDataLayerModule.kt new file mode 100644 index 000000000000..549929c2c04a --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/FakeStatusBarPipelineMobileDataLayerModule.kt @@ -0,0 +1,22 @@ +/* + * 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.pipeline.mobile.data + +import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepositoryModule +import dagger.Module + +@Module(includes = [FakeUserSetupRepositoryModule::class]) +object FakeStatusBarPipelineMobileDataLayerModule diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeUserSetupRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeUserSetupRepository.kt index 141b50c017e1..55e81bbc77e7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeUserSetupRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeUserSetupRepository.kt @@ -16,10 +16,15 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow /** Defaults to `true` */ -class FakeUserSetupRepository : UserSetupRepository { +@SysUISingleton +class FakeUserSetupRepository @Inject constructor() : UserSetupRepository { private val _isUserSetup: MutableStateFlow<Boolean> = MutableStateFlow(true) override val isUserSetupFlow = _isUserSetup @@ -27,3 +32,8 @@ class FakeUserSetupRepository : UserSetupRepository { _isUserSetup.value = setup } } + +@Module +interface FakeUserSetupRepositoryModule { + @Binds fun bindFake(fake: FakeUserSetupRepository): UserSetupRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/FakeTelephonyDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/FakeTelephonyDataLayerModule.kt new file mode 100644 index 000000000000..ec866ae84fb2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/FakeTelephonyDataLayerModule.kt @@ -0,0 +1,21 @@ +/* + * 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.telephony.data + +import com.android.systemui.telephony.data.repository.FakeTelephonyRepositoryModule +import dagger.Module + +@Module(includes = [FakeTelephonyRepositoryModule::class]) object FakeTelephonyDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/FakeTelephonyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/FakeTelephonyRepository.kt index 59f24ef2a706..7c70846d4d76 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/FakeTelephonyRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/FakeTelephonyRepository.kt @@ -17,11 +17,16 @@ package com.android.systemui.telephony.data.repository +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -class FakeTelephonyRepository : TelephonyRepository { +@SysUISingleton +class FakeTelephonyRepository @Inject constructor() : TelephonyRepository { private val _callState = MutableStateFlow(0) override val callState: Flow<Int> = _callState.asStateFlow() @@ -30,3 +35,8 @@ class FakeTelephonyRepository : TelephonyRepository { _callState.value = value } } + +@Module +interface FakeTelephonyRepositoryModule { + @Binds fun bindFake(fake: FakeTelephonyRepository): TelephonyRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/FakeUserDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/FakeUserDataLayerModule.kt new file mode 100644 index 000000000000..b00f8d22ca2e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/FakeUserDataLayerModule.kt @@ -0,0 +1,21 @@ +/* + * 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.user.data + +import com.android.systemui.user.data.repository.FakeUserRepositoryModule +import dagger.Module + +@Module(includes = [FakeUserRepositoryModule::class]) object FakeUserDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt index 5ad19eed18b8..1124425e5d49 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt @@ -19,17 +19,22 @@ package com.android.systemui.user.data.repository import android.content.pm.UserInfo import android.os.UserHandle +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.user.data.model.SelectedUserModel import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.model.UserSwitcherSettingsModel +import dagger.Binds +import dagger.Module import java.util.concurrent.atomic.AtomicBoolean +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.yield -class FakeUserRepository : UserRepository { +@SysUISingleton +class FakeUserRepository @Inject constructor() : UserRepository { companion object { // User id to represent a non system (human) user id. We presume this is the main user. private const val MAIN_USER_ID = 10 @@ -117,3 +122,8 @@ class FakeUserRepository : UserRepository { _isGuestUserAutoCreated = value } } + +@Module +interface FakeUserRepositoryModule { + @Binds fun bindFake(fake: FakeUserRepository): UserRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt new file mode 100644 index 000000000000..5de05c27ba2e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt @@ -0,0 +1,35 @@ +/* + * 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.concurrency + +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.util.time.FakeSystemClock +import dagger.Binds +import dagger.Module +import dagger.Provides +import java.util.concurrent.Executor + +@Module(includes = [FakeExecutorModule.Bindings::class]) +class FakeExecutorModule( + @get:Provides val clock: FakeSystemClock = FakeSystemClock(), +) { + @get:Provides val executor = FakeExecutor(clock) + + @Module + interface Bindings { + @Binds @Main fun bindMainExecutor(executor: FakeExecutor): Executor + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java index 4f9cb35db1a3..be57658a4266 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java @@ -46,7 +46,7 @@ public class FakeRotationLockController extends BaseLeakChecker<RotationLockCont } @Override - public void setRotationLocked(boolean locked) { + public void setRotationLocked(boolean locked, String caller) { } @@ -56,7 +56,7 @@ public class FakeRotationLockController extends BaseLeakChecker<RotationLockCont } @Override - public void setRotationLockedAtAngle(boolean locked, int rotation) { + public void setRotationLockedAtAngle(boolean locked, int rotation, String caller) { } } diff --git a/services/Android.bp b/services/Android.bp index 9264172974e1..3ae9360f3c44 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -34,17 +34,18 @@ java_defaults { }, } -// Opt-in config for optimizing and shrinking the services target using R8. -// Enabled via `export SYSTEM_OPTIMIZE_JAVA=true`, or explicitly in Make via the +// Config to control optimizing and shrinking the services target using R8. +// Set via `export SYSTEM_OPTIMIZE_JAVA=true|false`, or explicitly in Make via the // `SOONG_CONFIG_ANDROID_SYSTEM_OPTIMIZE_JAVA` variable. -// TODO(b/196084106): Enable optimizations by default after stabilizing and -// building out retrace infrastructure. soong_config_module_type { name: "system_optimized_java_defaults", module_type: "java_defaults", config_namespace: "ANDROID", bool_variables: ["SYSTEM_OPTIMIZE_JAVA"], - properties: ["optimize"], + properties: [ + "optimize", + "dxflags", + ], } system_optimized_java_defaults { @@ -75,6 +76,9 @@ system_optimized_java_defaults { // permission subpackage to prune unused jarjar'ed Kotlin dependencies. proguard_flags_files: ["proguard_permission.flags"], }, + // Explicitly configure R8 to preserve debug info, as this path should + // really only allow stripping of permission-specific code and deps. + dxflags: ["--debug"], }, }, }, @@ -273,4 +277,5 @@ droidstubs { tag: ".removed-api.txt", }, ], + api_surface: "system-server", } diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 3709f4714a4f..11189cf9e39c 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -19,4 +19,18 @@ flag { namespace: "accessibility" description: "Whether to enable joystick controls for magnification" bug: "297211257" -}
\ No newline at end of file +} + +flag { + name: "send_a11y_events_based_on_state" + namespace: "accessibility" + description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness." + bug: "295575684" +} + +flag { + name: "add_window_token_without_lock" + namespace: "accessibility" + description: "Calls WMS.addWindowToken without holding A11yManagerService#mLock" + bug: "297972548" +} diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 44ffb51452e1..fa73cffb83ea 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -56,6 +56,7 @@ import android.graphics.ParcelableColorSpace; import android.graphics.Region; import android.hardware.HardwareBuffer; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerInternal; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -97,6 +98,7 @@ import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; import com.android.internal.util.function.pooled.PooledLambda; +import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection; import com.android.server.accessibility.magnification.MagnificationProcessor; import com.android.server.inputmethod.InputMethodManagerInternal; @@ -1439,24 +1441,19 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, callback); return; } + final long identity = Binder.clearCallingIdentity(); try { - ScreenCapture.ScreenCaptureListener screenCaptureListener = new - ScreenCapture.ScreenCaptureListener( - (screenshotBuffer, result) -> { - if (screenshotBuffer != null && result == 0) { - sendScreenshotSuccess(screenshotBuffer, callback); - } else { - sendScreenshotFailure( - AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, - callback); - } - } - ); - mWindowManagerService.captureDisplay(displayId, null, screenCaptureListener); - } catch (Exception e) { - sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, - callback); + mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> { + final ScreenshotHardwareBuffer screenshotBuffer = LocalServices + .getService(DisplayManagerInternal.class).userScreenshot(displayId); + if (screenshotBuffer != null) { + sendScreenshotSuccess(screenshotBuffer, callback); + } else { + sendScreenshotFailure( + AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, callback); + } + }, null).recycleOnUse()); } finally { Binder.restoreCallingIdentity(identity); } @@ -1464,24 +1461,22 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private void sendScreenshotSuccess(ScreenshotHardwareBuffer screenshotBuffer, RemoteCallback callback) { - mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> { - final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer(); - final ParcelableColorSpace colorSpace = - new ParcelableColorSpace(screenshotBuffer.getColorSpace()); + final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer(); + final ParcelableColorSpace colorSpace = + new ParcelableColorSpace(screenshotBuffer.getColorSpace()); - final Bundle payload = new Bundle(); - payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS, - AccessibilityService.TAKE_SCREENSHOT_SUCCESS); - payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER, - hardwareBuffer); - payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace); - payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP, - SystemClock.uptimeMillis()); + final Bundle payload = new Bundle(); + payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS, + AccessibilityService.TAKE_SCREENSHOT_SUCCESS); + payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER, + hardwareBuffer); + payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace); + payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP, + SystemClock.uptimeMillis()); - // Send back the result. - callback.sendResult(payload); - hardwareBuffer.close(); - }, null).recycleOnUse()); + // Send back the result. + callback.sendResult(payload); + hardwareBuffer.close(); } private void sendScreenshotFailure(@AccessibilityService.ScreenshotErrorCode int errorCode, @@ -1511,11 +1506,17 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } - public void onAdded() { + /** + * Called when the connection is first created. Add a window token for all known displays. + * <p> + * <strong>Note:</strong> Should not be called while holding the AccessibilityManagerService + * lock because this calls out to WindowManagerService. + */ + void addWindowTokensForAllDisplays() { final Display[] displays = mDisplayManager.getDisplays(); for (int i = 0; i < displays.length; i++) { final int displayId = displays[i].getDisplayId(); - onDisplayAdded(displayId); + addWindowTokenForDisplay(displayId); } } @@ -1523,9 +1524,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ * Called whenever a logical display has been added to the system. Add a window token for adding * an accessibility overlay. * + * <p> + * <strong>Note:</strong> Should not be called while holding the AccessibilityManagerService + * lock because this calls out to WindowManagerService. + * * @param displayId The id of the logical display that was added. */ - public void onDisplayAdded(int displayId) { + void addWindowTokenForDisplay(int displayId) { final long identity = Binder.clearCallingIdentity(); try { final IBinder overlayWindowToken = new Binder(); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 93ba362070d3..60d4ee61fdd4 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -154,6 +154,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IntPair; +import com.android.internal.util.Preconditions; import com.android.server.AccessibilityManagerInternal; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -4500,6 +4501,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private int mSystemUiUid = 0; AccessibilityDisplayListener(Context context, Handler handler) { + if (Flags.addWindowTokenWithoutLock()) { + // Avoid concerns about one thread adding displays while another thread removes + // them by ensuring the looper is the main looper and the DisplayListener + // callbacks are always executed on the one main thread. + final boolean isMainHandler = handler.getLooper() == Looper.getMainLooper(); + final String errorMessage = + "AccessibilityDisplayListener must use the main handler"; + if (Build.IS_USERDEBUG || Build.IS_ENG) { + Preconditions.checkArgument(isMainHandler, errorMessage); + } else if (!isMainHandler) { + Slog.e(LOG_TAG, errorMessage); + } + } + mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); mDisplayManager.registerDisplayListener(this, handler); initializeDisplayList(); @@ -4541,11 +4556,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void onDisplayAdded(int displayId) { + if (Flags.addWindowTokenWithoutLock()) { + final boolean isMainThread = Looper.getMainLooper().isCurrentThread(); + final String errorMessage = "onDisplayAdded must be called from the main thread"; + if (Build.IS_USERDEBUG || Build.IS_ENG) { + Preconditions.checkArgument(isMainThread, errorMessage); + } else if (!isMainThread) { + Slog.e(LOG_TAG, errorMessage); + } + } final Display display = mDisplayManager.getDisplay(displayId); if (!isValidDisplay(display)) { return; } + final List<AccessibilityServiceConnection> services; synchronized (mLock) { mDisplaysList.add(display); mA11yOverlayLayers.put( @@ -4554,21 +4579,42 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mInputFilter.onDisplayAdded(display); } AccessibilityUserState userState = getCurrentUserStateLocked(); - if (displayId != Display.DEFAULT_DISPLAY) { - final List<AccessibilityServiceConnection> services = userState.mBoundServices; - for (int i = 0; i < services.size(); i++) { - AccessibilityServiceConnection boundClient = services.get(i); - boundClient.onDisplayAdded(displayId); + if (Flags.addWindowTokenWithoutLock()) { + services = new ArrayList<>(userState.mBoundServices); + } else { + services = userState.mBoundServices; + if (displayId != Display.DEFAULT_DISPLAY) { + for (int i = 0; i < services.size(); i++) { + AccessibilityServiceConnection boundClient = services.get(i); + boundClient.addWindowTokenForDisplay(displayId); + } } } updateMagnificationLocked(userState); updateWindowsForAccessibilityCallbackLocked(userState); notifyClearAccessibilityCacheLocked(); } + if (Flags.addWindowTokenWithoutLock()) { + if (displayId != Display.DEFAULT_DISPLAY) { + for (int i = 0; i < services.size(); i++) { + AccessibilityServiceConnection boundClient = services.get(i); + boundClient.addWindowTokenForDisplay(displayId); + } + } + } } @Override public void onDisplayRemoved(int displayId) { + if (Flags.addWindowTokenWithoutLock()) { + final boolean isMainThread = Looper.getMainLooper().isCurrentThread(); + final String errorMessage = "onDisplayRemoved must be called from the main thread"; + if (Build.IS_USERDEBUG || Build.IS_ENG) { + Preconditions.checkArgument(isMainThread, errorMessage); + } else if (!isMainThread) { + Slog.e(LOG_TAG, errorMessage); + } + } synchronized (mLock) { if (!removeDisplayFromList(displayId)) { return; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 9e700734d821..7a2a60263d38 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -44,7 +44,6 @@ import android.util.Slog; import android.view.Display; import android.view.MotionEvent; - import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -169,6 +168,10 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public void onServiceConnected(ComponentName componentName, IBinder service) { + AccessibilityUserState userState = mUserStateWeakReference.get(); + if (userState != null && Flags.addWindowTokenWithoutLock()) { + addWindowTokensForAllDisplays(); + } synchronized (mLock) { if (mService != service) { if (mService != null) { @@ -184,7 +187,6 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } } mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service); - AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; userState.addServiceLocked(this); mSystemSupport.onClientChangeLocked(false); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index ab6cc71fecab..693526adc8d6 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -224,7 +224,9 @@ class AccessibilityUserState { void addServiceLocked(AccessibilityServiceConnection serviceConnection) { if (!mBoundServices.contains(serviceConnection)) { - serviceConnection.onAdded(); + if (!Flags.addWindowTokenWithoutLock()) { + serviceConnection.addWindowTokensForAllDisplays(); + } mBoundServices.add(serviceConnection); mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection); mServiceInfoChangeListener.onServiceInfoChangedLocked(this); diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java index 208acdfb49e3..53c629a9ed2d 100644 --- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java @@ -25,9 +25,11 @@ import android.app.UiAutomation; import android.content.ComponentName; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; +import android.os.Looper; import android.os.RemoteCallback; import android.os.RemoteException; import android.util.Slog; @@ -35,6 +37,7 @@ import android.view.Display; import android.view.accessibility.AccessibilityEvent; import com.android.internal.util.DumpUtils; +import com.android.internal.util.Preconditions; import com.android.server.utils.Slogf; import com.android.server.wm.WindowManagerInternal; @@ -98,46 +101,47 @@ class UiAutomationManager { accessibilityServiceInfo.setComponentName(COMPONENT_NAME); Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s) when called by user %d", accessibilityServiceInfo.getId(), Binder.getCallingUserHandle().getIdentifier()); - synchronized (mLock) { - if (mUiAutomationService != null) { - throw new IllegalStateException( - "UiAutomationService " + mUiAutomationService.mServiceInterface - + "already registered!"); - } - - try { - owner.linkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0); - } catch (RemoteException re) { - Slog.e(LOG_TAG, "Couldn't register for the death of a UiTestAutomationService!", - re); - return; - } + if (mUiAutomationService != null) { + throw new IllegalStateException( + "UiAutomationService " + mUiAutomationService.mServiceInterface + + "already registered!"); + } - mUiAutomationFlags = flags; - mSystemSupport = systemSupport; - // Ignore registering UiAutomation if it is not allowed to use the accessibility - // subsystem. - if (!useAccessibility()) { - return; - } - mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id, - mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal, - systemActionPerformer, awm); - mUiAutomationServiceOwner = owner; - mUiAutomationService.mServiceInterface = serviceClient; - try { - mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService, - 0); - } catch (RemoteException re) { - Slog.e(LOG_TAG, "Failed registering death link: " + re); - destroyUiAutomationService(); - return; - } + try { + owner.linkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0); + } catch (RemoteException re) { + Slog.e(LOG_TAG, "Couldn't register for the death of a UiTestAutomationService!", + re); + return; + } - mUiAutomationService.onAdded(); + mUiAutomationFlags = flags; + mSystemSupport = systemSupport; + // Ignore registering UiAutomation if it is not allowed to use the accessibility + // subsystem. + if (!useAccessibility()) { + return; + } + mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id, + mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal, + systemActionPerformer, awm); + mUiAutomationServiceOwner = owner; + mUiAutomationService.mServiceInterface = serviceClient; + try { + mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService, + 0); + } catch (RemoteException re) { + Slog.e(LOG_TAG, "Failed registering death link: " + re); + destroyUiAutomationService(); + return; + } - mUiAutomationService.connectServiceUnknownThread(); + if (!Flags.addWindowTokenWithoutLock()) { + mUiAutomationService.addWindowTokensForAllDisplays(); } + // UiAutomationService#connectServiceUnknownThread posts to a handler + // so this call should return immediately. + mUiAutomationService.connectServiceUnknownThread(); } void unregisterUiTestAutomationServiceLocked(IAccessibilityServiceClient serviceClient) { @@ -253,6 +257,13 @@ class UiAutomationManager { super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock, securityPolicy, systemSupport, trace, windowManagerInternal, systemActionPerformer, awm); + final boolean isMainHandler = mainHandler.getLooper() == Looper.getMainLooper(); + final String errorMessage = "UiAutomationService must use the main handler"; + if (Build.IS_USERDEBUG || Build.IS_ENG) { + Preconditions.checkArgument(isMainHandler, errorMessage); + } else if (!isMainHandler) { + Slog.e(LOG_TAG, errorMessage); + } mMainHandler = mainHandler; setDisplayTypes(DISPLAY_TYPE_DEFAULT | DISPLAY_TYPE_PROXY); } @@ -274,6 +285,9 @@ class UiAutomationManager { // If the serviceInterface is null, the UiAutomation has been shut down on // another thread. if (serviceInterface != null) { + if (Flags.addWindowTokenWithoutLock()) { + mUiAutomationService.addWindowTokensForAllDisplays(); + } if (mTrace.isA11yTracingEnabledForTypes( AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) { mTrace.logTrace("UiAutomationService.connectServiceUnknownThread", @@ -286,7 +300,7 @@ class UiAutomationManager { mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); } } catch (RemoteException re) { - Slog.w(LOG_TAG, "Error initialized connection", re); + Slog.w(LOG_TAG, "Error initializing connection", re); destroyUiAutomationService(); } }); diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index 8060d5a96aa6..c4184854e690 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -58,6 +58,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accessibility.BaseEventStreamTransformation; import com.android.server.accessibility.EventStreamTransformation; +import com.android.server.accessibility.Flags; import com.android.server.policy.WindowManagerPolicy; import java.util.ArrayList; @@ -352,16 +353,34 @@ public class TouchExplorer extends BaseEventStreamTransformation } // The event for gesture end should be strictly after the // last hover exit event. - if (mSendTouchExplorationEndDelayed.isPending()) { - mSendTouchExplorationEndDelayed.cancel(); - mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END); - } + if (Flags.sendA11yEventsBasedOnState()) { + if (mSendTouchExplorationEndDelayed.isPending()) { + mSendTouchExplorationEndDelayed.cancel(); + } + if (mState.isTouchExploring()) { + mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END); + } - // The event for touch interaction end should be strictly after the - // last hover exit and the touch exploration gesture end events. - if (mSendTouchInteractionEndDelayed.isPending()) { - mSendTouchInteractionEndDelayed.cancel(); - mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END); + // The event for touch interaction end should be strictly after the + // last hover exit and the touch exploration gesture end events. + if (mSendTouchInteractionEndDelayed.isPending()) { + mSendTouchInteractionEndDelayed.cancel(); + } + if (mState.isTouchInteracting()) { + mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END); + } + } else { + if (mSendTouchExplorationEndDelayed.isPending()) { + mSendTouchExplorationEndDelayed.cancel(); + mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END); + } + + // The event for touch interaction end should be strictly after the + // last hover exit and the touch exploration gesture end events. + if (mSendTouchInteractionEndDelayed.isPending()) { + mSendTouchInteractionEndDelayed.cancel(); + mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END); + } } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index 2ca84f884f78..30b9d0b59467 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -40,6 +40,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; @@ -166,9 +167,12 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH @VisibleForTesting boolean mIsSinglePanningEnabled; - /** - * FullScreenMagnificationGestureHandler Constructor. - */ + private final FullScreenMagnificationVibrationHelper mFullScreenMagnificationVibrationHelper; + + @VisibleForTesting final OverscrollHandler mOverscrollHandler; + + private final boolean mIsWatch; + public FullScreenMagnificationGestureHandler(@UiContext Context context, FullScreenMagnificationController fullScreenMagnificationController, AccessibilityTraceManager trace, @@ -254,11 +258,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH mDetectingState = new DetectingState(context); mViewportDraggingState = new ViewportDraggingState(); mPanningScalingState = new PanningScalingState(context); - mSinglePanningState = new SinglePanningState(context, - fullScreenMagnificationVibrationHelper); + mSinglePanningState = new SinglePanningState(context); + mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper; setSinglePanningEnabled( context.getResources() .getBoolean(R.bool.config_enable_a11y_magnification_single_panning)); + mOverscrollHandler = new OverscrollHandler(); + mIsWatch = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); if (mDetectShortcutTrigger) { mScreenStateReceiver = new ScreenStateReceiver(context, this); @@ -468,15 +474,21 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH @Override public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { int action = event.getActionMasked(); - if (action == ACTION_POINTER_UP && event.getPointerCount() == 2 // includes the pointer currently being released && mPreviousState == mViewportDraggingState) { - + // if feature flag is enabled, currently only true on watches + if (mIsSinglePanningEnabled) { + mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded(); + mOverscrollHandler.clearEdgeState(); + } persistScaleAndTransitionTo(mViewportDraggingState); - } else if (action == ACTION_UP || action == ACTION_CANCEL) { - + // if feature flag is enabled, currently only true on watches + if (mIsSinglePanningEnabled) { + mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded(); + mOverscrollHandler.clearEdgeState(); + } persistScaleAndTransitionTo(mDetectingState); } } @@ -502,7 +514,12 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } public void persistScaleAndTransitionTo(State state) { - mFullScreenMagnificationController.persistScale(mDisplayId); + // If device is a watch don't change user settings scale. On watches, warp effect + // is enabled and the current display scale could be differ from the default user + // settings scale (should not change the scale due to the warp effect) + if (!mIsWatch) { + mFullScreenMagnificationController.persistScale(mDisplayId); + } clear(); transitionTo(state); } @@ -546,6 +563,9 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } mFullScreenMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX, distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); + if (mIsSinglePanningEnabled) { + mOverscrollHandler.onScrollStateChanged(first, second); + } return /* event consumed: */ true; } @@ -893,6 +913,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) { transitionToViewportDraggingStateAndClear(event); } else if (isActivated() && event.getPointerCount() == 2) { + if (mIsSinglePanningEnabled + && overscrollState(event, mFirstPointerDownLocation) + == OVERSCROLL_VERTICAL_EDGE) { + transitionToDelegatingStateAndClear(); + } //Primary pointer is swiping, so transit to PanningScalingState transitToPanningScalingStateAndClear(); } else if (mIsSinglePanningEnabled @@ -1264,6 +1289,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH + ", mMagnificationController=" + mFullScreenMagnificationController + ", mDisplayId=" + mDisplayId + ", mIsSinglePanningEnabled=" + mIsSinglePanningEnabled + + ", mOverscrollHandler=" + mOverscrollHandler + '}'; } @@ -1411,32 +1437,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH private final GestureDetector mScrollGestureDetector; private MotionEventInfo mEvent; - private final FullScreenMagnificationVibrationHelper - mFullScreenMagnificationVibrationHelper; - - @VisibleForTesting int mOverscrollState; - - // mPivotEdge is the point on the edge of the screen when the magnified view hits the edge - // This point sets the center of magnified view when warp/scale effect is triggered - private final PointF mPivotEdge; - - // mReachedEdgeCoord is the user's pointer location on the screen when the magnified view - // has hit the edge - private final PointF mReachedEdgeCoord; - // mEdgeCooldown value will be set to true when user hits the edge and will be set to false - // once the user moves x distance away from the edge. This is so that vibrating haptic - // doesn't get triggered by slight movements - private boolean mEdgeCooldown; - - SinglePanningState( - Context context, - FullScreenMagnificationVibrationHelper fullScreenMagnificationVibrationHelper) { + SinglePanningState(Context context) { mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain()); - mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper; - mOverscrollState = OVERSCROLL_NONE; - mPivotEdge = new PointF(Float.NaN, Float.NaN); - mReachedEdgeCoord = new PointF(Float.NaN, Float.NaN); - mEdgeCooldown = false; } @Override @@ -1445,17 +1447,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH switch (action) { case ACTION_UP: case ACTION_CANCEL: - if (mOverscrollState == OVERSCROLL_LEFT_EDGE - || mOverscrollState == OVERSCROLL_RIGHT_EDGE) { - mFullScreenMagnificationController.setScaleAndCenter( - mDisplayId, - mFullScreenMagnificationController.getPersistedScale(mDisplayId), - mPivotEdge.x, - mPivotEdge.y, - true, - AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); - } - clear(); + mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded(); + mOverscrollHandler.clearEdgeState(); transitionTo(mDetectingState); break; } @@ -1482,23 +1475,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH + " isAtEdge: " + mFullScreenMagnificationController.isAtEdge(mDisplayId)); } - if (mFullScreenMagnificationController.isAtEdge(mDisplayId)) { - playEdgeVibration(second); - setPivotEdge(); - } - if (mOverscrollState == OVERSCROLL_NONE) { - mOverscrollState = overscrollState(second, new PointF(first.getX(), first.getY())); - } else if (mOverscrollState == OVERSCROLL_VERTICAL_EDGE) { - clear(); - transitionTo(mDelegatingState); - } else { - boolean reset = warpEffectReset(second); - if (reset) { - mFullScreenMagnificationController.reset(mDisplayId, /* animate */ true); - clear(); - transitionTo(mDetectingState); - } - } + mOverscrollHandler.onScrollStateChanged(first, second); return /* event consumed: */ true; } @@ -1515,35 +1492,37 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH public String toString() { return "SinglePanningState{" + "isEdgeOfView=" - + mFullScreenMagnificationController.isAtEdge(mDisplayId) - + "overscrollStatus=" - + mOverscrollState - + "}"; + + mFullScreenMagnificationController.isAtEdge(mDisplayId); } - private void playEdgeVibration(MotionEvent event) { - if (mOverscrollState == OVERSCROLL_NONE) { - vibrateIfNeeded(event); - } - } + } - private void setPivotEdge() { - if (!pointerValid(mPivotEdge)) { - Rect bounds = new Rect(); - mFullScreenMagnificationController.getMagnificationBounds(mDisplayId, bounds); - if (mOverscrollState == OVERSCROLL_LEFT_EDGE) { - mPivotEdge.set( - bounds.left, - mFullScreenMagnificationController.getCenterY(mDisplayId)); - } else if (mOverscrollState == OVERSCROLL_RIGHT_EDGE) { - mPivotEdge.set( - bounds.right, - mFullScreenMagnificationController.getCenterY(mDisplayId)); - } - } + /** Overscroll Handler handles the logic when user is at the edge and scrolls past an edge */ + final class OverscrollHandler { + + @VisibleForTesting int mOverscrollState; + + // mPivotEdge is the point on the edge of the screen when the magnified view hits the edge + // This point sets the center of magnified view when warp/scale effect is triggered + private final PointF mPivotEdge; + + // mReachedEdgeCoord is the user's pointer location on the screen when the magnified view + // has hit the edge + private final PointF mReachedEdgeCoord; + + // mEdgeCooldown value will be set to true when user hits the edge and will be set to false + // once the user moves x distance away from the edge. This is so that vibrating haptic + // doesn't get triggered by slight movements + private boolean mEdgeCooldown; + + OverscrollHandler() { + mOverscrollState = OVERSCROLL_NONE; + mPivotEdge = new PointF(Float.NaN, Float.NaN); + mReachedEdgeCoord = new PointF(Float.NaN, Float.NaN); + mEdgeCooldown = false; } - private boolean warpEffectReset(MotionEvent second) { + protected boolean warpEffectReset(MotionEvent second) { float scale = calculateOverscrollScale(second); if (scale < 0) return false; mFullScreenMagnificationController.setScaleAndCenter( @@ -1566,7 +1545,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH float overshootDistX = second.getX() - mReachedEdgeCoord.x; if ((mOverscrollState == OVERSCROLL_LEFT_EDGE && overshootDistX < 0) || (mOverscrollState == OVERSCROLL_RIGHT_EDGE && overshootDistX > 0)) { - clear(); + clearEdgeState(); return -1.0f; } float overshootDistY = second.getY() - mReachedEdgeCoord.y; @@ -1611,21 +1590,109 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } private void vibrateIfNeeded(MotionEvent event) { + if (mOverscrollState != OVERSCROLL_NONE) { + return; + } if ((mFullScreenMagnificationController.isAtLeftEdge(mDisplayId) || mFullScreenMagnificationController.isAtRightEdge(mDisplayId)) && !mEdgeCooldown) { mFullScreenMagnificationVibrationHelper.vibrateIfSettingEnabled(); + } + } + + private void setPivotEdge(MotionEvent event) { + if (!pointerValid(mPivotEdge)) { + Rect bounds = new Rect(); + mFullScreenMagnificationController.getMagnificationBounds(mDisplayId, bounds); + if (mOverscrollState == OVERSCROLL_LEFT_EDGE) { + mPivotEdge.set( + bounds.left, mFullScreenMagnificationController.getCenterY(mDisplayId)); + } else if (mOverscrollState == OVERSCROLL_RIGHT_EDGE) { + mPivotEdge.set( + bounds.right, + mFullScreenMagnificationController.getCenterY(mDisplayId)); + } mReachedEdgeCoord.set(event.getX(), event.getY()); mEdgeCooldown = true; } } - @Override - public void clear() { + private void onScrollStateChanged(MotionEvent first, MotionEvent second) { + if (mFullScreenMagnificationController.isAtEdge(mDisplayId)) { + vibrateIfNeeded(second); + setPivotEdge(second); + } + switch (mOverscrollState) { + case OVERSCROLL_NONE: + onNoOverscroll(first, second); + break; + case OVERSCROLL_VERTICAL_EDGE: + onVerticalOverscroll(); + break; + case OVERSCROLL_LEFT_EDGE: + case OVERSCROLL_RIGHT_EDGE: + onHorizontalOverscroll(second); + break; + default: + Slog.d(mLogTag, "Invalid overscroll state"); + break; + } + } + + public void onNoOverscroll(MotionEvent first, MotionEvent second) { + mOverscrollState = overscrollState(second, new PointF(first.getX(), first.getY())); + } + + public void onVerticalOverscroll() { + clearEdgeState(); + transitionTo(mDelegatingState); + } + + public void onHorizontalOverscroll(MotionEvent second) { + boolean reset = warpEffectReset(second); + if (reset) { + mFullScreenMagnificationController.reset(mDisplayId, /* animate */ true); + clearEdgeState(); + transitionTo(mDelegatingState); + } + } + + private void setScaleAndCenterToEdgeIfNeeded() { + if (mOverscrollState == OVERSCROLL_LEFT_EDGE + || mOverscrollState == OVERSCROLL_RIGHT_EDGE) { + mFullScreenMagnificationController.setScaleAndCenter( + mDisplayId, + mFullScreenMagnificationController.getPersistedScale(mDisplayId), + mPivotEdge.x, + mPivotEdge.y, + true, + AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); + } + } + + private void clearEdgeState() { mOverscrollState = OVERSCROLL_NONE; mPivotEdge.set(Float.NaN, Float.NaN); mReachedEdgeCoord.set(Float.NaN, Float.NaN); mEdgeCooldown = false; } + + @Override + public String toString() { + return "OverscrollHandler {" + + "mOverscrollState=" + + mOverscrollState + + "mPivotEdge.x=" + + mPivotEdge.x + + "mPivotEdge.y=" + + mPivotEdge.y + + "mReachedEdgeCoord.x=" + + mReachedEdgeCoord.x + + "mReachedEdgeCoord.y=" + + mReachedEdgeCoord.y + + "mEdgeCooldown=" + + mEdgeCooldown + + "}"; + } } } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index ba4533960db4..c79149816b1a 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -155,6 +155,7 @@ public class CompanionDeviceManagerService extends SystemService { "debug.cdm.cdmservice.removal_time_window"; private static final long ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT = DAYS.toMillis(90); + private static final int MAX_CN_LENGTH = 500; private final ActivityManager mActivityManager; private final OnPackageVisibilityChangeListener mOnPackageVisibilityChangeListener; @@ -757,6 +758,9 @@ public class CompanionDeviceManagerService extends SystemService { String callingPackage = component.getPackageName(); checkCanCallNotificationApi(callingPackage); // TODO: check userId. + if (component.flattenToString().length() > MAX_CN_LENGTH) { + throw new IllegalArgumentException("Component name is too long."); + } final long identity = Binder.clearCallingIdentity(); try { return PendingIntent.getActivityAsUser(getContext(), diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java index e8839a2a76ae..720687ef20cc 100644 --- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java +++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java @@ -562,7 +562,8 @@ public class SecureChannel { private byte[] constructToken(D2DHandshakeContext.Role role, byte[] authValue) throws GeneralSecurityException { MessageDigest hash = MessageDigest.getInstance("SHA-256"); - byte[] roleUtf8 = role.name().getBytes(StandardCharsets.UTF_8); + String roleName = role == Role.INITIATOR ? "Initiator" : "Responder"; + byte[] roleUtf8 = roleName.getBytes(StandardCharsets.UTF_8); int tokenLength = roleUtf8.length + authValue.length; return hash.digest(ByteBuffer.allocate(tokenLength) .put(roleUtf8) diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java index 102c26245dc3..a3ccb168aa4e 100644 --- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java @@ -133,6 +133,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @GuardedBy("mGenericWindowPolicyControllerLock") private boolean mShowTasksInHostDeviceRecents; + @Nullable private final ComponentName mCustomHomeComponent; /** * Creates a window policy controller that is generic to the different use cases of virtual @@ -157,6 +158,10 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController * @param intentListenerCallback Callback that is called to intercept intents when matching * passed in filters. * @param showTasksInHostDeviceRecents whether to show activities in recents on the host device. + * @param customHomeComponent The component acting as a home activity on the virtual display. If + * {@code null}, then the system-default secondary home activity will be used. This is only + * applicable to displays that support home activities, i.e. they're created with the relevant + * virtual display flag. */ public GenericWindowPolicyController( int windowFlags, @@ -172,7 +177,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @Nullable SecureWindowCallback secureWindowCallback, @Nullable IntentListenerCallback intentListenerCallback, @NonNull Set<String> displayCategories, - boolean showTasksInHostDeviceRecents) { + boolean showTasksInHostDeviceRecents, + @Nullable ComponentName customHomeComponent) { super(); mAllowedUsers = allowedUsers; mActivityLaunchAllowedByDefault = activityLaunchAllowedByDefault; @@ -187,6 +193,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mIntentListenerCallback = intentListenerCallback; mDisplayCategories = displayCategories; mShowTasksInHostDeviceRecents = showTasksInHostDeviceRecents; + mCustomHomeComponent = customHomeComponent; } /** @@ -384,6 +391,11 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController return false; } + @Override + public @Nullable ComponentName getCustomHomeComponent() { + return mCustomHomeComponent; + } + /** * Returns true if an app with the given UID has an activity running on the virtual display for * this controller. 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 f328b22352ae..203a152ccc73 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -53,7 +53,7 @@ import android.companion.virtual.flags.Flags; import android.companion.virtual.sensor.VirtualSensor; import android.companion.virtual.sensor.VirtualSensorEvent; import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledSince; +import android.compat.annotation.EnabledAfter; import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; @@ -131,7 +131,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT */ @ChangeId - @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public static final long MAKE_VIRTUAL_DISPLAY_FLAGS_CONSISTENT_WITH_DISPLAY_MANAGER = 294837146L; @@ -938,6 +938,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mParams.getDefaultNavigationPolicy() == NAVIGATION_POLICY_DEFAULT_ALLOWED; final boolean showTasksInHostDeviceRecents = getDevicePolicy(POLICY_TYPE_RECENTS) == DEVICE_POLICY_DEFAULT; + final ComponentName homeComponent = + Flags.vdmCustomHome() ? mParams.getHomeComponent() : null; final GenericWindowPolicyController gwpc = new GenericWindowPolicyController( FLAG_SECURE, @@ -955,7 +957,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub this::onSecureWindowShown, this::shouldInterceptIntent, displayCategories, - showTasksInHostDeviceRecents); + showTasksInHostDeviceRecents, + homeComponent); gwpc.registerRunningAppsChangedListener(/* listener= */ this); return gwpc; } diff --git a/services/core/Android.bp b/services/core/Android.bp index d9c269410b93..6521fabe5b7c 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -194,6 +194,7 @@ java_library_static { "notification_flags_lib", "camera_platform_flags_core_java_lib", "biometrics_flags_lib", + "am_flags_lib", ], javac_shard_size: 50, javacflags: [ diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index c20f0aa4a62a..9716cf69015c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -225,7 +225,7 @@ final class ActivityManagerConstants extends ContentObserver { /** * The default value to {@link #KEY_ENABLE_NEW_OOMADJ}. */ - private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false; + private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite(); /** * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} diff --git a/services/core/java/com/android/server/am/Android.bp b/services/core/java/com/android/server/am/Android.bp new file mode 100644 index 000000000000..af1200e4bdf8 --- /dev/null +++ b/services/core/java/com/android/server/am/Android.bp @@ -0,0 +1,10 @@ +aconfig_declarations { + name: "am_flags", + package: "com.android.server.am", + srcs: ["*.aconfig"], +} + +java_aconfig_library { + name: "am_flags_lib", + aconfig_declarations: "am_flags", +} diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 5fa0ffaa9606..6005b64ca1bc 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -1442,7 +1442,6 @@ public final class CachedAppOptimizer { uidRec.setFrozen(false); postUidFrozenMessage(uidRec.getUid(), false); } - reportProcessFreezableChangedLocked(app); opt.setFreezerOverride(false); if (pid == 0 || !opt.isFrozen()) { @@ -1481,6 +1480,7 @@ public final class CachedAppOptimizer { if (processKilled) { return; } + reportProcessFreezableChangedLocked(app); long freezeTime = opt.getFreezeUnfreezeTime(); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 3d11c6843338..0615ecf029fa 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -3087,6 +3087,8 @@ public final class ProcessList { if (old == proc && proc.isPersistent()) { // We are re-adding a persistent process. Whatevs! Just leave it there. Slog.w(TAG, "Re-adding persistent process " + proc); + // Ensure that the mCrashing flag is cleared, since this is a restart + proc.resetCrashingOnRestart(); } else if (old != null) { if (old.isKilled()) { // The old process has been killed, we probably haven't had diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index cfbb5a5bc195..d8a269598bdc 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -618,6 +618,10 @@ class ProcessRecord implements WindowProcessListener { mPkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode)); } + void resetCrashingOnRestart() { + mErrorState.setCrashing(false); + } + @GuardedBy(anyOf = {"mService", "mProcLock"}) UidRecord getUidRecord() { return mUidRecord; diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 6d2fc0dc21f6..4a0bc4b9ca6c 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -133,11 +133,14 @@ public class SettingsToPropertiesMapper { "companion", "context_hub", "core_experiments_team_internal", + "core_graphics", "haptics", "hardware_backed_security_mainline", + "machine_learning", "media_audio", "media_solutions", "nfc", + "pixel_audio_android", "pixel_system_sw_touch", "pixel_watch", "platform_security", @@ -151,6 +154,7 @@ public class SettingsToPropertiesMapper { "threadnetwork", "tv_system_ui", "vibrator", + "virtual_devices", "wear_frameworks", "wear_system_health", "wear_systems", diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig new file mode 100644 index 000000000000..b03cc6295b8d --- /dev/null +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -0,0 +1,9 @@ +package: "com.android.server.am" + +flag { + name: "oomadjuster_correctness_rewrite" + namespace: "android_platform_power_optimization" + description: "Utilize new OomAdjuster implementation" + bug: "298055811" + is_fixed_read_only: true +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index cd867f60ebd0..0d6635d5b6e4 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -43,7 +43,6 @@ import android.window.ScreenCapture; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; -import com.android.server.wm.WindowManagerInternal; import libcore.io.Streams; @@ -408,12 +407,6 @@ final class ColorFade { } } - void stop() { - if (mEglContext != null && mEglDisplay != null) { - EGL14.eglDestroyContext(mEglDisplay, mEglContext); - } - } - /** * Draws an animation frame showing the color fade activated at the * specified level. @@ -574,21 +567,8 @@ final class ColorFade { } private ScreenCapture.ScreenshotHardwareBuffer captureScreen() { - WindowManagerInternal windowManagerService = LocalServices.getService( - WindowManagerInternal.class); - ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer; - ScreenCapture.SynchronousScreenCaptureListener screenCaptureListener = - ScreenCapture.createSyncCaptureListener(); - ScreenCapture.CaptureArgs captureArgs = new ScreenCapture.CaptureArgs.Builder<>() - .setCaptureSecureLayers(true) - .setAllowProtected(true) - .build(); - try { - windowManagerService.captureDisplay(mDisplayId, captureArgs, screenCaptureListener); - screenshotBuffer = screenCaptureListener.getBuffer(); - } catch (Exception e) { - screenshotBuffer = null; - } + ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = + mDisplayManagerInternal.systemScreenshot(mDisplayId); if (screenshotBuffer == null) { Slog.e(TAG, "Failed to take screenshot. Buffer is null"); return null; diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 507ae2676b16..9e92c8d7342d 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -500,6 +500,8 @@ public class DisplayDeviceConfig { public static final String DEFAULT_ID = "default"; + public static final int DEFAULT_LOW_REFRESH_RATE = 60; + private static final float BRIGHTNESS_DEFAULT = 0.5f; private static final String ETC_DIR = "etc"; private static final String DISPLAY_CONFIG_DIR = "displayconfig"; @@ -513,7 +515,6 @@ public class DisplayDeviceConfig { private static final int DEFAULT_PEAK_REFRESH_RATE = 0; private static final int DEFAULT_REFRESH_RATE = 60; private static final int DEFAULT_REFRESH_RATE_IN_HBM = 0; - private static final int DEFAULT_LOW_REFRESH_RATE = 60; private static final int DEFAULT_HIGH_REFRESH_RATE = 0; private static final float[] DEFAULT_BRIGHTNESS_THRESHOLDS = new float[]{}; diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index 213ee646fc34..3529b048bd34 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -16,6 +16,8 @@ package com.android.server.display; +import static android.view.Display.Mode.INVALID_MODE_ID; + import android.hardware.display.DeviceProductInfo; import android.hardware.display.DisplayViewport; import android.util.DisplayMetrics; @@ -275,6 +277,11 @@ final class DisplayDeviceInfo { public int defaultModeId; /** + * The mode of the display which is preferred by user. + */ + public int userPreferredModeId = INVALID_MODE_ID; + + /** * The supported modes of the display. */ public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY; @@ -472,6 +479,7 @@ final class DisplayDeviceInfo { || modeId != other.modeId || renderFrameRate != other.renderFrameRate || defaultModeId != other.defaultModeId + || userPreferredModeId != other.userPreferredModeId || !Arrays.equals(supportedModes, other.supportedModes) || !Arrays.equals(supportedColorModes, other.supportedColorModes) || !Objects.equals(hdrCapabilities, other.hdrCapabilities) @@ -517,6 +525,7 @@ final class DisplayDeviceInfo { modeId = other.modeId; renderFrameRate = other.renderFrameRate; defaultModeId = other.defaultModeId; + userPreferredModeId = other.userPreferredModeId; supportedModes = other.supportedModes; colorMode = other.colorMode; supportedColorModes = other.supportedColorModes; @@ -559,6 +568,7 @@ final class DisplayDeviceInfo { sb.append(", modeId ").append(modeId); sb.append(", renderFrameRate ").append(renderFrameRate); sb.append(", defaultModeId ").append(defaultModeId); + sb.append(", userPreferredModeId ").append(userPreferredModeId); sb.append(", supportedModes ").append(Arrays.toString(supportedModes)); sb.append(", colorMode ").append(colorMode); sb.append(", supportedColorModes ").append(Arrays.toString(supportedColorModes)); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index df45001b7086..9ef84cb8417e 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -137,6 +137,7 @@ import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl.RefreshRateRange; import android.window.DisplayWindowPolicyController; +import android.window.ScreenCapture; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -556,7 +557,7 @@ public final class DisplayManagerService extends SystemService { mLogicalDisplayMapper = new LogicalDisplayMapper(mContext, new FoldSettingProvider(mContext, new SettingsWrapper()), mDisplayDeviceRepo, new LogicalDisplayListener(), mSyncRoot, mHandler, mFlags); - mDisplayModeDirector = new DisplayModeDirector(context, mHandler); + mDisplayModeDirector = new DisplayModeDirector(context, mHandler, mFlags); mBrightnessSynchronizer = new BrightnessSynchronizer(mContext); Resources resources = mContext.getResources(); mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger( @@ -2027,9 +2028,6 @@ public final class DisplayManagerService extends SystemService { mDisplayBrightnesses.delete(displayId); DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); - sendDisplayEventLocked(display, event); - scheduleTraversalLocked(false); - if (mDisplayWindowPolicyControllers.contains(displayId)) { final IVirtualDevice virtualDevice = mDisplayWindowPolicyControllers.removeReturnOld(displayId).first; @@ -2040,6 +2038,9 @@ public final class DisplayManagerService extends SystemService { }); } } + + sendDisplayEventLocked(display, event); + scheduleTraversalLocked(false); } private void handleLogicalDisplaySwappedLocked(@NonNull LogicalDisplay display) { @@ -2673,6 +2674,42 @@ public final class DisplayManagerService extends SystemService { return null; } + private ScreenCapture.ScreenshotHardwareBuffer systemScreenshotInternal(int displayId) { + final ScreenCapture.DisplayCaptureArgs captureArgs; + synchronized (mSyncRoot) { + final IBinder token = getDisplayToken(displayId); + if (token == null) { + return null; + } + final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId); + if (logicalDisplay == null) { + return null; + } + + final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked(); + captureArgs = new ScreenCapture.DisplayCaptureArgs.Builder(token) + .setSize(displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight()) + .setCaptureSecureLayers(true) + .setAllowProtected(true) + .build(); + } + return ScreenCapture.captureDisplay(captureArgs); + } + + private ScreenCapture.ScreenshotHardwareBuffer userScreenshotInternal(int displayId) { + synchronized (mSyncRoot) { + final IBinder token = getDisplayToken(displayId); + if (token == null) { + return null; + } + + final ScreenCapture.DisplayCaptureArgs captureArgs = + new ScreenCapture.DisplayCaptureArgs.Builder(token) + .build(); + return ScreenCapture.captureDisplay(captureArgs); + } + } + @VisibleForTesting DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributesInternal( int displayId) { @@ -4441,6 +4478,16 @@ public final class DisplayManagerService extends SystemService { } @Override + public ScreenCapture.ScreenshotHardwareBuffer systemScreenshot(int displayId) { + return systemScreenshotInternal(displayId); + } + + @Override + public ScreenCapture.ScreenshotHardwareBuffer userScreenshot(int displayId) { + return userScreenshotInternal(displayId); + } + + @Override public DisplayInfo getDisplayInfo(int displayId) { return getDisplayInfoInternal(displayId, Process.myUid()); } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index bc81491aefbf..83f4df97c5dc 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -3577,8 +3577,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) { - return new DisplayPowerState(blanker, colorFade, displayId, displayState, - new Handler(/*async=*/ true)); + return new DisplayPowerState(blanker, colorFade, displayId, displayState); } DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps, diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 90a8490b6f49..b0d293a1709f 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -324,6 +324,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // Must only be accessed on the handler thread. private DisplayPowerState mPowerState; + + // The currently active screen on unblocker. This field is non-null whenever // we are waiting for a callback to release it and unblock the screen. private ScreenOnUnblocker mPendingScreenOnUnblocker; @@ -2914,8 +2916,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) { - return new DisplayPowerState(blanker, colorFade, displayId, displayState, - new Handler(/*async=*/ true)); + return new DisplayPowerState(blanker, colorFade, displayId, displayState); } DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps, diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 85c6a6de860f..2c257a17af91 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -74,9 +74,8 @@ final class DisplayPowerState { private volatile boolean mStopped; DisplayPowerState( - DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState, - Handler handler) { - mHandler = handler; + DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) { + mHandler = new Handler(true /*async*/); mChoreographer = Choreographer.getInstance(); mBlanker = blanker; mColorFade = colorFade; @@ -318,7 +317,6 @@ final class DisplayPowerState { mStopped = true; mPhotonicModulator.interrupt(); dismissColorFade(); - stopColorFade(); mCleanListener = null; mHandler.removeCallbacksAndMessages(null); } @@ -378,11 +376,6 @@ final class DisplayPowerState { } } - // Clears up color fade resources. - private void stopColorFade() { - if (mColorFade != null) mColorFade.stop(); - } - private final Runnable mScreenUpdateRunnable = new Runnable() { @Override public void run() { diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 924b1b3c66ab..0f3e7c5196ca 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -640,6 +640,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.modeId = mActiveModeId; mInfo.renderFrameRate = mActiveRenderFrameRate; mInfo.defaultModeId = getPreferredModeId(); + mInfo.userPreferredModeId = mUserPreferredModeId; mInfo.supportedModes = getDisplayModes(mSupportedModes); mInfo.colorMode = mActiveColorMode; mInfo.allmSupported = mAllmSupported; @@ -1344,6 +1345,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { public interface DisplayEventListener { void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected); + void onHotplugConnectionError(long timestampNanos, int connectionError); void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId, long renderPeriod); void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId, @@ -1366,6 +1368,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override + public void onHotplugConnectionError(long timestampNanos, int errorCode) { + mListener.onHotplugConnectionError(timestampNanos, errorCode); + } + + @Override public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId, long renderPeriod) { mListener.onModeChanged(timestampNanos, physicalDisplayId, modeId, renderPeriod); @@ -1391,6 +1398,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override + public void onHotplugConnectionError(long timestampNanos, int connectionError) { + if (DEBUG) { + Slog.d(TAG, "onHotplugConnectionError(" + + "timestampNanos=" + timestampNanos + + ", connectionError=" + connectionError + ")"); + } + } + + @Override public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId, long renderPeriod) { if (DEBUG) { diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 0405ebe8b5d2..d4d104e862f0 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -470,6 +470,7 @@ final class LogicalDisplay { mBaseDisplayInfo.modeId = deviceInfo.modeId; mBaseDisplayInfo.renderFrameRate = deviceInfo.renderFrameRate; mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId; + mBaseDisplayInfo.userPreferredModeId = deviceInfo.userPreferredModeId; mBaseDisplayInfo.supportedModes = Arrays.copyOf( deviceInfo.supportedModes, deviceInfo.supportedModes.length); mBaseDisplayInfo.colorMode = deviceInfo.colorMode; diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index 3f6bf1adfe48..ff768d64a7e1 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -47,6 +47,22 @@ public class DisplayManagerFlags { Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1, Flags::enableAdaptiveToneImprovements1); + private final FlagState mDisplayResolutionRangeVotingState = new FlagState( + Flags.FLAG_ENABLE_DISPLAY_RESOLUTION_RANGE_VOTING, + Flags::enableDisplayResolutionRangeVoting); + + private final FlagState mUserPreferredModeVoteState = new FlagState( + Flags.FLAG_ENABLE_USER_PREFERRED_MODE_VOTE, + Flags::enableUserPreferredModeVote); + + private final FlagState mExternalDisplayLimitModeState = new FlagState( + Flags.FLAG_ENABLE_MODE_LIMIT_FOR_EXTERNAL_DISPLAY, + Flags::enableModeLimitForExternalDisplay); + + private final FlagState mDisplaysRefreshRatesSynchronizationState = new FlagState( + Flags.FLAG_ENABLE_DISPLAYS_REFRESH_RATES_SYNCHRONIZATION, + Flags::enableDisplaysRefreshRatesSynchronization); + /** Returns whether connected display management is enabled or not. */ public boolean isConnectedDisplayManagementEnabled() { return mConnectedDisplayManagementFlagState.isEnabled(); @@ -68,6 +84,33 @@ public class DisplayManagerFlags { return mAdaptiveToneImprovements1.isEnabled(); } + /** Returns whether resolution range voting feature is enabled or not. */ + public boolean isDisplayResolutionRangeVotingEnabled() { + return mDisplayResolutionRangeVotingState.isEnabled(); + } + + /** + * @return Whether user preferred mode is added as a vote in + * {@link com.android.server.display.mode.DisplayModeDirector} + */ + public boolean isUserPreferredModeVoteEnabled() { + return mUserPreferredModeVoteState.isEnabled(); + } + + /** + * @return Whether external display mode limitation is enabled. + */ + public boolean isExternalDisplayLimitModeEnabled() { + return mExternalDisplayLimitModeState.isEnabled(); + } + + /** + * @return Whether displays refresh rate synchronization is enabled. + */ + public boolean isDisplaysRefreshRatesSynchronizationEnabled() { + return mDisplaysRefreshRatesSynchronizationState.isEnabled(); + } + private static class FlagState { private final String mName; diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index 4d8600448c33..a5b8cbbcdec4 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -5,7 +5,7 @@ package: "com.android.server.display.feature.flags" flag { name: "enable_connected_display_management" namespace: "display_manager" - description: "Feature flag for Connected Display managment" + description: "Feature flag for Connected Display management" bug: "280739508" is_fixed_read_only: true } @@ -34,3 +34,34 @@ flag { is_fixed_read_only: true } +flag { + name: "enable_display_resolution_range_voting" + namespace: "display_manager" + description: "Feature flag to enable voting for ranges of resolutions" + bug: "299297058" + is_fixed_read_only: true +} + +flag { + name: "enable_user_preferred_mode_vote" + namespace: "display_manager" + description: "Feature flag to use voting for UserPreferredMode for display" + bug: "297018612" + is_fixed_read_only: true +} + +flag { + name: "enable_mode_limit_for_external_display" + namespace: "display_manager" + description: "Feature limiting external display resolution and refresh rate" + bug: "242093547" + is_fixed_read_only: true +} + +flag { + name: "enable_displays_refresh_rates_synchronization" + namespace: "display_manager" + description: "Enables synchronization of refresh rates across displays" + bug: "294015845" + is_fixed_read_only: true +} diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index 2c2af3f7f435..71ea8cc30405 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -19,6 +19,9 @@ 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; import static android.os.PowerManager.BRIGHTNESS_INVALID_FLOAT; +import static android.view.Display.Mode.INVALID_MODE_ID; + +import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE; import android.annotation.IntegerRes; import android.annotation.NonNull; @@ -71,6 +74,7 @@ import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; import com.android.server.display.DisplayDeviceConfig; import com.android.server.display.feature.DeviceConfigParameterProvider; +import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.utils.AmbientFilter; import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.display.utils.DeviceConfigParsingUtils; @@ -84,9 +88,11 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Callable; import java.util.function.Function; import java.util.function.IntSupplier; @@ -96,6 +102,8 @@ import java.util.function.IntSupplier; * picked by the system based on system-wide and display-specific configuration. */ public class DisplayModeDirector { + public static final float SYNCHRONIZED_REFRESH_RATE_TARGET = DEFAULT_LOW_REFRESH_RATE; + public static final float SYNCHRONIZED_REFRESH_RATE_TOLERANCE = 1; private static final String TAG = "DisplayModeDirector"; private boolean mLoggingEnabled; @@ -151,12 +159,38 @@ public class DisplayModeDirector { @DisplayManager.SwitchingType private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS; - public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) { - this(context, handler, new RealInjector(context)); + /** + * Whether resolution range voting feature is enabled. + */ + private final boolean mIsDisplayResolutionRangeVotingEnabled; + + /** + * Whether user preferred mode voting feature is enabled. + */ + private final boolean mIsUserPreferredModeVoteEnabled; + + /** + * Whether limit display mode feature is enabled. + */ + private final boolean mIsExternalDisplayLimitModeEnabled; + + private final boolean mIsDisplaysRefreshRatesSynchronizationEnabled; + + public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler, + @NonNull DisplayManagerFlags displayManagerFlags) { + this(context, handler, new RealInjector(context), displayManagerFlags); } public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler, - @NonNull Injector injector) { + @NonNull Injector injector, + @NonNull DisplayManagerFlags displayManagerFlags) { + mIsDisplayResolutionRangeVotingEnabled = displayManagerFlags + .isDisplayResolutionRangeVotingEnabled(); + mIsUserPreferredModeVoteEnabled = displayManagerFlags.isUserPreferredModeVoteEnabled(); + mIsExternalDisplayLimitModeEnabled = displayManagerFlags + .isExternalDisplayLimitModeEnabled(); + mIsDisplaysRefreshRatesSynchronizationEnabled = displayManagerFlags + .isDisplaysRefreshRatesSynchronizationEnabled(); mContext = context; mHandler = new DisplayModeDirectorHandler(handler.getLooper()); mInjector = injector; @@ -230,6 +264,8 @@ public class DisplayModeDirector { public float maxRenderFrameRate; public int width; public int height; + public int minWidth; + public int minHeight; public boolean disableRefreshRateSwitching; public float appRequestBaseModeRefreshRate; @@ -244,6 +280,8 @@ public class DisplayModeDirector { maxRenderFrameRate = Float.POSITIVE_INFINITY; width = Vote.INVALID_SIZE; height = Vote.INVALID_SIZE; + minWidth = 0; + minHeight = 0; disableRefreshRateSwitching = false; appRequestBaseModeRefreshRate = 0f; } @@ -256,6 +294,8 @@ public class DisplayModeDirector { + ", maxRenderFrameRate=" + maxRenderFrameRate + ", width=" + width + ", height=" + height + + ", minWidth=" + minWidth + + ", minHeight=" + minHeight + ", disableRefreshRateSwitching=" + disableRefreshRateSwitching + ", appRequestBaseModeRefreshRate=" + appRequestBaseModeRefreshRate; } @@ -277,7 +317,6 @@ public class DisplayModeDirector { continue; } - // For physical refresh rates, just use the tightest bounds of all the votes. // The refresh rate cannot be lower than the minimal render frame rate. final float minPhysicalRefreshRate = Math.max(vote.refreshRateRanges.physical.min, @@ -298,10 +337,18 @@ public class DisplayModeDirector { // For display size, disable refresh rate switching and base mode refresh rate use only // the first vote we come across (i.e. the highest priority vote that includes the // attribute). - if (summary.height == Vote.INVALID_SIZE && summary.width == Vote.INVALID_SIZE - && vote.height > 0 && vote.width > 0) { - summary.width = vote.width; - summary.height = vote.height; + if (vote.height > 0 && vote.width > 0) { + if (summary.width == Vote.INVALID_SIZE && summary.height == Vote.INVALID_SIZE) { + summary.width = vote.width; + summary.height = vote.height; + summary.minWidth = vote.minWidth; + summary.minHeight = vote.minHeight; + } else if (mIsDisplayResolutionRangeVotingEnabled) { + summary.width = Math.min(summary.width, vote.width); + summary.height = Math.min(summary.height, vote.height); + summary.minWidth = Math.max(summary.minWidth, vote.minWidth); + summary.minHeight = Math.max(summary.minHeight, vote.minHeight); + } } if (!summary.disableRefreshRateSwitching && vote.disableRefreshRateSwitching) { summary.disableRefreshRateSwitching = true; @@ -413,6 +460,8 @@ public class DisplayModeDirector { || primarySummary.width == Vote.INVALID_SIZE) { primarySummary.width = defaultMode.getPhysicalWidth(); primarySummary.height = defaultMode.getPhysicalHeight(); + } else if (mIsDisplayResolutionRangeVotingEnabled) { + updateSummaryWithBestAllowedResolution(modes, primarySummary); } availableModes = filterModes(modes, primarySummary); @@ -654,6 +703,38 @@ public class DisplayModeDirector { return availableModes; } + private void updateSummaryWithBestAllowedResolution(final Display.Mode[] supportedModes, + VoteSummary outSummary) { + final int maxAllowedWidth = outSummary.width; + final int maxAllowedHeight = outSummary.height; + if (mLoggingEnabled) { + Slog.i(TAG, "updateSummaryWithBestAllowedResolution " + outSummary); + } + outSummary.width = Vote.INVALID_SIZE; + outSummary.height = Vote.INVALID_SIZE; + + int maxNumberOfPixels = 0; + for (Display.Mode mode : supportedModes) { + if (mode.getPhysicalWidth() > maxAllowedWidth + || mode.getPhysicalHeight() > maxAllowedHeight + || mode.getPhysicalWidth() < outSummary.minWidth + || mode.getPhysicalHeight() < outSummary.minHeight) { + continue; + } + + int numberOfPixels = mode.getPhysicalHeight() * mode.getPhysicalWidth(); + if (numberOfPixels > maxNumberOfPixels || (mode.getPhysicalWidth() == maxAllowedWidth + && mode.getPhysicalHeight() == maxAllowedHeight)) { + if (mLoggingEnabled) { + Slog.i(TAG, "updateSummaryWithBestAllowedResolution updated with " + mode); + } + maxNumberOfPixels = numberOfPixels; + outSummary.width = mode.getPhysicalWidth(); + outSummary.height = mode.getPhysicalHeight(); + } + } + } + /** * Gets the observer responsible for application display mode requests. */ @@ -1393,11 +1474,38 @@ public class DisplayModeDirector { private final Context mContext; private final Handler mHandler; private final VotesStorage mVotesStorage; + private int mExternalDisplayPeakWidth; + private int mExternalDisplayPeakHeight; + private int mExternalDisplayPeakRefreshRate; + private final boolean mRefreshRateSynchronizationEnabled; + private final Set<Integer> mExternalDisplaysConnected = new HashSet<>(); DisplayObserver(Context context, Handler handler, VotesStorage votesStorage) { mContext = context; mHandler = handler; mVotesStorage = votesStorage; + mExternalDisplayPeakRefreshRate = mContext.getResources().getInteger( + R.integer.config_externalDisplayPeakRefreshRate); + mExternalDisplayPeakWidth = mContext.getResources().getInteger( + R.integer.config_externalDisplayPeakWidth); + mExternalDisplayPeakHeight = mContext.getResources().getInteger( + R.integer.config_externalDisplayPeakHeight); + mRefreshRateSynchronizationEnabled = mContext.getResources().getBoolean( + R.bool.config_refreshRateSynchronizationEnabled); + } + + private boolean isExternalDisplayLimitModeEnabled() { + return mExternalDisplayPeakWidth > 0 + && mExternalDisplayPeakHeight > 0 + && mExternalDisplayPeakRefreshRate > 0 + && mIsExternalDisplayLimitModeEnabled + && mIsDisplayResolutionRangeVotingEnabled + && mIsUserPreferredModeVoteEnabled; + } + + private boolean isRefreshRateSynchronizationEnabled() { + return mRefreshRateSynchronizationEnabled + && mIsDisplaysRefreshRatesSynchronizationEnabled; } public void observe() { @@ -1428,6 +1536,9 @@ public class DisplayModeDirector { DisplayInfo displayInfo = getDisplayInfo(displayId); updateDisplayModes(displayId, displayInfo); updateLayoutLimitedFrameRate(displayId, displayInfo); + updateUserSettingDisplayPreferredSize(displayInfo); + updateDisplaysPeakRefreshRateAndResolution(displayInfo); + addDisplaysSynchronizedPeakRefreshRate(displayInfo); } @Override @@ -1437,6 +1548,9 @@ public class DisplayModeDirector { mDefaultModeByDisplay.remove(displayId); } updateLayoutLimitedFrameRate(displayId, null); + removeUserSettingDisplayPreferredSize(displayId); + removeDisplaysPeakRefreshRateAndResolution(displayId); + removeDisplaysSynchronizedPeakRefreshRate(displayId); } @Override @@ -1444,6 +1558,7 @@ public class DisplayModeDirector { DisplayInfo displayInfo = getDisplayInfo(displayId); updateDisplayModes(displayId, displayInfo); updateLayoutLimitedFrameRate(displayId, displayInfo); + updateUserSettingDisplayPreferredSize(displayInfo); } @Nullable @@ -1460,6 +1575,111 @@ public class DisplayModeDirector { mVotesStorage.updateVote(displayId, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE, vote); } + private void removeUserSettingDisplayPreferredSize(int displayId) { + if (!mIsUserPreferredModeVoteEnabled) { + return; + } + mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE, + null); + } + + private void updateUserSettingDisplayPreferredSize(@Nullable DisplayInfo info) { + if (info == null || !mIsUserPreferredModeVoteEnabled) { + return; + } + + var preferredMode = findDisplayPreferredMode(info); + if (preferredMode == null) { + removeUserSettingDisplayPreferredSize(info.displayId); + return; + } + + mVotesStorage.updateVote(info.displayId, + Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE, + Vote.forSize(/* width */ preferredMode.getPhysicalWidth(), + /* height */ preferredMode.getPhysicalHeight())); + } + + @Nullable + private Display.Mode findDisplayPreferredMode(@NonNull DisplayInfo info) { + if (info.userPreferredModeId == INVALID_MODE_ID) { + return null; + } + for (var mode : info.supportedModes) { + if (mode.getModeId() == info.userPreferredModeId) { + return mode; + } + } + return null; + } + + private void removeDisplaysPeakRefreshRateAndResolution(int displayId) { + if (!isExternalDisplayLimitModeEnabled()) { + return; + } + + mVotesStorage.updateVote(displayId, + Vote.PRIORITY_LIMIT_MODE, null); + } + + private void updateDisplaysPeakRefreshRateAndResolution(@Nullable final DisplayInfo info) { + // Only consider external display, only in case the refresh rate and resolution limits + // are non-zero. + if (info == null || info.type != Display.TYPE_EXTERNAL + || !isExternalDisplayLimitModeEnabled()) { + return; + } + + mVotesStorage.updateVote(info.displayId, + Vote.PRIORITY_LIMIT_MODE, + Vote.forSizeAndPhysicalRefreshRatesRange( + /* minWidth */ 0, /* minHeight */ 0, + mExternalDisplayPeakWidth, + mExternalDisplayPeakHeight, + /* minPhysicalRefreshRate */ 0, + mExternalDisplayPeakRefreshRate)); + } + + /** + * Sets 60Hz target refresh rate as the vote with + * {@link Vote#PRIORITY_SYNCHRONIZED_REFRESH_RATE} priority. + */ + private void addDisplaysSynchronizedPeakRefreshRate(@Nullable final DisplayInfo info) { + if (info == null || info.type != Display.TYPE_EXTERNAL + || !isRefreshRateSynchronizationEnabled()) { + return; + } + synchronized (mLock) { + mExternalDisplaysConnected.add(info.displayId); + if (mExternalDisplaysConnected.size() != 1) { + return; + } + } + // set minRefreshRate as the max refresh rate. + mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE, + Vote.forPhysicalRefreshRates( + SYNCHRONIZED_REFRESH_RATE_TARGET + - SYNCHRONIZED_REFRESH_RATE_TOLERANCE, + SYNCHRONIZED_REFRESH_RATE_TARGET + + SYNCHRONIZED_REFRESH_RATE_TOLERANCE)); + } + + private void removeDisplaysSynchronizedPeakRefreshRate(final int displayId) { + if (!isRefreshRateSynchronizationEnabled()) { + return; + } + synchronized (mLock) { + if (!mExternalDisplaysConnected.contains(displayId)) { + return; + } + mExternalDisplaysConnected.remove(displayId); + if (mExternalDisplaysConnected.size() != 0) { + return; + } + } + mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE, null); + } + private void updateDisplayModes(int displayId, @Nullable DisplayInfo info) { if (info == null) { return; diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java index a42d8f257ddf..b6a6069b5a63 100644 --- a/services/core/java/com/android/server/display/mode/Vote.java +++ b/services/core/java/com/android/server/display/mode/Vote.java @@ -18,6 +18,8 @@ package com.android.server.display.mode; import android.view.SurfaceControl; +import java.util.Objects; + final class Vote { // DEFAULT_RENDER_FRAME_RATE votes for render frame rate [0, DEFAULT]. As the lowest // priority vote, it's overridden by all other considerations. It acts to set a default @@ -36,12 +38,15 @@ final class Vote { // It votes [minRefreshRate, Float.POSITIVE_INFINITY] static final int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3; + // User setting preferred display resolution. + static final int PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE = 4; + // APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render // frame rate in certain cases, mostly to preserve power. // @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate // @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate // It votes to [preferredMinRefreshRate, preferredMaxRefreshRate]. - static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 4; + static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 5; // We split the app request into different priorities in case we can satisfy one desire // without the other. @@ -67,40 +72,47 @@ final class Vote { // The preferred refresh rate is set on the main surface of the app outside of // DisplayModeDirector. // @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded - static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 5; - static final int PRIORITY_APP_REQUEST_SIZE = 6; + static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 6; + + static final int PRIORITY_APP_REQUEST_SIZE = 7; // SETTING_PEAK_RENDER_FRAME_RATE has a high priority and will restrict the bounds of the // rest of low priority voters. It votes [0, max(PEAK, MIN)] - static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 7; + static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 8; + + // Restrict all displays to 60Hz when external display is connected. It votes [59Hz, 61Hz]. + static final int PRIORITY_SYNCHRONIZED_REFRESH_RATE = 9; + + // Restrict displays max available resolution and refresh rates. It votes [0, LIMIT] + static final int PRIORITY_LIMIT_MODE = 10; // To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh // rate to max value (same as for PRIORITY_UDFPS) on lock screen - static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 8; + static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 11; // For concurrent displays we want to limit refresh rate on all displays - static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 9; + static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 12; // LOW_POWER_MODE force the render frame rate to [0, 60HZ] if // Settings.Global.LOW_POWER_MODE is on. - static final int PRIORITY_LOW_POWER_MODE = 10; + static final int PRIORITY_LOW_POWER_MODE = 13; // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the // higher priority voters' result is a range, it will fix the rate to a single choice. // It's used to avoid refresh rate switches in certain conditions which may result in the // user seeing the display flickering when the switches occur. - static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 11; + static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 14; // Force display to [0, 60HZ] if skin temperature is at or above CRITICAL. - static final int PRIORITY_SKIN_TEMPERATURE = 12; + static final int PRIORITY_SKIN_TEMPERATURE = 15; // The proximity sensor needs the refresh rate to be locked in order to function, so this is // set to a high priority. - static final int PRIORITY_PROXIMITY = 13; + static final int PRIORITY_PROXIMITY = 16; // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order // to function, so this needs to be the highest priority of all votes. - static final int PRIORITY_UDFPS = 14; + static final int PRIORITY_UDFPS = 17; // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString. @@ -127,6 +139,14 @@ final class Vote { */ public final int height; /** + * Min requested width of the display in pixels, or 0; + */ + public final int minWidth; + /** + * Min requested height of the display in pixels, or 0; + */ + public final int minHeight; + /** * Information about the refresh rate frame rate ranges DM would like to set the display to. */ public final SurfaceControl.RefreshRateRanges refreshRateRanges; @@ -144,42 +164,82 @@ final class Vote { public final float appRequestBaseModeRefreshRate; static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) { - return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate, 0, - Float.POSITIVE_INFINITY, - minRefreshRate == maxRefreshRate, 0f); + return new Vote(/* minWidth= */ 0, /* minHeight= */ 0, + /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE, + /* minPhysicalRefreshRate= */ minRefreshRate, + /* maxPhysicalRefreshRate= */ maxRefreshRate, + /* minRenderFrameRate= */ 0, + /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY, + /* disableRefreshRateSwitching= */ minRefreshRate == maxRefreshRate, + /* baseModeRefreshRate= */ 0f); } static Vote forRenderFrameRates(float minFrameRate, float maxFrameRate) { - return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, minFrameRate, + return new Vote(/* minWidth= */ 0, /* minHeight= */ 0, + /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE, + /* minPhysicalRefreshRate= */ 0, + /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY, + minFrameRate, maxFrameRate, - false, 0f); + /* disableRefreshRateSwitching= */ false, + /* baseModeRefreshRate= */ 0f); } static Vote forSize(int width, int height) { - return new Vote(width, height, 0, Float.POSITIVE_INFINITY, 0, Float.POSITIVE_INFINITY, - false, - 0f); + return new Vote(/* minWidth= */ width, /* minHeight= */ height, + width, height, + /* minPhysicalRefreshRate= */ 0, + /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY, + /* minRenderFrameRate= */ 0, + /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY, + /* disableRefreshRateSwitching= */ false, + /* baseModeRefreshRate= */ 0f); + } + + static Vote forSizeAndPhysicalRefreshRatesRange(int minWidth, int minHeight, + int width, int height, float minRefreshRate, float maxRefreshRate) { + return new Vote(minWidth, minHeight, + width, height, + minRefreshRate, + maxRefreshRate, + /* minRenderFrameRate= */ 0, + /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY, + /* disableRefreshRateSwitching= */ minRefreshRate == maxRefreshRate, + /* baseModeRefreshRate= */ 0f); } static Vote forDisableRefreshRateSwitching() { - return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0, - Float.POSITIVE_INFINITY, true, - 0f); + return new Vote(/* minWidth= */ 0, /* minHeight= */ 0, + /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE, + /* minPhysicalRefreshRate= */ 0, + /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY, + /* minRenderFrameRate= */ 0, + /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY, + /* disableRefreshRateSwitching= */ true, + /* baseModeRefreshRate= */ 0f); } static Vote forBaseModeRefreshRate(float baseModeRefreshRate) { - return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0, - Float.POSITIVE_INFINITY, false, - baseModeRefreshRate); + return new Vote(/* minWidth= */ 0, /* minHeight= */ 0, + /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE, + /* minPhysicalRefreshRate= */ 0, + /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY, + /* minRenderFrameRate= */ 0, + /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY, + /* disableRefreshRateSwitching= */ false, + /* baseModeRefreshRate= */ baseModeRefreshRate); } - private Vote(int width, int height, + private Vote(int minWidth, int minHeight, + int width, int height, float minPhysicalRefreshRate, float maxPhysicalRefreshRate, float minRenderFrameRate, float maxRenderFrameRate, boolean disableRefreshRateSwitching, float baseModeRefreshRate) { + this.minWidth = minWidth; + this.minHeight = minHeight; this.width = width; this.height = height; this.refreshRateRanges = new SurfaceControl.RefreshRateRanges( @@ -215,6 +275,12 @@ final class Vote { return "PRIORITY_UDFPS"; case PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE: return "PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE"; + case PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE: + return "PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE"; + case PRIORITY_LIMIT_MODE: + return "PRIORITY_LIMIT_MODE"; + case PRIORITY_SYNCHRONIZED_REFRESH_RATE: + return "PRIORITY_SYNCHRONIZED_REFRESH_RATE"; case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE: return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE"; case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE: @@ -229,9 +295,29 @@ final class Vote { @Override public String toString() { return "Vote: {" - + "width: " + width + ", height: " + height + + "minWidth: " + minWidth + ", minHeight: " + minHeight + + ", width: " + width + ", height: " + height + ", refreshRateRanges: " + refreshRateRanges + ", disableRefreshRateSwitching: " + disableRefreshRateSwitching + ", appRequestBaseModeRefreshRate: " + appRequestBaseModeRefreshRate + "}"; } + + @Override + public int hashCode() { + return Objects.hash(minWidth, minHeight, width, height, refreshRateRanges, + disableRefreshRateSwitching, appRequestBaseModeRefreshRate); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Vote)) return false; + final var vote = (Vote) o; + return minWidth == vote.minWidth && minHeight == vote.minHeight + && width == vote.width && height == vote.height + && disableRefreshRateSwitching == vote.disableRefreshRateSwitching + && Float.compare(vote.appRequestBaseModeRefreshRate, + appRequestBaseModeRefreshRate) == 0 + && refreshRateRanges.equals(vote.refreshRateRanges); + } } diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java index dadcebe8d8a1..49c587aa5596 100644 --- a/services/core/java/com/android/server/display/mode/VotesStorage.java +++ b/services/core/java/com/android/server/display/mode/VotesStorage.java @@ -18,6 +18,7 @@ package com.android.server.display.mode; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Trace; import android.util.Slog; import android.util.SparseArray; @@ -30,7 +31,8 @@ class VotesStorage { private static final String TAG = "VotesStorage"; // Special ID used to indicate that given vote is to be applied globally, rather than to a // specific display. - private static final int GLOBAL_ID = -1; + @VisibleForTesting + static final int GLOBAL_ID = -1; private boolean mLoggingEnabled; @@ -90,6 +92,7 @@ class VotesStorage { + ", vote=" + vote); return; } + boolean changed = false; SparseArray<Vote> votes; synchronized (mStorageLock) { if (mVotesByDisplay.contains(displayId)) { @@ -98,16 +101,24 @@ class VotesStorage { votes = new SparseArray<>(); mVotesByDisplay.put(displayId, votes); } - if (vote != null) { + var currentVote = votes.get(priority); + if (vote != null && !vote.equals(currentVote)) { votes.put(priority, vote); - } else { + changed = true; + } else if (vote == null && currentVote != null) { votes.remove(priority); + changed = true; } } + Trace.traceCounter(Trace.TRACE_TAG_POWER, + TAG + "." + displayId + ":" + Vote.priorityToString(priority), + getMaxPhysicalRefreshRate(vote)); if (mLoggingEnabled) { Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes); } - mListener.onChanged(); + if (changed) { + mListener.onChanged(); + } } /** dump class values, for debugging */ @@ -146,6 +157,15 @@ class VotesStorage { } } + private int getMaxPhysicalRefreshRate(@Nullable Vote vote) { + if (vote == null) { + return -1; + } else if (vote.refreshRateRanges.physical.max == Float.POSITIVE_INFINITY) { + return 1000; // for visualisation, otherwise e.g. -1 -> 60 will be unnoticeable + } + return (int) vote.refreshRateRanges.physical.max; + } + interface Listener { void onChanged(); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java index 7045e65a8936..c01bc2063a59 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java @@ -868,6 +868,28 @@ public class HdmiCecNetwork { } @ServiceThreadOnly + void removeUnusedLocalDevices(ArrayList<HdmiCecLocalDevice> allocatedDevices) { + ArrayList<Integer> deviceTypesToRemove = new ArrayList<>(); + for (int i = 0; i < mLocalDevices.size(); i++) { + int deviceType = mLocalDevices.keyAt(i); + boolean shouldRemoveLocalDevice = allocatedDevices.stream().noneMatch( + localDevice -> localDevice.getDeviceInfo() != null + && localDevice.getDeviceInfo().getDeviceType() == deviceType); + if (shouldRemoveLocalDevice) { + deviceTypesToRemove.add(deviceType); + } + } + for (Integer deviceType : deviceTypesToRemove) { + mLocalDevices.remove(deviceType); + } + } + + @ServiceThreadOnly + void removeLocalDeviceWithType(int deviceType) { + mLocalDevices.remove(deviceType); + } + + @ServiceThreadOnly public void clearDeviceList() { assertRunOnServiceThread(); for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 232bc470c95c..429db5eeed49 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1313,9 +1313,6 @@ public class HdmiControlService extends SystemService { localDevice.init(); localDevices.add(localDevice); } - // It's now safe to flush existing local devices from mCecController since they were - // already moved to 'localDevices'. - clearCecLocalDevices(); mHdmiCecNetwork.clearDeviceList(); allocateLogicalAddress(localDevices, initiatedBy); } @@ -1344,6 +1341,7 @@ public class HdmiControlService extends SystemService { if (logicalAddress == Constants.ADDR_UNREGISTERED) { Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]"); + mHdmiCecNetwork.removeLocalDeviceWithType(deviceType); } else { // Set POWER_STATUS_ON to all local devices because they share // lifetime @@ -1352,6 +1350,8 @@ public class HdmiControlService extends SystemService { deviceType, HdmiControlManager.POWER_STATUS_ON, getCecVersion()); localDevice.setDeviceInfo(deviceInfo); + // If a local device of the same type already exists, it will be + // replaced. mHdmiCecNetwork.addLocalDevice(deviceType, localDevice); mHdmiCecNetwork.addCecDevice(localDevice.getDeviceInfo()); mCecController.addLogicalAddress(logicalAddress); @@ -1367,6 +1367,10 @@ public class HdmiControlService extends SystemService { // since we reallocate the logical address only. onInitializeCecComplete(initiatedBy); } + // We remove local devices here, instead of before the start of + // address allocation, to prevent multiple local devices of the + // same type from existing simultaneously. + mHdmiCecNetwork.removeUnusedLocalDevices(allocatedDevices); mAddressAllocated = true; notifyAddressAllocated(allocatedDevices, initiatedBy); // Reinvoke the saved display status callback once the local @@ -1386,9 +1390,19 @@ public class HdmiControlService extends SystemService { } } + /** + * Notifies local devices that address allocation finished. + * @param devices - list of local devices allocated. + * @param initiatedBy - reason for the address allocation. + */ + @VisibleForTesting @ServiceThreadOnly - private void notifyAddressAllocated(ArrayList<HdmiCecLocalDevice> devices, int initiatedBy) { + public void notifyAddressAllocated(ArrayList<HdmiCecLocalDevice> devices, int initiatedBy) { assertRunOnServiceThread(); + if (devices == null || devices.isEmpty()) { + Slog.w(TAG, "No local device to notify."); + return; + } List<HdmiCecMessage> bufferedMessages = mCecMessageBuffer.getBuffer(); for (HdmiCecLocalDevice device : devices) { int address = device.getDeviceInfo().getLogicalAddress(); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index d3ad6c437f78..3435e560b1f9 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -92,7 +92,6 @@ import android.os.IBinder; import android.os.LocaleList; import android.os.Looper; import android.os.Message; -import android.os.Parcel; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; @@ -1868,21 +1867,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub false /* enabledOnly */)); } - @Override - public boolean onTransact(int code, Parcel data, Parcel reply, int flags) - throws RemoteException { - try { - return super.onTransact(code, data, reply, flags); - } catch (RuntimeException e) { - // The input method manager only throws security exceptions, so let's - // log all others. - if (!(e instanceof SecurityException)) { - Slog.wtf(TAG, "Input Method Manager Crash", e); - } - throw e; - } - } - /** * TODO(b/32343335): The entire systemRunning() method needs to be revisited. */ diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index c3abfc16ca7d..f168f435b5d7 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -349,17 +349,17 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public void onUserStarting(@NonNull TargetUser user) { - mLockSettingsService.onStartUser(user.getUserIdentifier()); + mLockSettingsService.onUserStarting(user.getUserIdentifier()); } @Override public void onUserUnlocking(@NonNull TargetUser user) { - mLockSettingsService.onUnlockUser(user.getUserIdentifier()); + mLockSettingsService.onUserUnlocking(user.getUserIdentifier()); } @Override public void onUserStopped(@NonNull TargetUser user) { - mLockSettingsService.onCleanupUser(user.getUserIdentifier()); + mLockSettingsService.onUserStopped(user.getUserIdentifier()); } } @@ -784,7 +784,7 @@ public class LockSettingsService extends ILockSettings.Stub { } @VisibleForTesting - void onCleanupUser(int userId) { + void onUserStopped(int userId) { hideEncryptionNotification(new UserHandle(userId)); // User is stopped with its CE key evicted. Restore strong auth requirement to the default // flags after boot since stopping and restarting a user later is equivalent to rebooting @@ -796,7 +796,7 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private void onStartUser(final int userId) { + private void onUserStarting(final int userId) { maybeShowEncryptionNotificationForUser(userId, "user started"); } @@ -832,7 +832,7 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private void onUnlockUser(final int userId) { + private void onUserUnlocking(final int userId) { // Perform tasks which require locks in LSS on a handler, as we are callbacks from // ActivityManager.unlockUser() mHandler.post(new Runnable() { diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index c6f6fe2c01f3..fe91050917f5 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -1021,7 +1021,7 @@ abstract public class ManagedServices { synchronized (mSnoozing) { mSnoozing.remove(user); } - rebindServices(true, user); + unbindUserServices(user); } public void onUserSwitched(int user) { @@ -1408,12 +1408,24 @@ abstract public class ManagedServices { void unbindOtherUserServices(int currentUser) { TimingsTraceAndSlog t = new TimingsTraceAndSlog(); t.traceBegin("ManagedServices.unbindOtherUserServices_current" + currentUser); - final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>(); + unbindServicesImpl(currentUser, true /* allExceptUser */); + t.traceEnd(); + } + + void unbindUserServices(int user) { + TimingsTraceAndSlog t = new TimingsTraceAndSlog(); + t.traceBegin("ManagedServices.unbindUserServices" + user); + unbindServicesImpl(user, false /* allExceptUser */); + t.traceEnd(); + } + void unbindServicesImpl(int user, boolean allExceptUser) { + final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>(); synchronized (mMutex) { final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices(); for (ManagedServiceInfo info : removableBoundServices) { - if (info.userid != currentUser) { + if ((allExceptUser && (info.userid != user)) + || (!allExceptUser && (info.userid == user))) { Set<ComponentName> toUnbind = componentsToUnbind.get(info.userid, new ArraySet<>()); toUnbind.add(info.component); @@ -1422,7 +1434,6 @@ abstract public class ManagedServices { } } unbindFromServices(componentsToUnbind); - t.traceEnd(); } protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 802dfb182297..a3c71c2e0218 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -177,6 +177,7 @@ import android.app.compat.CompatChanges; import android.app.role.OnRoleHoldersChangedListener; import android.app.role.RoleManager; import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.companion.ICompanionDeviceManager; import android.compat.annotation.ChangeId; @@ -263,6 +264,7 @@ import android.telecom.TelecomManager; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.text.TextUtils; +import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; @@ -4159,7 +4161,7 @@ public class NotificationManagerService extends SystemService { String pkg) { checkCallerIsSystemOrSameApp(pkg); return mPreferencesHelper.getNotificationChannelGroups( - pkg, Binder.getCallingUid(), false, false, true); + pkg, Binder.getCallingUid(), false, false, true, true, null); } @Override @@ -4280,7 +4282,36 @@ public class NotificationManagerService extends SystemService { String pkg, int uid, boolean includeDeleted) { enforceSystemOrSystemUI("getNotificationChannelGroupsForPackage"); return mPreferencesHelper.getNotificationChannelGroups( - pkg, uid, includeDeleted, true, false); + pkg, uid, includeDeleted, true, false, true, null); + } + + @Override + public ParceledListSlice<NotificationChannelGroup> + getRecentBlockedNotificationChannelGroupsForPackage(String pkg, int uid) { + enforceSystemOrSystemUI("getRecentBlockedNotificationChannelGroupsForPackage"); + Set<String> recentlySentChannels = new HashSet<>(); + long now = System.currentTimeMillis(); + long startTime = now - (DateUtils.DAY_IN_MILLIS * 14); + UsageEvents events = mUsageStatsManagerInternal.queryEventsForUser( + UserHandle.getUserId(uid), startTime, now, UsageEvents.SHOW_ALL_EVENT_DATA); + // get all channelids that sent notifs in the past 2 weeks + if (events != null) { + UsageEvents.Event event = new UsageEvents.Event(); + while (events.hasNextEvent()) { + events.getNextEvent(event); + if (event.getEventType() == UsageEvents.Event.NOTIFICATION_INTERRUPTION) { + if (pkg.equals(event.mPackage)) { + String channelId = event.mNotificationChannelId; + if (channelId != null) { + recentlySentChannels.add(channelId); + } + } + } + } + } + + return mPreferencesHelper.getNotificationChannelGroups( + pkg, uid, false, true, false, true, recentlySentChannels); } @Override diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index b132a23b575b..de698d916cce 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -1459,9 +1459,9 @@ public class PreferencesHelper implements RankingConfig { } } - @Override public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg, - int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) { + int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty, + boolean includeBlocked, Set<String> activeChannelFilter) { Objects.requireNonNull(pkg); Map<String, NotificationChannelGroup> groups = new ArrayMap<>(); synchronized (mPackagePreferences) { @@ -1473,7 +1473,11 @@ public class PreferencesHelper implements RankingConfig { int N = r.channels.size(); for (int i = 0; i < N; i++) { final NotificationChannel nc = r.channels.valueAt(i); - if (includeDeleted || !nc.isDeleted()) { + boolean includeChannel = (includeDeleted || !nc.isDeleted()) + && (activeChannelFilter == null + || (includeBlocked && nc.getImportance() == IMPORTANCE_NONE) + || activeChannelFilter.contains(nc.getId())); + if (includeChannel) { if (nc.getGroup() != null) { if (r.groups.get(nc.getGroup()) != null) { NotificationChannelGroup ncg = groups.get(nc.getGroup()); @@ -1481,7 +1485,6 @@ public class PreferencesHelper implements RankingConfig { ncg = r.groups.get(nc.getGroup()).clone(); ncg.setChannels(new ArrayList<>()); groups.put(nc.getGroup(), ncg); - } ncg.addChannel(nc); } diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index fec359198e88..8df24c9911a6 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -40,8 +40,6 @@ public interface RankingConfig { int uid); void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group, boolean fromTargetApp, int callingUid, boolean isSystemOrSystemUi); - ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg, - int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty); boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromTargetApp, boolean hasDndAccess, int callingUid, boolean isSystemOrSystemUi); diff --git a/services/core/java/com/android/server/notification/TEST_MAPPING b/services/core/java/com/android/server/notification/TEST_MAPPING index 59b2bc1e4f73..7db2e8b1333f 100644 --- a/services/core/java/com/android/server/notification/TEST_MAPPING +++ b/services/core/java/com/android/server/notification/TEST_MAPPING @@ -13,7 +13,7 @@ "exclude-annotation": "org.junit.Ignore" }, { - "exclude-annotation": "android.platform.test.annotations.LargeTest" + "exclude-annotation": "androidx.test.filters.LargeTest" }, { "exclude-annotation": "androidx.test.filters.LargeTest" @@ -33,7 +33,7 @@ "exclude-annotation": "org.junit.Ignore" }, { - "exclude-annotation": "android.platform.test.annotations.LargeTest" + "exclude-annotation": "androidx.test.filters.LargeTest" }, { "exclude-annotation": "androidx.test.filters.LargeTest" diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 52eef47b3759..b9464d96a019 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -18,9 +18,6 @@ package com.android.server.om; import static android.app.AppGlobals.getPackageManager; import static android.content.Intent.ACTION_OVERLAY_CHANGED; -import static android.content.Intent.ACTION_PACKAGE_ADDED; -import static android.content.Intent.ACTION_PACKAGE_CHANGED; -import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.Intent.ACTION_USER_ADDED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_PACKAGE_NAME; @@ -31,10 +28,10 @@ import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISA import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED; import static android.content.om.OverlayManagerTransaction.Request.TYPE_UNREGISTER_FABRICATED; import static android.content.pm.PackageManager.SIGNATURE_MATCH; +import static android.os.Process.INVALID_UID; import static android.os.Trace.TRACE_TAG_RRO; import static android.os.Trace.traceBegin; import static android.os.Trace.traceEnd; - import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException; import android.annotation.NonNull; @@ -82,6 +79,7 @@ import android.util.EventLog; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.content.PackageMonitor; import com.android.internal.content.om.OverlayConfig; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; @@ -261,6 +259,8 @@ public final class OverlayManagerService extends SystemService { private final OverlayActorEnforcer mActorEnforcer; + private final PackageMonitor mPackageMonitor = new OverlayManagerPackageMonitor(); + private int mPrevStartedUserId = -1; public OverlayManagerService(@NonNull final Context context) { @@ -277,16 +277,9 @@ public final class OverlayManagerService extends SystemService { OverlayConfig.getSystemInstance(), getDefaultOverlayPackages()); mActorEnforcer = new OverlayActorEnforcer(mPackageManager); - HandlerThread packageReceiverThread = new HandlerThread(TAG); - packageReceiverThread.start(); - - final IntentFilter packageFilter = new IntentFilter(); - packageFilter.addAction(ACTION_PACKAGE_ADDED); - packageFilter.addAction(ACTION_PACKAGE_CHANGED); - packageFilter.addAction(ACTION_PACKAGE_REMOVED); - packageFilter.addDataScheme("package"); - getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, - packageFilter, null, packageReceiverThread.getThreadHandler()); + HandlerThread packageMonitorThread = new HandlerThread(TAG); + packageMonitorThread.start(); + mPackageMonitor.register(context, packageMonitorThread.getLooper(), true); final IntentFilter userFilter = new IntentFilter(); userFilter.addAction(ACTION_USER_ADDED); @@ -372,166 +365,171 @@ public final class OverlayManagerService extends SystemService { return defaultPackages.toArray(new String[defaultPackages.size()]); } - private final class PackageReceiver extends BroadcastReceiver { + private final class OverlayManagerPackageMonitor extends PackageMonitor { + @Override - public void onReceive(@NonNull final Context context, @NonNull final Intent intent) { - final String action = intent.getAction(); - if (action == null) { - Slog.e(TAG, "Cannot handle package broadcast with null action"); - return; - } - final Uri data = intent.getData(); - if (data == null) { - Slog.e(TAG, "Cannot handle package broadcast with null data"); - return; - } - final String packageName = data.getSchemeSpecificPart(); + public void onPackageAppearedWithExtras(String packageName, Bundle extras) { + handlePackageAdd(packageName, extras); + } - final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); - final boolean systemUpdateUninstall = - intent.getBooleanExtra(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, false); + @Override + public void onPackageChangedWithExtras(String packageName, Bundle extras) { + handlePackageChange(packageName, extras); + } - final int[] userIds; - final int extraUid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL); - if (extraUid == UserHandle.USER_NULL) { - userIds = mUserManager.getUserIds(); - } else { - userIds = new int[] { UserHandle.getUserId(extraUid) }; - } + @Override + public void onPackageDisappearedWithExtras(String packageName, Bundle extras) { + handlePackageRemove(packageName, extras); + } + } - switch (action) { - case ACTION_PACKAGE_ADDED: - if (replacing) { - onPackageReplaced(packageName, userIds); - } else { - onPackageAdded(packageName, userIds); - } - break; - case ACTION_PACKAGE_CHANGED: - // ignore the intent if it was sent by the package manager as a result of the - // overlay manager having sent ACTION_OVERLAY_CHANGED - if (!ACTION_OVERLAY_CHANGED.equals(intent.getStringExtra(EXTRA_REASON))) { - onPackageChanged(packageName, userIds); - } - break; - case ACTION_PACKAGE_REMOVED: - if (replacing) { - onPackageReplacing(packageName, systemUpdateUninstall, userIds); - } else { - onPackageRemoved(packageName, userIds); - } - break; - default: - // do nothing - break; - } + private int[] getUserIds(int uid) { + final int[] userIds; + if (uid == INVALID_UID) { + userIds = mUserManager.getUserIds(); + } else { + userIds = new int[] { UserHandle.getUserId(uid) }; } + return userIds; + } - private void onPackageAdded(@NonNull final String packageName, - @NonNull final int[] userIds) { - try { - traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName); - for (final int userId : userIds) { - synchronized (mLock) { - var packageState = mPackageManager.onPackageAdded(packageName, userId); - if (packageState != null && !mPackageManager.isInstantApp(packageName, - userId)) { - try { - updateTargetPackagesLocked( - mImpl.onPackageAdded(packageName, userId)); - } catch (OperationFailedException e) { - Slog.e(TAG, "onPackageAdded internal error", e); - } + private void handlePackageAdd(String packageName, Bundle extras) { + final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false); + final int uid = extras.getInt(Intent.EXTRA_UID, 0); + final int[] userIds = getUserIds(uid); + if (replacing) { + onPackageReplaced(packageName, userIds); + } else { + onPackageAdded(packageName, userIds); + } + } + + private void handlePackageChange(String packageName, Bundle extras) { + final int uid = extras.getInt(Intent.EXTRA_UID, 0); + final int[] userIds = getUserIds(uid); + if (!ACTION_OVERLAY_CHANGED.equals(extras.getString(EXTRA_REASON))) { + onPackageChanged(packageName, userIds); + } + } + + private void handlePackageRemove(String packageName, Bundle extras) { + final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false); + final boolean systemUpdateUninstall = + extras.getBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, false); + final int uid = extras.getInt(Intent.EXTRA_UID, 0); + final int[] userIds = getUserIds(uid); + + if (replacing) { + onPackageReplacing(packageName, systemUpdateUninstall, userIds); + } else { + onPackageRemoved(packageName, userIds); + } + } + + private void onPackageAdded(@NonNull final String packageName, + @NonNull final int[] userIds) { + try { + traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName); + for (final int userId : userIds) { + synchronized (mLock) { + var packageState = mPackageManager.onPackageAdded(packageName, userId); + if (packageState != null && !mPackageManager.isInstantApp(packageName, + userId)) { + try { + updateTargetPackagesLocked( + mImpl.onPackageAdded(packageName, userId)); + } catch (OperationFailedException e) { + Slog.e(TAG, "onPackageAdded internal error", e); } } } - } finally { - traceEnd(TRACE_TAG_RRO); } + } finally { + traceEnd(TRACE_TAG_RRO); } + } - private void onPackageChanged(@NonNull final String packageName, - @NonNull final int[] userIds) { - try { - traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName); - for (int userId : userIds) { - synchronized (mLock) { - var packageState = mPackageManager.onPackageUpdated(packageName, userId); - if (packageState != null && !mPackageManager.isInstantApp(packageName, - userId)) { - try { - updateTargetPackagesLocked( - mImpl.onPackageChanged(packageName, userId)); - } catch (OperationFailedException e) { - Slog.e(TAG, "onPackageChanged internal error", e); - } + private void onPackageChanged(@NonNull final String packageName, + @NonNull final int[] userIds) { + try { + traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName); + for (int userId : userIds) { + synchronized (mLock) { + var packageState = mPackageManager.onPackageUpdated(packageName, userId); + if (packageState != null && !mPackageManager.isInstantApp(packageName, + userId)) { + try { + updateTargetPackagesLocked( + mImpl.onPackageChanged(packageName, userId)); + } catch (OperationFailedException e) { + Slog.e(TAG, "onPackageChanged internal error", e); } } } - } finally { - traceEnd(TRACE_TAG_RRO); } + } finally { + traceEnd(TRACE_TAG_RRO); } + } - private void onPackageReplacing(@NonNull final String packageName, - boolean systemUpdateUninstall, @NonNull final int[] userIds) { - try { - traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName); - for (int userId : userIds) { - synchronized (mLock) { - var packageState = mPackageManager.onPackageUpdated(packageName, userId); - if (packageState != null && !mPackageManager.isInstantApp(packageName, - userId)) { - try { - updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName, - systemUpdateUninstall, userId)); - } catch (OperationFailedException e) { - Slog.e(TAG, "onPackageReplacing internal error", e); - } + private void onPackageReplacing(@NonNull final String packageName, + boolean systemUpdateUninstall, @NonNull final int[] userIds) { + try { + traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName); + for (int userId : userIds) { + synchronized (mLock) { + var packageState = mPackageManager.onPackageUpdated(packageName, userId); + if (packageState != null && !mPackageManager.isInstantApp(packageName, + userId)) { + try { + updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName, + systemUpdateUninstall, userId)); + } catch (OperationFailedException e) { + Slog.e(TAG, "onPackageReplacing internal error", e); } } } - } finally { - traceEnd(TRACE_TAG_RRO); } + } finally { + traceEnd(TRACE_TAG_RRO); } + } - private void onPackageReplaced(@NonNull final String packageName, - @NonNull final int[] userIds) { - try { - traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName); - for (int userId : userIds) { - synchronized (mLock) { - var packageState = mPackageManager.onPackageUpdated(packageName, userId); - if (packageState != null && !mPackageManager.isInstantApp(packageName, - userId)) { - try { - updateTargetPackagesLocked( - mImpl.onPackageReplaced(packageName, userId)); - } catch (OperationFailedException e) { - Slog.e(TAG, "onPackageReplaced internal error", e); - } + private void onPackageReplaced(@NonNull final String packageName, + @NonNull final int[] userIds) { + try { + traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName); + for (int userId : userIds) { + synchronized (mLock) { + var packageState = mPackageManager.onPackageUpdated(packageName, userId); + if (packageState != null && !mPackageManager.isInstantApp(packageName, + userId)) { + try { + updateTargetPackagesLocked( + mImpl.onPackageReplaced(packageName, userId)); + } catch (OperationFailedException e) { + Slog.e(TAG, "onPackageReplaced internal error", e); } } } - } finally { - traceEnd(TRACE_TAG_RRO); } + } finally { + traceEnd(TRACE_TAG_RRO); } + } - private void onPackageRemoved(@NonNull final String packageName, - @NonNull final int[] userIds) { - try { - traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName); - for (int userId : userIds) { - synchronized (mLock) { - mPackageManager.onPackageRemoved(packageName, userId); - updateTargetPackagesLocked(mImpl.onPackageRemoved(packageName, userId)); - } + private void onPackageRemoved(@NonNull final String packageName, + @NonNull final int[] userIds) { + try { + traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName); + for (int userId : userIds) { + synchronized (mLock) { + mPackageManager.onPackageRemoved(packageName, userId); + updateTargetPackagesLocked(mImpl.onPackageRemoved(packageName, userId)); } - } finally { - traceEnd(TRACE_TAG_RRO); } + } finally { + traceEnd(TRACE_TAG_RRO); } } @@ -684,7 +682,7 @@ public final class OverlayManagerService extends SystemService { synchronized (mLock) { try { mImpl.setEnabledExclusive( - overlay, false /* withinCategory */, realUserId) + overlay, false /* withinCategory */, realUserId) .ifPresent( OverlayManagerService.this::updateTargetPackagesLocked); return true; diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java index 9a69d77c61f0..e367609e89b6 100644 --- a/services/core/java/com/android/server/pm/BroadcastHelper.java +++ b/services/core/java/com/android/server/pm/BroadcastHelper.java @@ -17,9 +17,14 @@ package com.android.server.pm; import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED; +import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED; import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED; +import static android.os.Process.SYSTEM_UID; import static android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED; + +import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP; import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; +import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY; import static com.android.server.pm.PackageManagerService.PACKAGE_SCHEME; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.pm.PackageManagerService.TAG; @@ -28,6 +33,7 @@ import android.Manifest; import android.annotation.AppIdInt; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.BroadcastOptions; @@ -38,12 +44,18 @@ import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.net.Uri; import android.os.Bundle; +import android.os.Handler; import android.os.PowerExemptionManager; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.storage.StorageManager; +import android.os.storage.VolumeInfo; import android.provider.DeviceConfig; +import android.stats.storage.StorageEnums; import android.util.IntArray; import android.util.Log; import android.util.Pair; @@ -51,10 +63,15 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FrameworkStatsLog; +import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.PackageUserStateInternal; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.function.BiFunction; -import java.util.function.Supplier; /** * Helper class to send broadcasts for various situations. @@ -70,14 +87,20 @@ public final class BroadcastHelper { private final UserManagerInternal mUmInternal; private final ActivityManagerInternal mAmInternal; private final Context mContext; + private final Handler mHandler; + private final PackageMonitorCallbackHelper mPackageMonitorCallbackHelper; + private final AppsFilterSnapshot mAppsFilter; BroadcastHelper(PackageManagerServiceInjector injector) { mUmInternal = injector.getUserManagerInternal(); mAmInternal = injector.getActivityManagerInternal(); mContext = injector.getContext(); + mHandler = injector.getHandler(); + mPackageMonitorCallbackHelper = injector.getPackageMonitorCallbackHelper(); + mAppsFilter = injector.getAppsFilter(); } - public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, + void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, final int flags, final String targetPkg, final IIntentReceiver finishedReceiver, final int[] userIds, int[] instantUserIds, @Nullable SparseArray<int[]> broadcastAllowList, @@ -114,9 +137,16 @@ public final class BroadcastHelper { * the system and applications allowed to see instant applications to receive package * lifecycle events for instant applications. */ - public void doSendBroadcast(String action, String pkg, Bundle extras, - int flags, String targetPkg, IIntentReceiver finishedReceiver, - int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastAllowList, + private void doSendBroadcast( + @NonNull String action, + @Nullable String pkg, + @Nullable Bundle extras, + int flags, + @Nullable String targetPkg, + @Nullable IIntentReceiver finishedReceiver, + @NonNull int[] userIds, + boolean isInstantApp, + @Nullable SparseArray<int[]> broadcastAllowList, @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, @Nullable Bundle bOptions) { for (int userId : userIds) { @@ -166,9 +196,11 @@ public final class BroadcastHelper { } } - public void sendResourcesChangedBroadcast(@NonNull Supplier<Computer> snapshotComputer, - boolean mediaStatus, boolean replacing, @NonNull String[] pkgNames, - @NonNull int[] uids) { + void sendResourcesChangedBroadcast(@NonNull Computer snapshot, + boolean mediaStatus, + boolean replacing, + @NonNull String[] pkgNames, + @NonNull int[] uids) { if (ArrayUtils.isEmpty(pkgNames) || ArrayUtils.isEmpty(uids)) { return; } @@ -184,7 +216,7 @@ public final class BroadcastHelper { null /* targetPkg */, null /* finishedReceiver */, null /* userIds */, null /* instantUserIds */, null /* broadcastAllowList */, (callingUid, intentExtras) -> filterExtrasChangedPackageList( - snapshotComputer.get(), callingUid, intentExtras), + snapshot, callingUid, intentExtras), null /* bOptions */); } @@ -193,8 +225,9 @@ public final class BroadcastHelper { * automatically without needing an explicit launch. * Send it a LOCKED_BOOT_COMPLETED/BOOT_COMPLETED if it would ordinarily have gotten ones. */ - public void sendBootCompletedBroadcastToSystemApp( - String packageName, boolean includeStopped, int userId) { + private void sendBootCompletedBroadcastToSystemApp(@NonNull String packageName, + boolean includeStopped, + int userId) { // If user is not running, the app didn't miss any broadcast if (!mUmInternal.isUserRunning(userId)) { return; @@ -229,7 +262,7 @@ public final class BroadcastHelper { } } - public @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions( + private @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions( @PowerExemptionManager.ReasonCode int reasonCode) { long duration = 10_000; if (mAmInternal != null) { @@ -242,9 +275,14 @@ public final class BroadcastHelper { return bOptions; } - public void sendPackageChangedBroadcast(String packageName, boolean dontKillApp, - ArrayList<String> componentNames, int packageUid, String reason, - int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList) { + private void sendPackageChangedBroadcast(@NonNull String packageName, + boolean dontKillApp, + @NonNull ArrayList<String> componentNames, + int packageUid, + @Nullable String reason, + @Nullable int[] userIds, + @Nullable int[] instantUserIds, + @Nullable SparseArray<int[]> broadcastAllowList) { if (DEBUG_INSTALL) { Log.v(TAG, "Sending package changed: package=" + packageName + " components=" + componentNames); @@ -269,7 +307,7 @@ public final class BroadcastHelper { null /* bOptions */); } - public static void sendDeviceCustomizationReadyBroadcast() { + static void sendDeviceCustomizationReadyBroadcast() { final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY); intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); final IActivityManager am = ActivityManager.getService(); @@ -285,15 +323,23 @@ public final class BroadcastHelper { } } - public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId, - int launcherUid, @Nullable ComponentName launcherComponent, - @Nullable String appPredictionServicePackage) { + void sendSessionCommitBroadcast(@NonNull Computer snapshot, + @NonNull PackageInstaller.SessionInfo sessionInfo, + int userId, + @Nullable String appPredictionServicePackage) { + UserManagerService ums = UserManagerService.getInstance(); + if (ums == null || sessionInfo.isStaged()) { + return; + } + final UserInfo parent = ums.getProfileParent(userId); + final int launcherUserId = (parent != null) ? parent.id : userId; + final ComponentName launcherComponent = snapshot.getDefaultHomeActivity(launcherUserId); if (launcherComponent != null) { Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED) .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo) .putExtra(Intent.EXTRA_USER, UserHandle.of(userId)) .setPackage(launcherComponent.getPackageName()); - mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid)); + mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUserId)); } // TODO(b/122900055) Change/Remove this and replace with new permission role. if (appPredictionServicePackage != null) { @@ -301,30 +347,278 @@ public final class BroadcastHelper { .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo) .putExtra(Intent.EXTRA_USER, UserHandle.of(userId)) .setPackage(appPredictionServicePackage); - mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUid)); + mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUserId)); } } - public void sendPreferredActivityChangedBroadcast(int userId) { - final IActivityManager am = ActivityManager.getService(); - if (am == null) { - return; + void sendPreferredActivityChangedBroadcast(int userId) { + mHandler.post(() -> { + final IActivityManager am = ActivityManager.getService(); + if (am == null) { + return; + } + + final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + try { + am.broadcastIntentWithFeature(null, null, intent, null, null, + 0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE, + null, false, false, userId); + } catch (RemoteException e) { + } + }); + } + + void sendPostInstallBroadcasts(@NonNull Computer snapshot, + @NonNull InstallRequest request, + @NonNull String packageName, + @NonNull String requiredPermissionControllerPackage, + @NonNull String[] requiredVerifierPackages, + @NonNull String requiredInstallerPackage, + @NonNull PackageSender packageSender, + boolean isLaunchedForRestore, + boolean isKillApp, + boolean isUpdate, + boolean isArchived) { + // Send the removed broadcasts + if (request.getRemovedInfo() != null) { + if (request.getRemovedInfo().mIsExternal) { + if (DEBUG_INSTALL) { + Slog.i(TAG, "upgrading pkg " + request.getRemovedInfo().mRemovedPackage + + " is ASEC-hosted -> UNAVAILABLE"); + } + final String[] pkgNames = new String[]{ + request.getRemovedInfo().mRemovedPackage}; + final int[] uids = new int[]{request.getRemovedInfo().mUid}; + notifyResourcesChanged( + false /* mediaStatus */, true /* replacing */, pkgNames, uids); + sendResourcesChangedBroadcast( + snapshot, false /* mediaStatus */, true /* replacing */, pkgNames, uids); + } + sendPackageRemovedBroadcasts( + request.getRemovedInfo(), packageSender, isKillApp, false /*removedBySystem*/, + false /*isArchived*/); } - final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED); - intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - try { - am.broadcastIntentWithFeature(null, null, intent, null, null, - 0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE, - null, false, false, userId); - } catch (RemoteException e) { + final int[] firstUserIds = request.getFirstTimeBroadcastUserIds(); + final int[] firstInstantUserIds = request.getFirstTimeBroadcastInstantUserIds(); + final int[] updateUserIds = request.getUpdateBroadcastUserIds(); + final int[] instantUserIds = request.getUpdateBroadcastInstantUserIds(); + + final String installerPackageName = + request.getInstallerPackageName() != null + ? request.getInstallerPackageName() + : request.getRemovedInfo() != null + ? request.getRemovedInfo().mInstallerPackageName + : null; + + Bundle extras = new Bundle(); + extras.putInt(Intent.EXTRA_UID, request.getAppId()); + if (isUpdate) { + extras.putBoolean(Intent.EXTRA_REPLACING, true); + } + if (isArchived) { + extras.putBoolean(Intent.EXTRA_ARCHIVAL, true); + } + extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, request.getDataLoaderType()); + + final String staticSharedLibraryName = request.getPkg().getStaticSharedLibraryName(); + // If a package is a static shared library, then only the installer of the package + // should get the broadcast. + if (installerPackageName != null && staticSharedLibraryName != null) { + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName, + extras, 0 /*flags*/, + installerPackageName, null /*finishedReceiver*/, + request.getNewUsers(), null /* instantUserIds*/, + null /* broadcastAllowList */, null); + } + + // Send installed broadcasts if the package is not a static shared lib. + if (staticSharedLibraryName == null) { + // Send PACKAGE_ADDED broadcast for users that see the package for the first time + // sendPackageAddedForNewUsers also deals with system apps + final int appId = UserHandle.getAppId(request.getAppId()); + final boolean isSystem = request.isInstallSystem(); + final boolean isVirtualPreload = + ((request.getInstallFlags() & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); + sendPackageAddedForNewUsers(snapshot, packageName, + isSystem || isVirtualPreload, + isVirtualPreload /*startReceiver*/, appId, + firstUserIds, firstInstantUserIds, isArchived, request.getDataLoaderType()); + + // Send PACKAGE_ADDED broadcast for users that don't see + // the package for the first time + + // Send to all running apps. + final SparseArray<int[]> newBroadcastAllowList = + mAppsFilter.getVisibilityAllowList(snapshot, + snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID), + updateUserIds, snapshot.getPackageStates()); + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName, + extras, 0 /*flags*/, + null /*targetPackage*/, null /*finishedReceiver*/, + updateUserIds, instantUserIds, newBroadcastAllowList, null); + // Send to the installer, even if it's not running. + if (installerPackageName != null) { + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName, + extras, 0 /*flags*/, + installerPackageName, null /*finishedReceiver*/, + updateUserIds, instantUserIds, null /* broadcastAllowList */, null); + } + // Send to PermissionController for all update users, even if it may not be running + // for some users + if (isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) { + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName, + extras, 0 /*flags*/, + requiredPermissionControllerPackage, null /*finishedReceiver*/, + updateUserIds, instantUserIds, null /* broadcastAllowList */, null); + } + // Notify required verifier(s) that are not the installer of record for the package. + for (String verifierPackageName : requiredVerifierPackages) { + if (verifierPackageName != null && !verifierPackageName.equals( + installerPackageName)) { + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, + packageName, + extras, 0 /*flags*/, + verifierPackageName, null /*finishedReceiver*/, + updateUserIds, instantUserIds, null /* broadcastAllowList */, + null); + } + } + // If package installer is defined, notify package installer about new + // app installed + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName, + extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/, + requiredInstallerPackage, null /*finishedReceiver*/, + firstUserIds, instantUserIds, null /* broadcastAllowList */, null); + + // Send replaced for users that don't see the package for the first time + if (isUpdate) { + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, + packageName, extras, 0 /*flags*/, + null /*targetPackage*/, null /*finishedReceiver*/, + updateUserIds, instantUserIds, + request.getRemovedInfo().mBroadcastAllowList, null); + if (installerPackageName != null) { + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, packageName, + extras, 0 /*flags*/, + installerPackageName, null /*finishedReceiver*/, + updateUserIds, instantUserIds, null /*broadcastAllowList*/, + null); + } + for (String verifierPackageName : requiredVerifierPackages) { + if (verifierPackageName != null && !verifierPackageName.equals( + installerPackageName)) { + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, + packageName, extras, 0 /*flags*/, verifierPackageName, + null /*finishedReceiver*/, updateUserIds, instantUserIds, + null /*broadcastAllowList*/, null); + } + } + sendPackageBroadcastAndNotify(Intent.ACTION_MY_PACKAGE_REPLACED, + null /*package*/, null /*extras*/, 0 /*flags*/, + packageName /*targetPackage*/, + null /*finishedReceiver*/, updateUserIds, instantUserIds, + null /*broadcastAllowList*/, + getTemporaryAppAllowlistBroadcastOptions( + REASON_PACKAGE_REPLACED).toBundle()); + } else if (isLaunchedForRestore && !request.isInstallSystem()) { + // First-install and we did a restore, so we're responsible for the + // first-launch broadcast. + if (DEBUG_BACKUP) { + Slog.i(TAG, "Post-restore of " + packageName + + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds)); + } + sendFirstLaunchBroadcast(packageName, installerPackageName, + firstUserIds, firstInstantUserIds); + } + + // Send broadcast package appeared if external for all users + if (request.getPkg().isExternalStorage()) { + if (!isUpdate) { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + VolumeInfo volume = + storage.findVolumeByUuid( + StorageManager.convert( + request.getPkg().getVolumeUuid()).toString()); + int packageExternalStorageType = + PackageManagerServiceUtils.getPackageExternalStorageType(volume, + /* isExternalStorage */ true); + // If the package was installed externally, log it. + if (packageExternalStorageType != StorageEnums.UNKNOWN) { + FrameworkStatsLog.write( + FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED, + packageExternalStorageType, packageName); + } + } + if (DEBUG_INSTALL) { + Slog.i(TAG, "upgrading pkg " + packageName + " is external"); + } + if (!isArchived) { + final String[] pkgNames = new String[]{packageName}; + final int[] uids = new int[]{request.getPkg().getUid()}; + sendResourcesChangedBroadcast(snapshot, + true /* mediaStatus */, true /* replacing */, pkgNames, uids); + notifyResourcesChanged(true /* mediaStatus */, + true /* replacing */, pkgNames, uids); + } + } + } else { // if static shared lib + final ArrayList<AndroidPackage> libraryConsumers = request.getLibraryConsumers(); + if (!ArrayUtils.isEmpty(libraryConsumers)) { + // No need to kill consumers if it's installation of new version static shared lib. + final boolean dontKillApp = !isUpdate; + for (int i = 0; i < libraryConsumers.size(); i++) { + AndroidPackage pkg = libraryConsumers.get(i); + // send broadcast that all consumers of the static shared library have changed + sendPackageChangedBroadcast(snapshot, pkg.getPackageName(), + dontKillApp, + new ArrayList<>(Collections.singletonList(pkg.getPackageName())), + pkg.getUid(), null); + } + } } } - public void sendPackageAddedForNewUsers(String packageName, @AppIdInt int appId, int[] userIds, - int[] instantUserIds, boolean isArchived, int dataLoaderType, - SparseArray<int[]> broadcastAllowlist) { + private void sendPackageAddedForNewUsers(@NonNull Computer snapshot, + @NonNull String packageName, + boolean sendBootCompleted, + boolean includeStopped, + @AppIdInt int appId, + int[] userIds, + int[] instantUserIds, + boolean isArchived, + int dataLoaderType) { + if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) { + return; + } + SparseArray<int[]> broadcastAllowList = mAppsFilter.getVisibilityAllowList(snapshot, + snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID), + userIds, snapshot.getPackageStates()); + mHandler.post( + () -> sendPackageAddedForNewUsers(packageName, appId, userIds, + instantUserIds, isArchived, dataLoaderType, broadcastAllowList)); + mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(packageName, appId, userIds, + instantUserIds, isArchived, dataLoaderType, broadcastAllowList, mHandler); + if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) { + mHandler.post(() -> { + for (int userId : userIds) { + sendBootCompletedBroadcastToSystemApp( + packageName, includeStopped, userId); + } + } + ); + } + } + + private void sendPackageAddedForNewUsers(@NonNull String packageName, + @AppIdInt int appId, + int[] userIds, + int[] instantUserIds, + boolean isArchived, + int dataLoaderType, + @NonNull SparseArray<int[]> broadcastAllowlist) { Bundle extras = new Bundle(1); // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast final int uid = UserHandle.getUid( @@ -349,7 +643,30 @@ public final class BroadcastHelper { } } - public void sendFirstLaunchBroadcast(String pkgName, String installerPkg, + void sendPackageAddedForUser(@NonNull Computer snapshot, + @NonNull String packageName, + @NonNull PackageStateInternal packageState, + int userId, + boolean isArchived, + int dataLoaderType, + @Nullable String appPredictionServicePackage) { + final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId); + final boolean isSystem = packageState.isSystem(); + final boolean isInstantApp = userState.isInstantApp(); + final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; + final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; + sendPackageAddedForNewUsers(snapshot, packageName, isSystem /*sendBootCompleted*/, + false /*startReceiver*/, packageState.getAppId(), userIds, instantUserIds, + isArchived, dataLoaderType); + + // Send a session commit broadcast + final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo(); + info.installReason = userState.getInstallReason(); + info.appPackageName = packageName; + sendSessionCommitBroadcast(snapshot, info, userId, appPredictionServicePackage); + } + + void sendFirstLaunchBroadcast(String pkgName, String installerPkg, int[] userIds, int[] instantUserIds) { sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0, installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */, @@ -366,7 +683,7 @@ public final class BroadcastHelper { * access all the packages in the extras. */ @Nullable - public static Bundle filterExtrasChangedPackageList(@NonNull Computer snapshot, int callingUid, + private static Bundle filterExtrasChangedPackageList(@NonNull Computer snapshot, int callingUid, @NonNull Bundle extras) { if (UserHandle.isCore(callingUid)) { // see all @@ -392,7 +709,7 @@ public final class BroadcastHelper { } /** Returns whether the Safety Label Change notification, a privacy feature, is enabled. */ - public static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) { + private static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) { PackageManager packageManager = context.getPackageManager(); return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, true) @@ -424,4 +741,323 @@ public final class BroadcastHelper { pkgList.size() > 0 ? pkgList.toArray(new String[pkgList.size()]) : null, uidList != null && uidList.size() > 0 ? uidList.toArray() : null); } + + void sendApplicationHiddenForUser(@NonNull String packageName, + @NonNull PackageStateInternal packageState, + int userId, + @NonNull PackageSender packageSender) { + final PackageRemovedInfo info = new PackageRemovedInfo(); + info.mRemovedPackage = packageName; + info.mInstallerPackageName = packageState.getInstallSource().mInstallerPackageName; + info.mRemovedUsers = new int[] {userId}; + info.mBroadcastUsers = new int[] {userId}; + info.mUid = UserHandle.getUid(userId, packageState.getAppId()); + info.mRemovedPackageVersionCode = packageState.getVersionCode(); + sendPackageRemovedBroadcasts(info, packageSender, true /*killApp*/, + false /*removedBySystem*/, false /*isArchived*/); + } + + void sendPackageChangedBroadcast(@NonNull Computer snapshot, + @NonNull String packageName, + boolean dontKillApp, + @NonNull ArrayList<String> componentNames, + int packageUid, + @NonNull String reason) { + PackageStateInternal setting = snapshot.getPackageStateInternal(packageName, + Process.SYSTEM_UID); + if (setting == null) { + return; + } + final int userId = UserHandle.getUserId(packageUid); + final boolean isInstantApp = + snapshot.isInstantAppInternal(packageName, userId, Process.SYSTEM_UID); + final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; + final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; + final SparseArray<int[]> broadcastAllowList = + isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds); + mHandler.post(() -> sendPackageChangedBroadcast( + packageName, dontKillApp, componentNames, packageUid, reason, userIds, + instantUserIds, broadcastAllowList)); + mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames, + packageUid, reason, userIds, instantUserIds, broadcastAllowList, mHandler); + } + + private void sendPackageBroadcastAndNotify(@NonNull String action, + @NonNull String pkg, + @NonNull Bundle extras, + int flags, + @Nullable String targetPkg, + @Nullable IIntentReceiver finishedReceiver, + @NonNull int[] userIds, + @NonNull int[] instantUserIds, + @Nullable SparseArray<int[]> broadcastAllowList, + @Nullable Bundle bOptions) { + mHandler.post(() -> sendPackageBroadcast(action, pkg, extras, flags, + targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList, + null /* filterExtrasForReceiver */, bOptions)); + if (targetPkg == null) { + // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called + // many times to different targets, e.g. installer app, permission controller, other + // registered apps. We should filter it to avoid calling back many times for the same + // action. When the targetPkg is set, it sends the broadcast to specific app, e.g. + // installer app or null for registered apps. The callback only need to send back to the + // registered apps so we check the null condition here. + notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList); + } + } + + void sendSystemPackageUpdatedBroadcasts(@NonNull PackageRemovedInfo packageRemovedInfo) { + if (!packageRemovedInfo.mIsRemovedPackageSystemUpdate) { + return; + } + + final String removedPackage = packageRemovedInfo.mRemovedPackage; + final int removedAppId = packageRemovedInfo.mRemovedAppId; + final int uid = packageRemovedInfo.mUid; + final String installerPackageName = packageRemovedInfo.mInstallerPackageName; + final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList; + + Bundle extras = new Bundle(2); + extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid); + extras.putBoolean(Intent.EXTRA_REPLACING, true); + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras, + 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null); + + if (installerPackageName != null) { + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, + removedPackage, extras, 0 /*flags*/, + installerPackageName, null, null, null, null /* broadcastAllowList */, + null); + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, + removedPackage, extras, 0 /*flags*/, + installerPackageName, null, null, null, null /* broadcastAllowList */, + null); + } + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, removedPackage, + extras, 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null); + sendPackageBroadcastAndNotify(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0, + removedPackage, null, null, null, null /* broadcastAllowList */, + getTemporaryBroadcastOptionsForSystemPackageUpdate(REASON_PACKAGE_REPLACED) + .toBundle()); + } + + @SuppressLint("AndroidFrameworkRequiresPermission") + private @NonNull BroadcastOptions getTemporaryBroadcastOptionsForSystemPackageUpdate( + @PowerExemptionManager.ReasonCode int reasonCode) { + long duration = 10_000; + if (mAmInternal != null) { + duration = mAmInternal.getBootTimeTempAllowListDuration(); + } + final BroadcastOptions bOptions = BroadcastOptions.makeBasic(); + bOptions.setTemporaryAppAllowlist(duration, + TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + reasonCode, ""); + return bOptions; + } + + + void sendPackageRemovedBroadcasts( + @NonNull PackageRemovedInfo packageRemovedInfo, + @NonNull PackageSender packageSender, + boolean killApp, + boolean removedBySystem, + boolean isArchived) { + final String removedPackage = packageRemovedInfo.mRemovedPackage; + final int removedAppId = packageRemovedInfo.mRemovedAppId; + final int uid = packageRemovedInfo.mUid; + final String installerPackageName = packageRemovedInfo.mInstallerPackageName; + final int[] broadcastUserIds = packageRemovedInfo.mBroadcastUsers; + final int[] instantUserIds = packageRemovedInfo.mInstantUserIds; + final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList; + final boolean dataRemoved = packageRemovedInfo.mDataRemoved; + final boolean isUpdate = packageRemovedInfo.mIsUpdate; + final boolean isRemovedPackageSystemUpdate = + packageRemovedInfo.mIsRemovedPackageSystemUpdate; + final boolean isRemovedForAllUsers = packageRemovedInfo.mRemovedForAllUsers; + final boolean isStaticSharedLib = packageRemovedInfo.mIsStaticSharedLib; + + Bundle extras = new Bundle(); + final int removedUid = removedAppId >= 0 ? removedAppId : uid; + extras.putInt(Intent.EXTRA_UID, removedUid); + extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved); + extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, isRemovedPackageSystemUpdate); + extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp); + extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem); + final boolean isReplace = isUpdate || isRemovedPackageSystemUpdate; + if (isReplace || isArchived) { + extras.putBoolean(Intent.EXTRA_REPLACING, true); + } + if (isArchived) { + extras.putBoolean(Intent.EXTRA_ARCHIVAL, true); + } + extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, isRemovedForAllUsers); + + // Send PACKAGE_REMOVED broadcast to the respective installer. + if (removedPackage != null && installerPackageName != null) { + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED, + removedPackage, extras, 0 /*flags*/, + installerPackageName, null, broadcastUserIds, instantUserIds, null, null); + } + if (isStaticSharedLib) { + // When uninstalling static shared libraries, only the package's installer needs to be + // sent a PACKAGE_REMOVED broadcast. There are no other intended recipients. + return; + } + if (removedPackage != null) { + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED, + removedPackage, extras, 0, null /*targetPackage*/, null, + broadcastUserIds, instantUserIds, broadcastAllowList, null); + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED_INTERNAL, + removedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME, + null /*finishedReceiver*/, broadcastUserIds, instantUserIds, + broadcastAllowList, null /*bOptions*/); + if (dataRemoved && !isRemovedPackageSystemUpdate) { + sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_FULLY_REMOVED, + removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null, + null, broadcastUserIds, instantUserIds, broadcastAllowList, null); + packageSender.notifyPackageRemoved(removedPackage, removedUid); + } + } + if (removedAppId >= 0) { + // If a system app's updates are uninstalled the UID is not actually removed. Some + // services need to know the package name affected. + if (isReplace) { + extras.putString(Intent.EXTRA_PACKAGE_NAME, removedPackage); + } + + sendPackageBroadcastAndNotify(Intent.ACTION_UID_REMOVED, + null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, + null, null, broadcastUserIds, instantUserIds, broadcastAllowList, null); + } + } + + /** + * Send broadcast intents for packages suspension changes. + * + * @param intent The action name of the suspension intent. + * @param pkgList The names of packages which have suspension changes. + * @param uidList The uids of packages which have suspension changes. + * @param userId The user where packages reside. + */ + void sendPackagesSuspendedOrUnsuspendedForUser(@NonNull Computer snapshot, + @NonNull String intent, + @NonNull String[] pkgList, + @NonNull int[] uidList, + boolean quarantined, + int userId) { + final Bundle extras = new Bundle(3); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); + if (quarantined) { + extras.putBoolean(Intent.EXTRA_QUARANTINED, true); + } + final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND; + final Bundle options = new BroadcastOptions() + .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) + .toBundle(); + mHandler.post(() -> sendPackageBroadcast(intent, null /* pkg */, + extras, flags, null /* targetPkg */, null /* finishedReceiver */, + new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */, + (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList( + snapshot, callingUid, intentExtras), + options)); + notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId}, + null /* instantUserIds */, null /* broadcastAllowList */); + } + + void sendMyPackageSuspendedOrUnsuspended(@NonNull Computer snapshot, + @NonNull String[] affectedPackages, + boolean suspended, + int userId) { + final String action = suspended + ? Intent.ACTION_MY_PACKAGE_SUSPENDED + : Intent.ACTION_MY_PACKAGE_UNSUSPENDED; + mHandler.post(() -> { + final IActivityManager am = ActivityManager.getService(); + if (am == null) { + Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ " + + (suspended ? "" : "UN") + "SUSPENDED broadcasts"); + return; + } + final int[] targetUserIds = new int[] {userId}; + for (String packageName : affectedPackages) { + final Bundle appExtras = suspended + ? SuspendPackageHelper.getSuspendedPackageAppExtras( + snapshot, packageName, userId, SYSTEM_UID) + : null; + final Bundle intentExtras; + if (appExtras != null) { + intentExtras = new Bundle(1); + intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras); + } else { + intentExtras = null; + } + doSendBroadcast(action, null, intentExtras, + Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null, + targetUserIds, false, null, null, null); + } + }); + } + + /** + * Send broadcast intents for packages distracting changes. + * + * @param pkgList The names of packages which have suspension changes. + * @param uidList The uids of packages which have suspension changes. + * @param userId The user where packages reside. + */ + void sendDistractingPackagesChanged(@NonNull Computer snapshot, + @NonNull String[] pkgList, + @NonNull int[] uidList, + int userId, + int distractionFlags) { + final Bundle extras = new Bundle(); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); + extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags); + + mHandler.post(() -> sendPackageBroadcast( + Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */, + extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */, + null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */, + null /* broadcastAllowList */, + (callingUid, intentExtras) -> filterExtrasChangedPackageList( + snapshot, callingUid, intentExtras), + null /* bOptions */)); + } + + void sendResourcesChangedBroadcastAndNotify(@NonNull Computer snapshot, + boolean mediaStatus, + boolean replacing, + @NonNull ArrayList<AndroidPackage> packages) { + final int size = packages.size(); + final String[] packageNames = new String[size]; + final int[] packageUids = new int[size]; + for (int i = 0; i < size; i++) { + final AndroidPackage pkg = packages.get(i); + packageNames[i] = pkg.getPackageName(); + packageUids[i] = pkg.getUid(); + } + sendResourcesChangedBroadcast(snapshot, mediaStatus, + replacing, packageNames, packageUids); + notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids); + } + + private void notifyPackageMonitor(@NonNull String action, + @NonNull String pkg, + @Nullable Bundle extras, + @NonNull int[] userIds, + @NonNull int[] instantUserIds, + @Nullable SparseArray<int[]> broadcastAllowList) { + mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds, + instantUserIds, broadcastAllowList, mHandler); + } + + private void notifyResourcesChanged(boolean mediaStatus, + boolean replacing, + @NonNull String[] pkgNames, + @NonNull int[] uids) { + mPackageMonitorCallbackHelper.notifyResourcesChanged(mediaStatus, replacing, pkgNames, + uids, mHandler); + } } diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 83f90a1c1b3c..8e767e74fc9b 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -63,7 +63,6 @@ import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; -import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.ArchiveState; import com.android.server.pm.pkg.PackageStateInternal; @@ -87,19 +86,16 @@ final class DeletePackageHelper { private final PackageManagerService mPm; private final UserManagerInternal mUserManagerInternal; - private final PermissionManagerServiceInternal mPermissionManager; private final RemovePackageHelper mRemovePackageHelper; + private final BroadcastHelper mBroadcastHelper; // TODO(b/198166813): remove PMS dependency - DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper) { + DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper, + BroadcastHelper broadcastHelper) { mPm = pm; mUserManagerInternal = mPm.mInjector.getUserManagerInternal(); - mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal(); mRemovePackageHelper = removePackageHelper; - } - - DeletePackageHelper(PackageManagerService pm) { - this(pm, new RemovePackageHelper(pm)); + mBroadcastHelper = broadcastHelper; } /** @@ -121,7 +117,7 @@ final class DeletePackageHelper { */ public int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags, boolean removedBySystem) { - final PackageRemovedInfo info = new PackageRemovedInfo(mPm); + final PackageRemovedInfo info = new PackageRemovedInfo(); final boolean res; final int removeUser = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0 @@ -251,8 +247,9 @@ final class DeletePackageHelper { if (res) { final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0; final boolean isArchived = (deleteFlags & PackageManager.DELETE_ARCHIVE) != 0; - info.sendPackageRemovedBroadcasts(killApp, removedBySystem, isArchived); - info.sendSystemPackageUpdatedBroadcasts(); + mBroadcastHelper.sendPackageRemovedBroadcasts(info, mPm, killApp, + removedBySystem, isArchived); + mBroadcastHelper.sendSystemPackageUpdatedBroadcasts(info); PackageMetrics.onUninstallSucceeded(info, deleteFlags, removeUser); } @@ -314,7 +311,7 @@ final class DeletePackageHelper { Slog.i(TAG, "Enabling system stub after removal; pkg: " + stubPkg.getPackageName()); } - new InstallPackageHelper(mPm).enableCompressedPackage(stubPkg, stubPs); + mPm.enableCompressedPackage(stubPkg, stubPs); } else if (DEBUG_COMPRESSION) { Slog.i(TAG, "System stub disabled for all users, leaving uncompressed " + "after removal; pkg: " + stubPkg.getPackageName()); @@ -491,8 +488,7 @@ final class DeletePackageHelper { // When an updated system application is deleted we delete the existing resources // as well and fall back to existing code in system partition deleteInstalledSystemPackage(action, allUserHandles, writeSettings); - new InstallPackageHelper(mPm).restoreDisabledSystemPackageLIF( - action, allUserHandles, writeSettings); + mPm.restoreDisabledSystemPackageLIF(action, allUserHandles, writeSettings); } else { if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.getPackageName()); if (ps.isIncremental()) { diff --git a/services/core/java/com/android/server/pm/DistractingPackageHelper.java b/services/core/java/com/android/server/pm/DistractingPackageHelper.java index 8ebb6eaccd1a..c5ec73b4e2b8 100644 --- a/services/core/java/com/android/server/pm/DistractingPackageHelper.java +++ b/services/core/java/com/android/server/pm/DistractingPackageHelper.java @@ -19,10 +19,7 @@ package com.android.server.pm; import static android.content.pm.PackageManager.RESTRICTION_NONE; import android.annotation.NonNull; -import android.content.Intent; import android.content.pm.PackageManager.DistractionRestriction; -import android.os.Bundle; -import android.os.Handler; import android.os.UserHandle; import android.util.ArraySet; import android.util.IntArray; @@ -42,17 +39,16 @@ public final class DistractingPackageHelper { // TODO(b/198166813): remove PMS dependency private final PackageManagerService mPm; - private final PackageManagerServiceInjector mInjector; private final BroadcastHelper mBroadcastHelper; private final SuspendPackageHelper mSuspendPackageHelper; /** * Constructor for {@link PackageManagerService}. */ - DistractingPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector, - BroadcastHelper broadcastHelper, SuspendPackageHelper suspendPackageHelper) { + DistractingPackageHelper(PackageManagerService pm, + BroadcastHelper broadcastHelper, + SuspendPackageHelper suspendPackageHelper) { mPm = pm; - mInjector = injector; mBroadcastHelper = broadcastHelper; mSuspendPackageHelper = suspendPackageHelper; } @@ -127,8 +123,8 @@ public final class DistractingPackageHelper { if (!changedPackagesList.isEmpty()) { final String[] changedPackages = changedPackagesList.toArray( new String[changedPackagesList.size()]); - sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId, - restrictionFlags); + mBroadcastHelper.sendDistractingPackagesChanged(mPm.snapshotComputer(), + changedPackages, changedUids.toArray(), userId, restrictionFlags); mPm.scheduleWritePackageRestrictions(userId); } return unactionedPackages.toArray(new String[0]); @@ -202,34 +198,9 @@ public final class DistractingPackageHelper { if (!changedPackages.isEmpty()) { final String[] packageArray = changedPackages.toArray( new String[changedPackages.size()]); - sendDistractingPackagesChanged(packageArray, changedUids.toArray(), userId, - RESTRICTION_NONE); + mBroadcastHelper.sendDistractingPackagesChanged(mPm.snapshotComputer(), + packageArray, changedUids.toArray(), userId, RESTRICTION_NONE); mPm.scheduleWritePackageRestrictions(userId); } } - - /** - * Send broadcast intents for packages distracting changes. - * - * @param pkgList The names of packages which have suspension changes. - * @param uidList The uids of packages which have suspension changes. - * @param userId The user where packages reside. - */ - void sendDistractingPackagesChanged(@NonNull String[] pkgList, int[] uidList, int userId, - int distractionFlags) { - final Bundle extras = new Bundle(); - extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); - extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); - extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags); - - final Handler handler = mInjector.getHandler(); - handler.post(() -> mBroadcastHelper.sendPackageBroadcast( - Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */, - extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */, - null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */, - null /* broadcastAllowList */, - (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList( - mPm.snapshotComputer(), callingUid, intentExtras), - null /* bOptions */)); - } } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index e1e5e6db104a..86cdc04d0ebf 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -38,7 +38,6 @@ import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4; import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile; -import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.os.incremental.IncrementalManager.isIncrementalPath; import static android.os.storage.StorageManager.FLAG_STORAGE_CE; @@ -50,7 +49,6 @@ import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet; import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME; -import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP; import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING; @@ -130,7 +128,6 @@ import android.content.pm.parsing.result.ParseTypeImpl; import android.net.Uri; import android.os.Binder; import android.os.Build; -import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; import android.os.Message; @@ -143,9 +140,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.incremental.IncrementalManager; import android.os.incremental.IncrementalStorage; -import android.os.storage.StorageManager; -import android.os.storage.VolumeInfo; -import android.stats.storage.StorageEnums; import android.system.ErrnoException; import android.system.Os; import android.text.TextUtils; @@ -163,7 +157,6 @@ import com.android.internal.content.F2fsUtils; import com.android.internal.security.VerityUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; -import com.android.internal.util.FrameworkStatsLog; import com.android.server.EventLogTags; import com.android.server.LocalManagerRegistry; import com.android.server.SystemConfig; @@ -220,6 +213,7 @@ final class InstallPackageHelper { private final AppDataHelper mAppDataHelper; private final BroadcastHelper mBroadcastHelper; private final RemovePackageHelper mRemovePackageHelper; + private final DeletePackageHelper mDeletePackageHelper; private final IncrementalManager mIncrementalManager; private final ApexManager mApexManager; private final DexManager mDexManager; @@ -233,12 +227,17 @@ final class InstallPackageHelper { private final UpdateOwnershipHelper mUpdateOwnershipHelper; // TODO(b/198166813): remove PMS dependency - InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) { + InstallPackageHelper(PackageManagerService pm, + AppDataHelper appDataHelper, + RemovePackageHelper removePackageHelper, + DeletePackageHelper deletePackageHelper, + BroadcastHelper broadcastHelper) { mPm = pm; mInjector = pm.mInjector; mAppDataHelper = appDataHelper; - mBroadcastHelper = new BroadcastHelper(pm.mInjector); - mRemovePackageHelper = new RemovePackageHelper(pm); + mBroadcastHelper = broadcastHelper; + mRemovePackageHelper = removePackageHelper; + mDeletePackageHelper = deletePackageHelper; mIncrementalManager = pm.mInjector.getIncrementalManager(); mApexManager = pm.mInjector.getApexManager(); mDexManager = pm.mInjector.getDexManager(); @@ -251,10 +250,6 @@ final class InstallPackageHelper { mUpdateOwnershipHelper = pm.mInjector.getUpdateOwnershipHelper(); } - InstallPackageHelper(PackageManagerService pm) { - this(pm, new AppDataHelper(pm)); - } - /** * Commits the package scan and modifies system state. * <p><em>WARNING:</em> The method may throw an exception in the middle @@ -263,7 +258,7 @@ final class InstallPackageHelper { * possible and the system is not left in an inconsistent state. */ @GuardedBy("mPm.mLock") - public AndroidPackage commitReconciledScanResultLocked( + private AndroidPackage commitReconciledScanResultLocked( @NonNull ReconciledPackage reconciledPkg, int[] allUsers) { final InstallRequest request = reconciledPkg.mInstallRequest; // TODO(b/135203078): Move this even further away @@ -731,8 +726,9 @@ final class InstallPackageHelper { } // TODO(b/278553670) Store archive state for the user. boolean isArchived = (pkgSetting.getPkg() == null); - mPm.sendPackageAddedForUser(mPm.snapshotComputer(), packageName, pkgSetting, userId, - isArchived, DataLoaderType.NONE); + mBroadcastHelper.sendPackageAddedForUser(mPm.snapshotComputer(), packageName, + pkgSetting, userId, isArchived, DataLoaderType.NONE, + mPm.mAppPredictionServicePackage); synchronized (mPm.mLock) { mPm.updateSequenceNumberLP(pkgSetting, new int[]{ userId }); } @@ -1745,7 +1741,7 @@ final class InstallPackageHelper { } // Update what is removed - PackageRemovedInfo removedInfo = new PackageRemovedInfo(mPm); + PackageRemovedInfo removedInfo = new PackageRemovedInfo(); removedInfo.mUid = ps.getAppId(); removedInfo.mRemovedPackage = ps.getPackageName(); removedInfo.mInstallerPackageName = @@ -2074,8 +2070,6 @@ final class InstallPackageHelper { final InstallRequest installRequest = reconciledPkg.mInstallRequest; final ParsedPackage parsedPackage = installRequest.getParsedPackage(); final String packageName = parsedPackage.getPackageName(); - final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm); - final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm); installRequest.onCommitStarted(); if (installRequest.isInstallReplace()) { @@ -2097,7 +2091,7 @@ final class InstallPackageHelper { allUsers, mPm.mSettings.getPackagesLocked()); if (installRequest.isInstallSystem()) { // Remove existing system package - removePackageHelper.removePackage(oldPackage, true); + mRemovePackageHelper.removePackage(oldPackage, true); if (!disableSystemPackageLPw(oldPackage)) { // We didn't need to disable the .apk as a current system package, // which means we are replacing another update that is already @@ -2113,7 +2107,7 @@ final class InstallPackageHelper { } else { try { // Settings will be written during the call to updateSettingsLI(). - deletePackageHelper.executeDeletePackage( + mDeletePackageHelper.executeDeletePackage( reconciledPkg.mDeletePackageAction, packageName, true, allUsers, false); } catch (SystemDeleteException e) { @@ -2796,24 +2790,21 @@ final class InstallPackageHelper { final Computer snapshot = mPm.snapshotComputer(); // Send broadcasts for (int i = 0; i < numBroadcasts; i++) { - mPm.sendPackageChangedBroadcast(snapshot, packages[i], true /* dontKillApp */, - components[i], uids[i], null /* reason */); + mBroadcastHelper.sendPackageChangedBroadcast(snapshot, packages[i], + true /* dontKillApp */, components[i], uids[i], null /* reason */); } } void handlePackagePostInstall(InstallRequest request, boolean launchedForRestore) { final boolean killApp = (request.getInstallFlags() & PackageManager.INSTALL_DONT_KILL_APP) == 0; - final boolean virtualPreload = - ((request.getInstallFlags() & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); - final String installerPackage = request.getInstallerPackageName(); - final int dataLoaderType = request.getDataLoaderType(); final boolean succeeded = request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED; final boolean update = request.isUpdate(); final boolean archived = request.isArchived(); final String packageName = request.getName(); + final Computer snapshot = mPm.snapshotComputer(); final PackageStateInternal pkgSetting = - succeeded ? mPm.snapshotComputer().getPackageStateInternal(packageName) : null; + succeeded ? snapshot.getPackageStateInternal(packageName) : null; final boolean removedBeforeUpdate = (pkgSetting == null) || (pkgSetting.isSystem() && !pkgSetting.getPath().getPath().equals( request.getPkg().getPath())); @@ -2834,207 +2825,21 @@ final class InstallPackageHelper { // Clear the uid cache after we installed a new package. mPm.mPerUidReadTimeoutsCache = null; - // Send the removed broadcasts - if (request.getRemovedInfo() != null) { - if (request.getRemovedInfo().mIsExternal) { - if (DEBUG_INSTALL) { - Slog.i(TAG, "upgrading pkg " + request.getRemovedInfo().mRemovedPackage - + " is ASEC-hosted -> UNAVAILABLE"); - } - final String[] pkgNames = new String[]{ - request.getRemovedInfo().mRemovedPackage}; - final int[] uids = new int[]{request.getRemovedInfo().mUid}; - mPm.notifyResourcesChanged(false /* mediaStatus */, - true /* replacing */, pkgNames, uids); - mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer, - false /* mediaStatus */, true /* replacing */, pkgNames, uids); - } - request.getRemovedInfo().sendPackageRemovedBroadcasts( - killApp, false /*removedBySystem*/, false /*isArchived*/); - } - - final String installerPackageName = - request.getInstallerPackageName() != null - ? request.getInstallerPackageName() - : request.getRemovedInfo() != null - ? request.getRemovedInfo().mInstallerPackageName - : null; - mPm.notifyInstantAppPackageInstalled(request.getPkg().getPackageName(), request.getNewUsers()); request.populateBroadcastUsers(); final int[] firstUserIds = request.getFirstTimeBroadcastUserIds(); - final int[] firstInstantUserIds = request.getFirstTimeBroadcastInstantUserIds(); - final int[] updateUserIds = request.getUpdateBroadcastUserIds(); - final int[] instantUserIds = request.getUpdateBroadcastInstantUserIds(); - Bundle extras = new Bundle(); - extras.putInt(Intent.EXTRA_UID, request.getAppId()); - if (update) { - extras.putBoolean(Intent.EXTRA_REPLACING, true); - } - if (archived) { - extras.putBoolean(Intent.EXTRA_ARCHIVAL, true); - } - extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); - - // If a package is a static shared library, then only the installer of the package - // should get the broadcast. - if (installerPackageName != null - && request.getPkg().getStaticSharedLibraryName() != null) { - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, 0 /*flags*/, - installerPackageName, null /*finishedReceiver*/, - request.getNewUsers(), null /* instantUserIds*/, - null /* broadcastAllowList */, null); - } - - // Send installed broadcasts if the package is not a static shared lib. if (request.getPkg().getStaticSharedLibraryName() == null) { mPm.mProcessLoggingHandler.invalidateBaseApkHash(request.getPkg().getBaseApkPath()); + } - // Send PACKAGE_ADDED broadcast for users that see the package for the first time - // sendPackageAddedForNewUsers also deals with system apps - int appId = UserHandle.getAppId(request.getAppId()); - boolean isSystem = request.isInstallSystem(); - mPm.sendPackageAddedForNewUsers(mPm.snapshotComputer(), packageName, - isSystem || virtualPreload, virtualPreload /*startReceiver*/, appId, - firstUserIds, firstInstantUserIds, archived, dataLoaderType); + mBroadcastHelper.sendPostInstallBroadcasts(mPm.snapshotComputer(), request, packageName, + mPm.mRequiredPermissionControllerPackage, mPm.mRequiredVerifierPackages, + mPm.mRequiredInstallerPackage, + /* packageSender= */ mPm, launchedForRestore, killApp, update, archived); - // Send PACKAGE_ADDED broadcast for users that don't see - // the package for the first time - - // Send to all running apps. - final SparseArray<int[]> newBroadcastAllowList; - synchronized (mPm.mLock) { - final Computer snapshot = mPm.snapshotComputer(); - newBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(snapshot, - snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID), - updateUserIds, mPm.mSettings.getPackagesLocked()); - } - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, 0 /*flags*/, - null /*targetPackage*/, null /*finishedReceiver*/, - updateUserIds, instantUserIds, newBroadcastAllowList, null); - // Send to the installer, even if it's not running. - if (installerPackageName != null) { - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, 0 /*flags*/, - installerPackageName, null /*finishedReceiver*/, - updateUserIds, instantUserIds, null /* broadcastAllowList */, null); - } - // Send to PermissionController for all update users, even if it may not be running - // for some users - if (BroadcastHelper.isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) { - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, 0 /*flags*/, - mPm.mRequiredPermissionControllerPackage, null /*finishedReceiver*/, - updateUserIds, instantUserIds, null /* broadcastAllowList */, null); - } - // Notify required verifier(s) that are not the installer of record for the package. - for (String verifierPackageName : mPm.mRequiredVerifierPackages) { - if (verifierPackageName != null && !verifierPackageName.equals( - installerPackageName)) { - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, 0 /*flags*/, - verifierPackageName, null /*finishedReceiver*/, - updateUserIds, instantUserIds, null /* broadcastAllowList */, - null); - } - } - // If package installer is defined, notify package installer about new - // app installed - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/, - mPm.mRequiredInstallerPackage, null /*finishedReceiver*/, - firstUserIds, instantUserIds, null /* broadcastAllowList */, null); - - // Send replaced for users that don't see the package for the first time - if (update) { - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, - packageName, extras, 0 /*flags*/, - null /*targetPackage*/, null /*finishedReceiver*/, - updateUserIds, instantUserIds, - request.getRemovedInfo().mBroadcastAllowList, null); - if (installerPackageName != null) { - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, - extras, 0 /*flags*/, - installerPackageName, null /*finishedReceiver*/, - updateUserIds, instantUserIds, null /*broadcastAllowList*/, - null); - } - for (String verifierPackageName : mPm.mRequiredVerifierPackages) { - if (verifierPackageName != null && !verifierPackageName.equals( - installerPackageName)) { - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, - packageName, extras, 0 /*flags*/, verifierPackageName, - null /*finishedReceiver*/, updateUserIds, instantUserIds, - null /*broadcastAllowList*/, null); - } - } - mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, - null /*package*/, null /*extras*/, 0 /*flags*/, - packageName /*targetPackage*/, - null /*finishedReceiver*/, updateUserIds, instantUserIds, - null /*broadcastAllowList*/, - mBroadcastHelper.getTemporaryAppAllowlistBroadcastOptions( - REASON_PACKAGE_REPLACED).toBundle()); - } else if (launchedForRestore && !request.isInstallSystem()) { - // First-install and we did a restore, so we're responsible for the - // first-launch broadcast. - if (DEBUG_BACKUP) { - Slog.i(TAG, "Post-restore of " + packageName - + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds)); - } - mBroadcastHelper.sendFirstLaunchBroadcast(packageName, installerPackage, - firstUserIds, firstInstantUserIds); - } - - // Send broadcast package appeared if external for all users - if (request.getPkg().isExternalStorage()) { - if (!update) { - final StorageManager storageManager = - mInjector.getSystemService(StorageManager.class); - VolumeInfo volume = - storageManager.findVolumeByUuid( - StorageManager.convert( - request.getPkg().getVolumeUuid()).toString()); - int packageExternalStorageType = - PackageManagerServiceUtils.getPackageExternalStorageType(volume, - request.getPkg().isExternalStorage()); - // If the package was installed externally, log it. - if (packageExternalStorageType != StorageEnums.UNKNOWN) { - FrameworkStatsLog.write( - FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED, - packageExternalStorageType, packageName); - } - } - if (DEBUG_INSTALL) { - Slog.i(TAG, "upgrading pkg " + request.getPkg() + " is external"); - } - if (!archived) { - final String[] pkgNames = new String[]{packageName}; - final int[] uids = new int[]{request.getPkg().getUid()}; - mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer, - true /* mediaStatus */, true /* replacing */, pkgNames, uids); - mPm.notifyResourcesChanged(true /* mediaStatus */, true /* replacing */, - pkgNames, uids); - } - } - } else if (!ArrayUtils.isEmpty(request.getLibraryConsumers())) { // if static shared lib - // No need to kill consumers if it's installation of new version static shared lib. - final Computer snapshot = mPm.snapshotComputer(); - final boolean dontKillApp = !update - && request.getPkg().getStaticSharedLibraryName() != null; - for (int i = 0; i < request.getLibraryConsumers().size(); i++) { - AndroidPackage pkg = request.getLibraryConsumers().get(i); - // send broadcast that all consumers of the static shared library have changed - mPm.sendPackageChangedBroadcast(snapshot, pkg.getPackageName(), dontKillApp, - new ArrayList<>(Collections.singletonList(pkg.getPackageName())), - pkg.getUid(), null); - } - } // Work that needs to happen on first install within each user if (firstUserIds.length > 0) { @@ -3074,7 +2879,6 @@ final class InstallPackageHelper { } if (!archived) { - final Computer snapshot = mPm.snapshotComputer(); // Notify DexManager that the package was installed for new users. // The updated users should already be indexed and the package code paths // should not change. @@ -3090,7 +2894,7 @@ final class InstallPackageHelper { } } else { // Now send PACKAGE_REMOVED + EXTRA_REPLACING broadcast. - final PackageRemovedInfo info = new PackageRemovedInfo(mPm); + final PackageRemovedInfo info = new PackageRemovedInfo(); info.mRemovedPackage = packageName; info.mInstallerPackageName = request.getInstallerPackageName(); info.mRemovedUsers = firstUserIds; @@ -3099,8 +2903,8 @@ final class InstallPackageHelper { info.mRemovedPackageVersionCode = request.getPkg().getLongVersionCode(); info.mRemovedForAllUsers = true; - info.sendPackageRemovedBroadcasts(false /*killApp*/, - false /*removedBySystem*/, true /*isArchived*/); + mBroadcastHelper.sendPackageRemovedBroadcasts(info, mPm, + false /*killApp*/, false /*removedBySystem*/, true /*isArchived*/); } } @@ -3291,15 +3095,14 @@ final class InstallPackageHelper { synchronized (mPm.mLock) { mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/); } - final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm); - removePackageHelper.removePackage(stubPkg, true /*chatty*/); + mRemovePackageHelper.removePackage(stubPkg, true /*chatty*/); try { return initPackageTracedLI(scanFile, parseFlags, scanFlags); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(), e); // Remove the failed install - removePackageHelper.removeCodePath(scanFile); + mRemovePackageHelper.removeCodePath(scanFile); throw e; } } @@ -3342,7 +3145,7 @@ final class InstallPackageHelper { if (!dstCodePath.exists()) { return null; } - new RemovePackageHelper(mPm).removeCodePath(dstCodePath); + mRemovePackageHelper.removeCodePath(dstCodePath); return null; } @@ -4298,8 +4101,8 @@ final class InstallPackageHelper { parsedPackage.getPackageName(), UserHandle.USER_ALL, "scanPackageInternalLI", ApplicationExitInfo.REASON_OTHER, null /* request */)) { - DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm); - deletePackageHelper.deletePackageLIF(parsedPackage.getPackageName(), null, true, + mDeletePackageHelper.deletePackageLIF( + parsedPackage.getPackageName(), null, true, mPm.mUserManager.getUserIds(), 0, null, false); } } else if (newPkgVersionGreater || newSharedUserSetting) { diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java index fe6a8a1778a2..ca8dc2973404 100644 --- a/services/core/java/com/android/server/pm/InstallingSession.java +++ b/services/core/java/com/android/server/pm/InstallingSession.java @@ -94,8 +94,6 @@ class InstallingSession { private final UserHandle mUser; @NonNull final PackageManagerService mPm; - final InstallPackageHelper mInstallPackageHelper; - final RemovePackageHelper mRemovePackageHelper; final boolean mIsInherit; final int mSessionId; final int mRequireUserAction; @@ -108,8 +106,6 @@ class InstallingSession { PackageLite packageLite, PackageManagerService pm) { mPm = pm; mUser = user; - mInstallPackageHelper = new InstallPackageHelper(mPm); - mRemovePackageHelper = new RemovePackageHelper(mPm); mOriginInfo = originInfo; mMoveInfo = moveInfo; mObserver = observer; @@ -142,8 +138,6 @@ class InstallingSession { PackageLite packageLite, PackageManagerService pm) { mPm = pm; mUser = user; - mInstallPackageHelper = new InstallPackageHelper(mPm); - mRemovePackageHelper = new RemovePackageHelper(mPm); mOriginInfo = OriginInfo.fromStagedFile(stagedDir); mMoveInfo = null; mInstallReason = fixUpInstallReason( @@ -242,7 +236,7 @@ class InstallingSession { // state can change within this delay and hence we need to re-verify certain conditions. boolean isStaged = (mInstallFlags & INSTALL_STAGED) != 0; if (isStaged) { - Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode( + Pair<Integer, String> ret = mPm.verifyReplacingVersionCode( pkgLite, mRequiredInstalledVersionCode, mInstallFlags); mRet = ret.first; if (mRet != INSTALL_SUCCEEDED) { @@ -540,39 +534,39 @@ class InstallingSession { } } } else { - mInstallPackageHelper.installPackagesTraced(installRequests); + mPm.installPackagesTraced(installRequests); for (InstallRequest request : installRequests) { doPostInstall(request); } } for (InstallRequest request : installRequests) { - mInstallPackageHelper.restoreAndPostInstall(request); + mPm.restoreAndPostInstall(request); } } private void doPostInstall(InstallRequest request) { if (mMoveInfo != null) { if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) { - mRemovePackageHelper.cleanUpForMoveInstall(mMoveInfo.mFromUuid, + mPm.cleanUpForMoveInstall(mMoveInfo.mFromUuid, mMoveInfo.mPackageName, mMoveInfo.mFromCodePath); } else { - mRemovePackageHelper.cleanUpForMoveInstall(mMoveInfo.mToUuid, + mPm.cleanUpForMoveInstall(mMoveInfo.mToUuid, mMoveInfo.mPackageName, mMoveInfo.mFromCodePath); } } else { if (request.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) { - mRemovePackageHelper.removeCodePath(request.getCodeFile()); + mPm.removeCodePath(request.getCodeFile()); } } } private void cleanUpForFailedInstall(InstallRequest request) { if (request.isInstallMove()) { - mRemovePackageHelper.cleanUpForMoveInstall(request.getMoveToUuid(), + mPm.cleanUpForMoveInstall(request.getMoveToUuid(), request.getMovePackageName(), request.getMoveFromCodePath()); } else { - mRemovePackageHelper.removeCodePath(request.getCodeFile()); + mPm.removeCodePath(request.getCodeFile()); } } diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java index b4ca477ddfec..4ecbd154cab5 100644 --- a/services/core/java/com/android/server/pm/PackageHandler.java +++ b/services/core/java/com/android/server/pm/PackageHandler.java @@ -60,14 +60,10 @@ import java.io.IOException; */ final class PackageHandler extends Handler { private final PackageManagerService mPm; - private final InstallPackageHelper mInstallPackageHelper; - private final RemovePackageHelper mRemovePackageHelper; PackageHandler(Looper looper, PackageManagerService pm) { super(looper); mPm = pm; - mInstallPackageHelper = new InstallPackageHelper(mPm); - mRemovePackageHelper = new RemovePackageHelper(mPm); } @Override @@ -82,7 +78,7 @@ final class PackageHandler extends Handler { void doHandleMessage(Message msg) { switch (msg.what) { case SEND_PENDING_BROADCAST: { - mInstallPackageHelper.sendPendingBroadcasts(); + mPm.sendPendingBroadcasts(); break; } case POST_INSTALL: { @@ -96,7 +92,7 @@ final class PackageHandler extends Handler { request.onInstallCompleted(); request.runPostInstallRunnable(); if (!request.isInstallExistingForUser()) { - mInstallPackageHelper.handlePackagePostInstall(request, didRestore); + mPm.handlePackagePostInstall(request, didRestore); } else if (DEBUG_INSTALL) { // No post-install when we run restore from installExistingPackageForUser Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1); @@ -107,7 +103,7 @@ final class PackageHandler extends Handler { case DEFERRED_NO_KILL_POST_DELETE: { InstallArgs args = (InstallArgs) msg.obj; if (args != null) { - mRemovePackageHelper.cleanUpResources(args.mCodeFile, args.mInstructionSets); + mPm.cleanUpResources(args.mCodeFile, args.mInstructionSets); } } break; case DEFERRED_NO_KILL_INSTALL_OBSERVER: diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 95b565d235cb..1bb20b4769f5 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -18,7 +18,9 @@ package com.android.server.pm; import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO; import static android.os.Process.INVALID_UID; + import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME; + import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; @@ -407,11 +409,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } private void removeStagingDirs(ArraySet<File> stagingDirsToRemove) { - final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm); // Clean up orphaned staging directories for (File stage : stagingDirsToRemove) { Slog.w(TAG, "Deleting orphan stage " + stage); - removePackageHelper.removeCodePath(stage); + mPm.removeCodePath(stage); } } @@ -1320,9 +1321,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements @Override public void installExistingPackage(String packageName, int installFlags, int installReason, IntentSender statusReceiver, int userId, List<String> allowListedPermissions) { - final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm); - var result = installPackageHelper.installExistingPackageAsUser(packageName, userId, + var result = mPm.installExistingPackageAsUser(packageName, userId, installFlags, installReason, allowListedPermissions, statusReceiver); int returnCode = result.first; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index f554773becad..d0e5f96f8d0f 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2449,14 +2449,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } private void onSystemDataLoaderUnrecoverable() { - final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm); final String packageName = getPackageName(); if (TextUtils.isEmpty(packageName)) { // The package has not been installed. return; } mHandler.post(() -> { - if (deletePackageHelper.deletePackageX(packageName, + if (mPm.deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/) != PackageManager.DELETE_SUCCEEDED) { @@ -2931,15 +2930,40 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * @return a future that will be completed when the whole process is completed. */ private CompletableFuture<Void> install() { + // `futures` either contains only one session (`this`) or contains one parent session + // (`this`) and n-1 child sessions. List<CompletableFuture<InstallResult>> futures = installNonStaged(); CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()]; return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> { if (t == null) { setSessionApplied(); + var multiPackageWarnings = new ArrayList<String>(); + if (isMultiPackage()) { + // This is a parent session. Collect warnings from children. + for (CompletableFuture<InstallResult> f : futures) { + InstallResult result = f.join(); + if (result.session != this && result.extras != null) { + ArrayList<String> childWarnings = result.extras.getStringArrayList( + PackageInstaller.EXTRA_WARNINGS); + if (!ArrayUtils.isEmpty(childWarnings)) { + multiPackageWarnings.addAll(childWarnings); + } + } + } + } for (CompletableFuture<InstallResult> f : futures) { InstallResult result = f.join(); + Bundle extras = result.extras; + if (isMultiPackage() && result.session == this + && !multiPackageWarnings.isEmpty()) { + if (extras == null) { + extras = new Bundle(); + } + extras.putStringArrayList( + PackageInstaller.EXTRA_WARNINGS, multiPackageWarnings); + } result.session.dispatchSessionFinished( - INSTALL_SUCCEEDED, "Session installed", result.extras); + INSTALL_SUCCEEDED, "Session installed", extras); } } else { PackageManagerException e = (PackageManagerException) t.getCause(); @@ -3817,8 +3841,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Stage APK's fs-verity signature if present. maybeStageFsveritySignatureLocked(origFile, targetFile, isFsVerityRequiredForApk(origFile, targetFile)); - // Stage APK's v4 signature if present. - if (android.security.Flags.extendVbChainToUpdatedApk()) { + // Stage APK's v4 signature if present, and fs-verity is supported. + if (android.security.Flags.extendVbChainToUpdatedApk() + && VerityUtils.isFsVeritySupported()) { maybeStageV4SignatureLocked(origFile, targetFile); } // Stage dex metadata (.dm) and corresponding fs-verity signature if present. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 700fae953ead..ddc8369738de 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -70,7 +70,6 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; -import android.content.IIntentReceiver; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; @@ -98,6 +97,7 @@ import android.content.pm.InstantAppInfo; import android.content.pm.InstantAppRequest; import android.content.pm.ModuleInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageManager.ComponentEnabledSetting; @@ -115,6 +115,7 @@ import android.content.pm.TestUtilityService; import android.content.pm.UserInfo; import android.content.pm.UserPackage; import android.content.pm.VerifierDeviceIdentity; +import android.content.pm.VerifierInfo; import android.content.pm.VersionedPackage; import android.content.pm.overlay.OverlayPaths; import android.content.pm.parsing.PackageLite; @@ -985,7 +986,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService private final DeletePackageHelper mDeletePackageHelper; private final InitAppsHelper mInitAppsHelper; private final AppDataHelper mAppDataHelper; - private final InstallPackageHelper mInstallPackageHelper; + @NonNull private final InstallPackageHelper mInstallPackageHelper; private final PreferredActivityHelper mPreferredActivityHelper; private final ResolveIntentHelper mResolveIntentHelper; private final DexOptHelper mDexOptHelper; @@ -1715,7 +1716,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService (i, pm) -> new CrossProfileIntentFilterHelper(i.getSettings(), i.getUserManagerService(), i.getLock(), i.getUserManagerInternal(), context), - (i, pm) -> new UpdateOwnershipHelper()); + (i, pm) -> new UpdateOwnershipHelper(), + (i, pm) -> new PackageMonitorCallbackHelper()); if (Build.VERSION.SDK_INT <= 0) { Slog.w(TAG, "**** ro.build.version.sdk not set!"); @@ -2067,17 +2069,19 @@ public class PackageManagerService implements PackageSender, TestUtilityService mDomainVerificationManager.setConnection(mDomainVerificationConnection); mBroadcastHelper = new BroadcastHelper(mInjector); - mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper(mInjector); + mPackageMonitorCallbackHelper = injector.getPackageMonitorCallbackHelper(); mAppDataHelper = new AppDataHelper(this); - mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper); - mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper); - mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper); + mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper, mBroadcastHelper); + mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper, + mBroadcastHelper); + mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper, mRemovePackageHelper, + mDeletePackageHelper, mBroadcastHelper); mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager, mInjector.getUserManagerInternal(), mDeletePackageHelper); mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper); - mPreferredActivityHelper = new PreferredActivityHelper(this); + mPreferredActivityHelper = new PreferredActivityHelper(this, mBroadcastHelper); mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper, injector.getCompatibility(), mUserManager, mDomainVerificationManager, mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity, @@ -2085,7 +2089,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService mDexOptHelper = new DexOptHelper(this); mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mUserManager, mBroadcastHelper, mProtectedPackages); - mDistractingPackageHelper = new DistractingPackageHelper(this, mInjector, mBroadcastHelper, + mDistractingPackageHelper = new DistractingPackageHelper(this, mBroadcastHelper, mSuspendPackageHelper); mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper, mRemovePackageHelper); @@ -3078,38 +3082,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Override - public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, - final int flags, final String targetPkg, final IIntentReceiver finishedReceiver, - final int[] userIds, int[] instantUserIds, - @Nullable SparseArray<int[]> broadcastAllowList, - @Nullable Bundle bOptions) { - mHandler.post(() -> mBroadcastHelper.sendPackageBroadcast(action, pkg, extras, flags, - targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList, - null /* filterExtrasForReceiver */, bOptions)); - if (targetPkg == null) { - // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called - // many times to different targets, e.g. installer app, permission controller, other - // registered apps. We should filter it to avoid calling back many times for the same - // action. When the targetPkg is set, it sends the broadcast to specific app, e.g. - // installer app or null for registered apps. The callback only need to send back to the - // registered apps so we check the null condition here. - notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList); - } - } - - void notifyPackageMonitor(String action, String pkg, Bundle extras, int[] userIds, - int[] instantUserIds, SparseArray<int[]> broadcastAllowList) { - mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds, - instantUserIds, broadcastAllowList); - } - - void notifyResourcesChanged(boolean mediaStatus, boolean replacing, - @NonNull String[] pkgNames, @NonNull int[] uids) { - mPackageMonitorCallbackHelper.notifyResourcesChanged(mediaStatus, replacing, pkgNames, - uids); - } - - @Override public void notifyPackageAdded(String packageName, int uid) { mPackageObserverHelper.notifyAdded(packageName, uid); } @@ -3125,64 +3097,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService UserPackage.removeFromCache(UserHandle.getUserId(uid), packageName); } - void sendPackageAddedForUser(@NonNull Computer snapshot, String packageName, - @NonNull PackageStateInternal packageState, int userId, boolean isArchived, - int dataLoaderType) { - final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId); - final boolean isSystem = packageState.isSystem(); - final boolean isInstantApp = userState.isInstantApp(); - final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; - final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; - sendPackageAddedForNewUsers(snapshot, packageName, isSystem /*sendBootCompleted*/, - false /*startReceiver*/, packageState.getAppId(), userIds, instantUserIds, - isArchived, dataLoaderType); - - // Send a session commit broadcast - final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo(); - info.installReason = userState.getInstallReason(); - info.appPackageName = packageName; - sendSessionCommitBroadcast(info, userId); - } - - @Override - public void sendPackageAddedForNewUsers(@NonNull Computer snapshot, String packageName, - boolean sendBootCompleted, boolean includeStopped, @AppIdInt int appId, int[] userIds, - int[] instantUserIds, boolean isArchived, int dataLoaderType) { - if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) { - return; - } - SparseArray<int[]> broadcastAllowList = mAppsFilter.getVisibilityAllowList(snapshot, - snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID), - userIds, snapshot.getPackageStates()); - mHandler.post( - () -> mBroadcastHelper.sendPackageAddedForNewUsers(packageName, appId, userIds, - instantUserIds, isArchived, dataLoaderType, broadcastAllowList)); - mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(packageName, appId, userIds, - instantUserIds, isArchived, dataLoaderType, broadcastAllowList); - if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) { - mHandler.post(() -> { - for (int userId : userIds) { - mBroadcastHelper.sendBootCompletedBroadcastToSystemApp( - packageName, includeStopped, userId); - } - } - ); - } - } - - private void sendApplicationHiddenForUser(String packageName, PackageStateInternal packageState, - int userId) { - final PackageRemovedInfo info = new PackageRemovedInfo(this); - info.mRemovedPackage = packageName; - info.mInstallerPackageName = packageState.getInstallSource().mInstallerPackageName; - info.mRemovedUsers = new int[] {userId}; - info.mBroadcastUsers = new int[] {userId}; - info.mUid = UserHandle.getUid(userId, packageState.getAppId()); - info.mRemovedPackageVersionCode = packageState.getVersionCode(); - info.sendPackageRemovedBroadcasts(true /*killApp*/, false /*removedBySystem*/, - false /*isArchived*/); - } - boolean isUserRestricted(int userId, String restrictionKey) { Bundle restrictions = mUserManager.getUserRestrictions(userId); if (restrictions.getBoolean(restrictionKey, false)) { @@ -3505,11 +3419,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } - void postPreferredActivityChangedBroadcast(int userId) { - mHandler.post(() -> mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId)); - } - - /** This method takes a specific user id as well as UserHandle.USER_ALL. */ @GuardedBy("mLock") void clearPackagePreferredActivitiesLPw(String packageName, @@ -3609,17 +3518,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService } public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) { - UserManagerService ums = UserManagerService.getInstance(); - if (ums == null || sessionInfo.isStaged()) { - return; - } - final UserInfo parent = ums.getProfileParent(userId); - final int launcherUid = (parent != null) ? parent.id : userId; - // TODO: Should this snapshot be moved further up? - final ComponentName launcherComponent = snapshotComputer() - .getDefaultHomeActivity(launcherUid); - mBroadcastHelper.sendSessionCommitBroadcast(sessionInfo, userId, launcherUid, - launcherComponent, mAppPredictionServicePackage); + mBroadcastHelper.sendSessionCommitBroadcast(snapshotComputer(), sessionInfo, userId, + mAppPredictionServicePackage); } private @Nullable String getSetupWizardPackageNameImpl(@NonNull Computer computer) { @@ -3988,7 +3888,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService if (isSystemStub && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) { - if (!mInstallPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting)) { + if (!enableCompressedPackage(deletedPkg, pkgSetting)) { Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable " + "commpressed package " + setting.getPackageName()); updateAllowed[i] = false; @@ -4070,8 +3970,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService final ArrayList<String> components = sendNowBroadcasts.valueAt(i); final int packageUid = UserHandle.getUid( userId, pkgSettings.get(packageName).getAppId()); - sendPackageChangedBroadcast(newSnapshot, packageName, false /* dontKillApp */, - components, packageUid, null /* reason */); + mBroadcastHelper.sendPackageChangedBroadcast(newSnapshot, packageName, + false /* dontKillApp */, components, packageUid, null /* reason */); } } finally { Binder.restoreCallingIdentity(callingId); @@ -4147,27 +4047,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } - void sendPackageChangedBroadcast(@NonNull Computer snapshot, String packageName, - boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason) { - PackageStateInternal setting = snapshot.getPackageStateInternal(packageName, - Process.SYSTEM_UID); - if (setting == null) { - return; - } - final int userId = UserHandle.getUserId(packageUid); - final boolean isInstantApp = - snapshot.isInstantAppInternal(packageName, userId, Process.SYSTEM_UID); - final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; - final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; - final SparseArray<int[]> broadcastAllowList = - isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds); - mHandler.post(() -> mBroadcastHelper.sendPackageChangedBroadcast( - packageName, dontKillApp, componentNames, packageUid, reason, userIds, - instantUserIds, broadcastAllowList)); - mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames, - packageUid, reason, userIds, instantUserIds, broadcastAllowList); - } - /** * Used by SystemServer */ @@ -4312,7 +4191,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService if (pkg == null) { return; } - sendPackageChangedBroadcast(snapshot, pkg.getPackageName(), + mBroadcastHelper.sendPackageChangedBroadcast(snapshot, pkg.getPackageName(), true /* dontKillApp */, new ArrayList<>(Collections.singletonList(pkg.getPackageName())), pkg.getUid(), @@ -4681,9 +4560,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService @UserIdInt int userId, @NonNull String recentCallingPackage, @NonNull String debugInfo) { synchronized (mLock) { - final PackageUserStateInternal userState = mSettings.getPackageLPr( - packageName).getUserStateOrDefault(userId); - if (userState.isQuarantined()) { + final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); + // If the package doesn't exist, don't need to proceed to setPackageStoppedState. + if (pkgSetting == null) { + return; + } + if (pkgSetting.getUserStateOrDefault(userId).isQuarantined()) { Slog.i(TAG, "Component is quarantined+suspended but being used: " + packageName + " by " + recentCallingPackage + ", debugInfo: " @@ -5291,7 +5173,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService throw new SecurityException("Calling package " + packageName + " does not belong to calling uid " + callingUid); } - return mSuspendPackageHelper + return SuspendPackageHelper .getSuspendedPackageAppExtras(snapshot, packageName, userId, callingUid); } @@ -5854,10 +5736,14 @@ public class PackageManagerService implements PackageSender, TestUtilityService if (hidden) { killApplication(packageName, newPackageState.getAppId(), userId, "hiding pkg", ApplicationExitInfo.REASON_OTHER); - sendApplicationHiddenForUser(packageName, newPackageState, userId); + mBroadcastHelper.sendApplicationHiddenForUser( + packageName, newPackageState, userId, + /* packageSender= */ PackageManagerService.this); } else { - sendPackageAddedForUser(newSnapshot, packageName, newPackageState, userId, - false /* isArchived */, DataLoaderType.NONE); + mBroadcastHelper.sendPackageAddedForUser( + newSnapshot, packageName, newPackageState, userId, + false /* isArchived */, DataLoaderType.NONE, + mAppPredictionServicePackage); } scheduleWritePackageRestrictions(userId); @@ -7926,4 +7812,75 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } } + + void removeCodePath(@Nullable File codePath) { + mRemovePackageHelper.removeCodePath(codePath); + } + + void cleanUpResources(@Nullable File codeFile, @Nullable String[] instructionSets) { + mRemovePackageHelper.cleanUpResources(codeFile, instructionSets); + } + + void cleanUpForMoveInstall(String volumeUuid, String packageName, String fromCodePath) { + mRemovePackageHelper.cleanUpForMoveInstall(volumeUuid, packageName, fromCodePath); + } + + void sendPendingBroadcasts() { + mInstallPackageHelper.sendPendingBroadcasts(); + } + + void handlePackagePostInstall(@NonNull InstallRequest request, boolean launchedForRestore) { + mInstallPackageHelper.handlePackagePostInstall(request, launchedForRestore); + } + + Pair<Integer, IntentSender> installExistingPackageAsUser( + @Nullable String packageName, + @UserIdInt int userId, @PackageManager.InstallFlags int installFlags, + @PackageManager.InstallReason int installReason, + @Nullable List<String> allowlistedRestrictedPermissions, + @Nullable IntentSender intentSender) { + return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags, + installReason, allowlistedRestrictedPermissions, intentSender); + } + AndroidPackage initPackageTracedLI(File scanFile, final int parseFlags, int scanFlags) + throws PackageManagerException { + return mInstallPackageHelper.initPackageTracedLI(scanFile, parseFlags, scanFlags); + } + + void restoreDisabledSystemPackageLIF(@NonNull DeletePackageAction action, + @NonNull int[] allUserHandles, + boolean writeSettings) throws SystemDeleteException { + mInstallPackageHelper.restoreDisabledSystemPackageLIF( + action, allUserHandles, writeSettings); + } + boolean enableCompressedPackage(@NonNull AndroidPackage stubPkg, + @NonNull PackageSetting stubPs) { + return mInstallPackageHelper.enableCompressedPackage(stubPkg, stubPs); + } + + void installPackagesTraced(List<InstallRequest> requests) { + mInstallPackageHelper.installPackagesTraced(requests); + } + + void restoreAndPostInstall(InstallRequest request) { + mInstallPackageHelper.restoreAndPostInstall(request); + } + + Pair<Integer, String> verifyReplacingVersionCode(@NonNull PackageInfoLite pkgLite, + long requiredInstalledVersionCode, + int installFlags) { + return mInstallPackageHelper.verifyReplacingVersionCode( + pkgLite, requiredInstalledVersionCode, installFlags); + } + + int getUidForVerifier(VerifierInfo verifierInfo) { + return mInstallPackageHelper.getUidForVerifier(verifierInfo); + } + + int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags, + boolean removedBySystem) { + return mDeletePackageHelper.deletePackageX(packageName, + PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM, + PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/); + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java index 0c2e082e0a86..5b770aab19ca 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java @@ -146,6 +146,7 @@ public class PackageManagerServiceInjector { private final Singleton<SharedLibrariesImpl> mSharedLibrariesProducer; private final Singleton<CrossProfileIntentFilterHelper> mCrossProfileIntentFilterHelperProducer; private final Singleton<UpdateOwnershipHelper> mUpdateOwnershipHelperProducer; + private final Singleton<PackageMonitorCallbackHelper> mPackageMonitorCallbackHelper; PackageManagerServiceInjector(Context context, PackageManagerTracedLock lock, Installer installer, Object installLock, PackageAbiHelper abiHelper, @@ -186,7 +187,8 @@ public class PackageManagerServiceInjector { Producer<IBackupManager> iBackupManager, Producer<SharedLibrariesImpl> sharedLibrariesProducer, Producer<CrossProfileIntentFilterHelper> crossProfileIntentFilterHelperProducer, - Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer) { + Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer, + Producer<PackageMonitorCallbackHelper> packageMonitorCallbackHelper) { mContext = context; mLock = lock; mInstaller = installer; @@ -242,6 +244,7 @@ public class PackageManagerServiceInjector { mCrossProfileIntentFilterHelperProducer = new Singleton<>( crossProfileIntentFilterHelperProducer); mUpdateOwnershipHelperProducer = new Singleton<>(updateOwnershipHelperProducer); + mPackageMonitorCallbackHelper = new Singleton<>(packageMonitorCallbackHelper); } /** @@ -431,6 +434,10 @@ public class PackageManagerServiceInjector { return mUpdateOwnershipHelperProducer.get(this, mPackageManager); } + public PackageMonitorCallbackHelper getPackageMonitorCallbackHelper() { + return mPackageMonitorCallbackHelper.get(this, mPackageManager); + } + /** Provides an abstraction to static access to system state. */ public interface SystemWrapper { diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java index bb3bf5360edc..b8c2b8616ea6 100644 --- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java +++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java @@ -52,12 +52,6 @@ class PackageMonitorCallbackHelper { private final Object mLock = new Object(); final IActivityManager mActivityManager = ActivityManager.getService(); - final Handler mHandler; - - PackageMonitorCallbackHelper(PackageManagerServiceInjector injector) { - mHandler = injector.getHandler(); - } - @NonNull @GuardedBy("mLock") private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>(); @@ -100,7 +94,8 @@ class PackageMonitorCallbackHelper { public void notifyPackageAddedForNewUsers(String packageName, @AppIdInt int appId, @NonNull int[] userIds, @NonNull int[] instantUserIds, - boolean isArchived, int dataLoaderType, SparseArray<int[]> broadcastAllowList) { + boolean isArchived, int dataLoaderType, SparseArray<int[]> broadcastAllowList, + @NonNull Handler handler) { Bundle extras = new Bundle(2); // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast final int uid = UserHandle.getUid( @@ -111,11 +106,11 @@ class PackageMonitorCallbackHelper { } extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, packageName, extras , - userIds /* userIds */, instantUserIds, broadcastAllowList); + userIds /* userIds */, instantUserIds, broadcastAllowList, handler); } public void notifyResourcesChanged(boolean mediaStatus, boolean replacing, - @NonNull String[] pkgNames, @NonNull int[] uids) { + @NonNull String[] pkgNames, @NonNull int[] uids, @NonNull Handler handler) { Bundle extras = new Bundle(); extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgNames); extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids); @@ -125,12 +120,12 @@ class PackageMonitorCallbackHelper { String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; notifyPackageMonitor(action, null /* pkg */, extras, null /* userIds */, - null /* instantUserIds */, null /* broadcastAllowList */); + null /* instantUserIds */, null /* broadcastAllowList */, handler); } public void notifyPackageChanged(String packageName, boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason, int[] userIds, - int[] instantUserIds, SparseArray<int[]> broadcastAllowList) { + int[] instantUserIds, SparseArray<int[]> broadcastAllowList, Handler handler) { Bundle extras = new Bundle(4); extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0)); String[] nameList = new String[componentNames.size()]; @@ -142,11 +137,12 @@ class PackageMonitorCallbackHelper { extras.putString(Intent.EXTRA_REASON, reason); } notifyPackageMonitor(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, userIds, - instantUserIds, broadcastAllowList); + instantUserIds, broadcastAllowList, handler); } public void notifyPackageMonitor(String action, String pkg, Bundle extras, - int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList) { + int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList, + Handler handler) { if (!isAllowedCallbackAction(action)) { return; } @@ -160,9 +156,10 @@ class PackageMonitorCallbackHelper { } if (ArrayUtils.isEmpty(instantUserIds)) { - doNotifyCallbacks(action, pkg, extras, resolvedUserIds, broadcastAllowList); + doNotifyCallbacks( + action, pkg, extras, resolvedUserIds, broadcastAllowList, handler); } else { - doNotifyCallbacks(action, pkg, extras, instantUserIds, broadcastAllowList); + doNotifyCallbacks(action, pkg, extras, instantUserIds, broadcastAllowList, handler); } } catch (RemoteException e) { // do nothing @@ -181,7 +178,7 @@ class PackageMonitorCallbackHelper { } private void doNotifyCallbacks(String action, String pkg, Bundle extras, int[] userIds, - SparseArray<int[]> broadcastAllowList) { + SparseArray<int[]> broadcastAllowList, Handler handler) { RemoteCallbackList<IRemoteCallback> callbacks; synchronized (mLock) { callbacks = mCallbacks; @@ -202,7 +199,7 @@ class PackageMonitorCallbackHelper { final int[] allowUids = broadcastAllowList != null ? broadcastAllowList.get(userId) : new int[]{}; - mHandler.post(() -> callbacks.broadcast((callback, user) -> { + handler.post(() -> callbacks.broadcast((callback, user) -> { RegisterUser registerUser = (RegisterUser) user; if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId() != userId)) { diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java index 9f02542a31e4..7ee1772adead 100644 --- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java +++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java @@ -16,24 +16,12 @@ package com.android.server.pm; -import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED; -import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED; -import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; - -import android.annotation.NonNull; -import android.app.ActivityManagerInternal; -import android.app.BroadcastOptions; -import android.content.Intent; -import android.os.Bundle; -import android.os.PowerExemptionManager; import android.util.SparseArray; import android.util.SparseIntArray; import com.android.internal.util.ArrayUtils; -import com.android.server.LocalServices; final class PackageRemovedInfo { - final PackageSender mPackageSender; String mRemovedPackage; String mInstallerPackageName; int mUid = -1; @@ -58,116 +46,6 @@ final class PackageRemovedInfo { InstallArgs mArgs = null; private static final int[] EMPTY_INT_ARRAY = new int[0]; - PackageRemovedInfo(PackageSender packageSender) { - mPackageSender = packageSender; - } - - void sendPackageRemovedBroadcasts(boolean killApp, boolean removedBySystem, - boolean isArchived) { - sendPackageRemovedBroadcastInternal(killApp, removedBySystem, isArchived); - } - - void sendSystemPackageUpdatedBroadcasts() { - if (mIsRemovedPackageSystemUpdate) { - sendSystemPackageUpdatedBroadcastsInternal(); - } - } - - private void sendSystemPackageUpdatedBroadcastsInternal() { - Bundle extras = new Bundle(2); - extras.putInt(Intent.EXTRA_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid); - extras.putBoolean(Intent.EXTRA_REPLACING, true); - mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, mRemovedPackage, extras, - 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null); - if (mInstallerPackageName != null) { - mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, - mRemovedPackage, extras, 0 /*flags*/, - mInstallerPackageName, null, null, null, null /* broadcastAllowList */, - null); - mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, - mRemovedPackage, extras, 0 /*flags*/, - mInstallerPackageName, null, null, null, null /* broadcastAllowList */, - null); - } - mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage, - extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null); - mPackageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0, - mRemovedPackage, null, null, null, null /* broadcastAllowList */, - getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle()); - } - - private static @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions( - @PowerExemptionManager.ReasonCode int reasonCode) { - long duration = 10_000; - final ActivityManagerInternal amInternal = - LocalServices.getService(ActivityManagerInternal.class); - if (amInternal != null) { - duration = amInternal.getBootTimeTempAllowListDuration(); - } - final BroadcastOptions bOptions = BroadcastOptions.makeBasic(); - bOptions.setTemporaryAppAllowlist(duration, - TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, - reasonCode, ""); - return bOptions; - } - - private void sendPackageRemovedBroadcastInternal(boolean killApp, boolean removedBySystem, - boolean isArchived) { - Bundle extras = new Bundle(); - final int removedUid = mRemovedAppId >= 0 ? mRemovedAppId : mUid; - extras.putInt(Intent.EXTRA_UID, removedUid); - extras.putBoolean(Intent.EXTRA_DATA_REMOVED, mDataRemoved); - extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, mIsRemovedPackageSystemUpdate); - extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp); - extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem); - final boolean isReplace = mIsUpdate || mIsRemovedPackageSystemUpdate; - if (isReplace || isArchived) { - extras.putBoolean(Intent.EXTRA_REPLACING, true); - } - if (isArchived) { - extras.putBoolean(Intent.EXTRA_ARCHIVAL, true); - } - extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, mRemovedForAllUsers); - - // Send PACKAGE_REMOVED broadcast to the respective installer. - if (mRemovedPackage != null && mInstallerPackageName != null) { - mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, - mRemovedPackage, extras, 0 /*flags*/, - mInstallerPackageName, null, mBroadcastUsers, mInstantUserIds, null, null); - } - if (mIsStaticSharedLib) { - // When uninstalling static shared libraries, only the package's installer needs to be - // sent a PACKAGE_REMOVED broadcast. There are no other intended recipients. - return; - } - if (mRemovedPackage != null) { - mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, - mRemovedPackage, extras, 0, null /*targetPackage*/, null, - mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null); - mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED_INTERNAL, - mRemovedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME, - null /*finishedReceiver*/, mBroadcastUsers, mInstantUserIds, - mBroadcastAllowList, null /*bOptions*/); - if (mDataRemoved && !mIsRemovedPackageSystemUpdate) { - mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, - mRemovedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null, - null, mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null); - mPackageSender.notifyPackageRemoved(mRemovedPackage, removedUid); - } - } - if (mRemovedAppId >= 0) { - // If a system app's updates are uninstalled the UID is not actually removed. Some - // services need to know the package name affected. - if (isReplace) { - extras.putString(Intent.EXTRA_PACKAGE_NAME, mRemovedPackage); - } - - mPackageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED, - null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, - null, null, mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null); - } - } - public void populateBroadcastUsers(PackageSetting deletedPackageSetting) { if (mRemovedUsers == null) { mBroadcastUsers = null; diff --git a/services/core/java/com/android/server/pm/PackageSender.java b/services/core/java/com/android/server/pm/PackageSender.java index 82e1d5f389c5..db83f593d5ee 100644 --- a/services/core/java/com/android/server/pm/PackageSender.java +++ b/services/core/java/com/android/server/pm/PackageSender.java @@ -16,24 +16,7 @@ package com.android.server.pm; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.IIntentReceiver; -import android.os.Bundle; -import android.util.SparseArray; - interface PackageSender { - /** - * @param userIds User IDs where the action occurred on a full application - * @param instantUserIds User IDs where the action occurred on an instant application - */ - void sendPackageBroadcast(String action, String pkg, - Bundle extras, int flags, String targetPkg, - IIntentReceiver finishedReceiver, int[] userIds, int[] instantUserIds, - @Nullable SparseArray<int[]> broadcastAllowList, @Nullable Bundle bOptions); - void sendPackageAddedForNewUsers(@NonNull Computer snapshot, String packageName, - boolean sendBootCompleted, boolean includeStopped, int appId, int[] userIds, - int[] instantUserIds, boolean isArchived, int dataLoaderType); void notifyPackageAdded(String packageName, int uid); void notifyPackageChanged(String packageName, int uid); void notifyPackageRemoved(String packageName, int uid); diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java index 571aab4f6969..41d2aeb9b168 100644 --- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java +++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java @@ -69,10 +69,12 @@ final class PreferredActivityHelper { private static final String TAG_DEFAULT_APPS = "da"; private final PackageManagerService mPm; + private final BroadcastHelper mBroadcastHelper; // TODO(b/198166813): remove PMS dependency - PreferredActivityHelper(PackageManagerService pm) { + PreferredActivityHelper(PackageManagerService pm, BroadcastHelper broadcastHelper) { mPm = pm; + mBroadcastHelper = broadcastHelper; } private ResolveInfo findPreferredActivityNotLocked(@NonNull Computer snapshot, Intent intent, @@ -120,7 +122,7 @@ final class PreferredActivityHelper { } if (changedUsers.size() > 0) { updateDefaultHomeNotLocked(mPm.snapshotComputer(), changedUsers); - mPm.postPreferredActivityChangedBroadcast(userId); + mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId); mPm.scheduleWritePackageRestrictions(userId); } } @@ -167,7 +169,7 @@ final class PreferredActivityHelper { return mPm.setActiveLauncherPackage(packageName, userId, successful -> { if (successful) { - mPm.postPreferredActivityChangedBroadcast(userId); + mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId); } }); } @@ -215,7 +217,7 @@ final class PreferredActivityHelper { } // Re-snapshot after mLock if (!(isHomeFilter(filter) && updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId))) { - mPm.postPreferredActivityChangedBroadcast(userId); + mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId); } } @@ -411,7 +413,7 @@ final class PreferredActivityHelper { if (isHomeFilter(filter)) { updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId); } - mPm.postPreferredActivityChangedBroadcast(userId); + mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId); } public void clearPackagePersistentPreferredActivities(String packageName, int userId) { @@ -426,7 +428,7 @@ final class PreferredActivityHelper { } if (changed) { updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId); - mPm.postPreferredActivityChangedBroadcast(userId); + mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId); mPm.scheduleWritePackageRestrictions(userId); } } @@ -443,7 +445,7 @@ final class PreferredActivityHelper { } if (changed) { updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId); - mPm.postPreferredActivityChangedBroadcast(userId); + mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId); mPm.scheduleWritePackageRestrictions(userId); } } @@ -616,7 +618,7 @@ final class PreferredActivityHelper { mPm.clearPackagePreferredActivitiesLPw(null, changedUsers, userId); } if (changedUsers.size() > 0) { - mPm.postPreferredActivityChangedBroadcast(userId); + mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId); } synchronized (mPm.mLock) { mPm.mSettings.applyDefaultPreferredAppsLPw(userId); diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java index d989c90a0597..3d61f3eea393 100644 --- a/services/core/java/com/android/server/pm/RemovePackageHelper.java +++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java @@ -22,6 +22,7 @@ import static android.os.incremental.IncrementalManager.isIncrementalPath; import static android.os.storage.StorageManager.FLAG_STORAGE_CE; import static android.os.storage.StorageManager.FLAG_STORAGE_DE; import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; + import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE; @@ -69,9 +70,11 @@ final class RemovePackageHelper { private final PermissionManagerServiceInternal mPermissionManager; private final SharedLibrariesImpl mSharedLibraries; private final AppDataHelper mAppDataHelper; + private final BroadcastHelper mBroadcastHelper; // TODO(b/198166813): remove PMS dependency - RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) { + RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper, + BroadcastHelper broadcastHelper) { mPm = pm; mIncrementalManager = mPm.mInjector.getIncrementalManager(); mInstaller = mPm.mInjector.getInstaller(); @@ -79,10 +82,7 @@ final class RemovePackageHelper { mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal(); mSharedLibraries = mPm.mInjector.getSharedLibrariesImpl(); mAppDataHelper = appDataHelper; - } - - RemovePackageHelper(PackageManagerService pm) { - this(pm, new AppDataHelper(pm)); + mBroadcastHelper = broadcastHelper; } public void removeCodePath(File codePath) { @@ -265,7 +265,8 @@ final class RemovePackageHelper { final List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() : Collections.emptyList(); - final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm); + final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm, + mBroadcastHelper); final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManagerInternal.getUserIds() : new int[] {userId}; for (int nextUserId : userIds) { @@ -395,10 +396,10 @@ final class RemovePackageHelper { } if (changedUsers.size() > 0) { final PreferredActivityHelper preferredActivityHelper = - new PreferredActivityHelper(mPm); + new PreferredActivityHelper(mPm, mBroadcastHelper); preferredActivityHelper.updateDefaultHomeNotLocked(mPm.snapshotComputer(), changedUsers); - mPm.postPreferredActivityChangedBroadcast(UserHandle.USER_ALL); + mBroadcastHelper.sendPreferredActivityChangedBroadcast(UserHandle.USER_ALL); } } else if (!deletedPs.isSystem() && outInfo != null && !outInfo.mIsUpdate && outInfo.mRemovedUsers != null) { diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java index db5b9b199b85..c725cdca9d76 100644 --- a/services/core/java/com/android/server/pm/StorageEventHelper.java +++ b/services/core/java/com/android/server/pm/StorageEventHelper.java @@ -147,7 +147,6 @@ public final class StorageEventHelper extends StorageEventListener { final Settings.VersionInfo ver; final List<? extends PackageStateInternal> packages; - final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm); synchronized (mPm.mLock) { ver = mPm.mSettings.findOrCreateVersion(volumeUuid); packages = mPm.mSettings.getVolumePackagesLPr(volumeUuid); @@ -160,7 +159,7 @@ public final class StorageEventHelper extends StorageEventListener { synchronized (mPm.mInstallLock) { final AndroidPackage pkg; try { - pkg = installPackageHelper.initPackageTracedLI( + pkg = mPm.initPackageTracedLI( ps.getPath(), parseFlags, SCAN_INITIAL); loaded.add(pkg); @@ -228,7 +227,8 @@ public final class StorageEventHelper extends StorageEventListener { } if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded); - sendResourcesChangedBroadcast(true /* mediaStatus */, false /* replacing */, loaded); + mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(), + true /* mediaStatus */, false /* replacing */, loaded); synchronized (mLoadedVolumes) { mLoadedVolumes.add(vol.getId()); } @@ -256,7 +256,7 @@ public final class StorageEventHelper extends StorageEventListener { final AndroidPackage pkg = ps.getPkg(); final int deleteFlags = PackageManager.DELETE_KEEP_DATA; - final PackageRemovedInfo outInfo = new PackageRemovedInfo(mPm); + final PackageRemovedInfo outInfo = new PackageRemovedInfo(); try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.getPackageName(), UserHandle.USER_ALL, deleteFlags, @@ -280,7 +280,8 @@ public final class StorageEventHelper extends StorageEventListener { } if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded); - sendResourcesChangedBroadcast(false /* mediaStatus */, false /* replacing */, unloaded); + mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(), + false /* mediaStatus */, false /* replacing */, unloaded); synchronized (mLoadedVolumes) { mLoadedVolumes.remove(vol.getId()); } @@ -295,21 +296,6 @@ public final class StorageEventHelper extends StorageEventListener { } } - private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing, - ArrayList<AndroidPackage> packages) { - final int size = packages.size(); - final String[] packageNames = new String[size]; - final int[] packageUids = new int[size]; - for (int i = 0; i < size; i++) { - final AndroidPackage pkg = packages.get(i); - packageNames[i] = pkg.getPackageName(); - packageUids[i] = pkg.getUid(); - } - mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer, mediaStatus, - replacing, packageNames, packageUids); - mPm.notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids); - } - /** * Examine all apps present on given mounted volume, and destroy apps that * aren't expected, either due to uninstallation or reinstallation on diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java index ddb045df4eaf..29d99a73a034 100644 --- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java +++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java @@ -26,17 +26,13 @@ import static com.android.server.pm.PackageManagerService.TAG; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.app.ActivityManager; import android.app.AppOpsManager; -import android.app.BroadcastOptions; -import android.app.IActivityManager; import android.app.admin.DevicePolicyManagerInternal; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.SuspendDialogInfo; import android.os.Binder; import android.os.Bundle; -import android.os.Handler; import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; @@ -47,7 +43,6 @@ import android.util.ArraySet; import android.util.IntArray; import android.util.Slog; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.server.LocalServices; @@ -207,19 +202,22 @@ public final class SuspendPackageHelper { } }); + final Computer newSnapshot = mPm.snapshotComputer(); if (!notifyPackagesList.isEmpty()) { final String[] changedPackages = notifyPackagesList.toArray(new String[0]); - sendPackagesSuspendedForUser( + mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot, suspended ? Intent.ACTION_PACKAGES_SUSPENDED : Intent.ACTION_PACKAGES_UNSUSPENDED, changedPackages, notifyUids.toArray(), quarantined, userId); - sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId); + mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, changedPackages, + suspended, userId); mPm.scheduleWritePackageRestrictions(userId); } // Send the suspension changed broadcast to ensure suspension state is not stale. if (!changedPackagesList.isEmpty()) { - sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, + mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot, + Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined, userId); } @@ -269,8 +267,10 @@ public final class SuspendPackageHelper { * @return The app extras of the suspended package. */ @Nullable - Bundle getSuspendedPackageAppExtras(@NonNull Computer snapshot, @NonNull String packageName, - int userId, int callingUid) { + static Bundle getSuspendedPackageAppExtras(@NonNull Computer snapshot, + @NonNull String packageName, + int userId, + int callingUid) { final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName, callingUid); if (ps == null) { return null; @@ -299,7 +299,7 @@ public final class SuspendPackageHelper { * suspensions will be removed. * @param userId The user for which the changes are taking place. */ - void removeSuspensionsBySuspendingPackage(@NonNull Computer computer, + void removeSuspensionsBySuspendingPackage(@NonNull Computer snapshot, @NonNull String[] packagesToChange, @NonNull Predicate<String> suspendingPackagePredicate, int userId) { final List<String> unsuspendedPackages = new ArrayList<>(); @@ -307,7 +307,7 @@ public final class SuspendPackageHelper { final ArrayMap<String, ArraySet<String>> pkgToSuspendingPkgsToCommit = new ArrayMap<>(); for (String packageName : packagesToChange) { final PackageStateInternal packageState = - computer.getPackageStateInternal(packageName); + snapshot.getPackageStateInternal(packageName); final PackageUserStateInternal packageUserState = packageState == null ? null : packageState.getUserStateOrDefault(userId); if (packageUserState == null || !packageUserState.isSuspended()) { @@ -350,11 +350,14 @@ public final class SuspendPackageHelper { }); mPm.scheduleWritePackageRestrictions(userId); + final Computer newSnapshot = mPm.snapshotComputer(); if (!unsuspendedPackages.isEmpty()) { final String[] packageArray = unsuspendedPackages.toArray( new String[unsuspendedPackages.size()]); - sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId); - sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED, + mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, packageArray, + false, userId); + mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot, + Intent.ACTION_PACKAGES_UNSUSPENDED, packageArray, unsuspendedUids.toArray(), false, userId); } } @@ -610,38 +613,6 @@ public final class SuspendPackageHelper { } /** - * Send broadcast intents for packages suspension changes. - * - * @param intent The action name of the suspension intent. - * @param pkgList The names of packages which have suspension changes. - * @param uidList The uids of packages which have suspension changes. - * @param userId The user where packages reside. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - void sendPackagesSuspendedForUser(@NonNull String intent, @NonNull String[] pkgList, - @NonNull int[] uidList, boolean quarantined, int userId) { - final Handler handler = mInjector.getHandler(); - final Bundle extras = new Bundle(3); - extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); - extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); - if (quarantined) { - extras.putBoolean(Intent.EXTRA_QUARANTINED, true); - } - final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND; - final Bundle options = new BroadcastOptions() - .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) - .toBundle(); - handler.post(() -> mBroadcastHelper.sendPackageBroadcast(intent, null /* pkg */, - extras, flags, null /* targetPkg */, null /* finishedReceiver */, - new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */, - (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList( - mPm.snapshotComputer(), callingUid, intentExtras), - options)); - mPm.notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId}, - null /* instantUserIds */, null /* broadcastAllowList */); - } - - /** * Suspends packages on behalf of an admin. * * @return array of packages that are unsuspendable, either because admin is not allowed to @@ -756,37 +727,4 @@ public final class SuspendPackageHelper { } return false; } - - private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended, - int userId) { - final Handler handler = mInjector.getHandler(); - final String action = suspended - ? Intent.ACTION_MY_PACKAGE_SUSPENDED - : Intent.ACTION_MY_PACKAGE_UNSUSPENDED; - handler.post(() -> { - final IActivityManager am = ActivityManager.getService(); - if (am == null) { - Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ " - + (suspended ? "" : "UN") + "SUSPENDED broadcasts"); - return; - } - final int[] targetUserIds = new int[] {userId}; - final Computer snapshot = mPm.snapshotComputer(); - for (String packageName : affectedPackages) { - final Bundle appExtras = suspended - ? getSuspendedPackageAppExtras(snapshot, packageName, userId, SYSTEM_UID) - : null; - final Bundle intentExtras; - if (appExtras != null) { - intentExtras = new Bundle(1); - intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras); - } else { - intentExtras = null; - } - mBroadcastHelper.doSendBroadcast(action, null, intentExtras, - Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null, - targetUserIds, false, null, null, null); - } - }); - } } diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java index 8c73ce8b6b95..c6435aeaba4d 100644 --- a/services/core/java/com/android/server/pm/VerifyingSession.java +++ b/services/core/java/com/android/server/pm/VerifyingSession.java @@ -139,7 +139,6 @@ final class VerifyingSession { private final UserHandle mUser; @NonNull private final PackageManagerService mPm; - private final InstallPackageHelper mInstallPackageHelper; VerifyingSession(UserHandle user, File stagedDir, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, InstallSource installSource, @@ -147,7 +146,6 @@ final class VerifyingSession { boolean userActionRequired, PackageManagerService pm) { mPm = pm; mUser = user; - mInstallPackageHelper = new InstallPackageHelper(mPm); mOriginInfo = OriginInfo.fromStagedFile(stagedDir); mObserver = observer; mInstallFlags = sessionParams.installFlags; @@ -181,7 +179,7 @@ final class VerifyingSession { PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext, mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride); - Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode( + Pair<Integer, String> ret = mPm.verifyReplacingVersionCode( pkgLite, mRequiredInstalledVersionCode, mInstallFlags); setReturnCode(ret.first, ret.second); if (mRet != INSTALL_SUCCEEDED) { @@ -729,7 +727,7 @@ final class VerifyingSession { continue; } - final int verifierUid = mInstallPackageHelper.getUidForVerifier(verifierInfo); + final int verifierUid = mPm.getUidForVerifier(verifierInfo); if (verifierUid == -1) { continue; } diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java index 87809916bcaf..c9db343697df 100644 --- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java +++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java @@ -105,6 +105,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.safetycenter.SafetyCenterManager; import android.service.voice.VoiceInteractionManagerInternal; import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; @@ -173,6 +174,8 @@ public final class SensorPrivacyService extends SystemService { private CallStateHelper mCallStateHelper; private KeyguardManager mKeyguardManager; + private SafetyCenterManager mSafetyCenterManager; + private int mCurrentUser = USER_NULL; public SensorPrivacyService(Context context) { @@ -188,6 +191,7 @@ public final class SensorPrivacyService extends SystemService { mTelephonyManager = context.getSystemService(TelephonyManager.class); mPackageManagerInternal = getLocalService(PackageManagerInternal.class); mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl(); + mSafetyCenterManager = mContext.getSystemService(SafetyCenterManager.class); } @Override @@ -652,8 +656,11 @@ public final class SensorPrivacyService extends SystemService { String contentTitle = getUiContext().getString(messageRes); Spanned contentText = Html.fromHtml(getUiContext().getString( R.string.sensor_privacy_start_use_notification_content_text, packageLabel), 0); + String action = mSafetyCenterManager.isSafetyCenterEnabled() + ? Settings.ACTION_PRIVACY_CONTROLS : Settings.ACTION_PRIVACY_SETTINGS; + PendingIntent contentIntent = PendingIntent.getActivity(mContext, sensor, - new Intent(Settings.ACTION_PRIVACY_SETTINGS), + new Intent(action), PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java index 96f4a01f7f3a..c2666f63d7a6 100644 --- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java +++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java @@ -297,7 +297,8 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn return; } - for (ClientState clientState : mClients.values()) { + + for (ClientState clientState : mClients.values().toArray(new ClientState[0])) { tryRespondWithError( clientState.mDelegatingListener.mRemoteListener, SpeechRecognizer.ERROR_SERVER_DISCONNECTED); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index d8245348d788..a0c78702fec5 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2887,7 +2887,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final boolean animate; if (mStartingData != null) { if (mStartingData.mWaitForSyncTransactionCommit - || mTransitionController.inCollectingTransition(startingWindow)) { + || mTransitionController.isCollecting(this)) { mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_REMOVE_DIRECTLY; mStartingData.mPrepareRemoveAnimation = prepareAnimation; return; diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 1ad57986353c..901975ba3614 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -795,10 +795,19 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { return false; } - // Try pausing the existing resumed activity in the Task if any. final Task task = r.getTask(); - if (task.pauseActivityIfNeeded(r, "realStart")) { - return false; + if (andResume) { + // Try pausing the existing resumed activity in the Task if any. + if (task.pauseActivityIfNeeded(r, "realStart")) { + return false; + } + final TaskFragment taskFragment = r.getTaskFragment(); + if (taskFragment != null && taskFragment.getResumedActivity() != null) { + if (taskFragment.startPausing(mUserLeaving, false /* uiSleeping */, r, + "realStart")) { + return false; + } + } } final Task rootTask = task.getRootTask(); diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java index 4b463c7e1756..b87e761e5795 100644 --- a/services/core/java/com/android/server/wm/AsyncRotationController.java +++ b/services/core/java/com/android/server/wm/AsyncRotationController.java @@ -275,15 +275,18 @@ class AsyncRotationController extends FadeAnimationController implements Consume // The previous animation leash will be dropped when preparing fade-in animation, so // simply apply new animation without restoring the transformation. fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM); - } else if (op.mAction == Operation.ACTION_SEAMLESS - && op.mLeash != null && op.mLeash.isValid()) { + } else if (op.isValidSeamless()) { if (DEBUG) Slog.d(TAG, "finishOp undo seamless " + windowToken.getTopChild()); final SurfaceControl.Transaction t = windowToken.getSyncTransaction(); - t.setMatrix(op.mLeash, 1, 0, 0, 1); - t.setPosition(op.mLeash, 0, 0); + clearTransform(t, op.mLeash); } } + private static void clearTransform(SurfaceControl.Transaction t, SurfaceControl sc) { + t.setMatrix(sc, 1, 0, 0, 1); + t.setPosition(sc, 0, 0); + } + /** * Completes all operations such as applying fade-in animation on the previously hidden window * tokens. This is called if all windows are ready in new rotation or timed out. @@ -400,7 +403,19 @@ class AsyncRotationController extends FadeAnimationController implements Consume synchronized (mService.mGlobalLock) { Slog.i(TAG, "Async rotation timeout: " + (!mIsStartTransactionCommitted ? " start transaction is not committed" : mTargetWindowTokens)); - mIsStartTransactionCommitted = true; + if (!mIsStartTransactionCommitted) { + // The transaction commit timeout will be handled by: + // 1. BLASTSyncEngine will notify onTransactionCommitTimeout() and then + // apply the start transaction of transition. + // 2. The TransactionCommittedListener in setupStartTransaction() will be + // notified to finish the operations of mTargetWindowTokens. + // 3. The slow remote side will also apply the start transaction which may + // contain stale surface transform. + // 4. Finally, the slow remote reports transition finished. The cleanup + // transaction from step (1) will be applied when finishing transition, + // which will recover the stale state from (3). + return; + } mDisplayContent.finishAsyncRotationIfPossible(); mService.mWindowPlacerLocked.performSurfacePlacement(); } @@ -539,6 +554,20 @@ class AsyncRotationController extends FadeAnimationController implements Consume }); } + /** Called when the start transition is ready, but it is not applied in time. */ + void onTransactionCommitTimeout(SurfaceControl.Transaction t) { + if (mIsStartTransactionCommitted) return; + for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { + final Operation op = mTargetWindowTokens.valueAt(i); + op.mIsCompletionPending = true; + if (op.isValidSeamless()) { + Slog.d(TAG, "Transaction timeout. Clear transform for " + + mTargetWindowTokens.keyAt(i).getTopChild()); + clearTransform(t, op.mLeash); + } + } + } + /** Called when the transition by shell is done. */ void onTransitionFinished() { if (mTransitionOp == OP_CHANGE) { @@ -681,6 +710,10 @@ class AsyncRotationController extends FadeAnimationController implements Consume mAction = action; } + boolean isValidSeamless() { + return mAction == ACTION_SEAMLESS && mLeash != null && mLeash.isValid(); + } + @Override public String toString() { return "Operation{a=" + mAction + " pending=" + mIsCompletionPending + '}'; diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java index 98ee98ba67f7..444470952c5d 100644 --- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java +++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java @@ -95,6 +95,7 @@ class BLASTSyncEngine { interface TransactionReadyListener { void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction); + default void onTransactionCommitTimeout() {} } /** @@ -249,6 +250,7 @@ class BLASTSyncEngine { " commit callback. Application ANR likely to follow."); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); synchronized (mWm.mGlobalLock) { + mListener.onTransactionCommitTimeout(); onCommitted(merged.mNativeObject != 0 ? merged : mWm.mTransactionFactory.get()); } diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 4bafccdaaec6..e97bda218dcd 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -418,7 +418,7 @@ public class BackgroundActivityStartController { callerAppUid = realCallingUid; } // don't abort if the callerApp or other processes of that uid are allowed in any way - if (callerApp != null && useCallingUidState) { + if (callerApp != null && (useCallingUidState || callerAppBasedOnPiSender)) { // first check the original calling process final @BalCode int balAllowedForCaller = callerApp .areBackgroundActivityStartsAllowed(appSwitchState); @@ -509,6 +509,7 @@ public class BackgroundActivityStartController { + "; intent: " + intent + "; callerApp: " + callerApp + "; inVisibleTask: " + (callerApp != null && callerApp.hasActivityInVisibleTask()) + + "; resultIfPiSenderAllowsBal: " + balCodeToString(resultIfPiSenderAllowsBal) + "]"; if (resultIfPiSenderAllowsBal != BAL_BLOCK) { // We should have returned before if !logVerdictChangeByPiDefaultChange diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4309e72c30d7..ca42400dad26 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2160,6 +2160,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } /** + * @see DisplayWindowPolicyController#getCustomHomeComponent() () + */ + @Nullable ComponentName getCustomHomeComponent() { + if (!supportsSystemDecorations() || mDwpcHelper == null) { + return null; + } + return mDwpcHelper.getCustomHomeComponent(); + } + + /** * Applies the rotation transaction. This must be called after {@link #updateRotationUnchecked} * (if it returned {@code true}) to actually finish the rotation. * diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index d461d1ec02f0..a1b8949c2582 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -130,6 +130,7 @@ public class DisplayRotation { private final int mUndockedHdmiRotation; private final RotationAnimationPair mTmpRotationAnim = new RotationAnimationPair(); private final RotationHistory mRotationHistory = new RotationHistory(); + private final RotationLockHistory mRotationLockHistory = new RotationLockHistory(); private OrientationListener mOrientationListener; private StatusBarManagerInternal mStatusBarManagerInternal; @@ -922,7 +923,8 @@ public class DisplayRotation { } @VisibleForTesting - void setUserRotation(int userRotationMode, int userRotation) { + void setUserRotation(int userRotationMode, int userRotation, String caller) { + mRotationLockHistory.addRecord(userRotationMode, userRotation, caller); mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED; if (useDefaultSettingsProvider()) { // We'll be notified via settings listener, so we don't need to update internal values. @@ -953,17 +955,17 @@ public class DisplayRotation { } } - void freezeRotation(int rotation) { + void freezeRotation(int rotation, String caller) { if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis(mDisplayContent)) { rotation = RotationUtils.reverseRotationDirectionAroundZAxis(rotation); } rotation = (rotation == -1) ? mRotation : rotation; - setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation); + setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation, caller); } - void thawRotation() { - setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation); + void thawRotation(String caller) { + setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation, caller); } boolean isRotationFrozen() { @@ -1712,6 +1714,15 @@ public class DisplayRotation { r.dump(prefix, pw); } } + + if (!mRotationLockHistory.mRecords.isEmpty()) { + pw.println(); + pw.println(prefix + " RotationLockHistory"); + prefix = " " + prefix; + for (RotationLockHistory.Record r : mRotationLockHistory.mRecords) { + r.dump(prefix, pw); + } + } } void dumpDebug(ProtoOutputStream proto, long fieldId) { @@ -2133,6 +2144,40 @@ public class DisplayRotation { } } + private static class RotationLockHistory { + private static final int MAX_SIZE = 8; + + private static class Record { + @WindowManagerPolicy.UserRotationMode final int mUserRotationMode; + @Surface.Rotation final int mUserRotation; + final String mCaller; + final long mTimestamp = System.currentTimeMillis(); + + private Record(int userRotationMode, int userRotation, String caller) { + mUserRotationMode = userRotationMode; + mUserRotation = userRotation; + mCaller = caller; + } + + void dump(String prefix, PrintWriter pw) { + pw.println(prefix + TimeUtils.logTimeOfDay(mTimestamp) + ": " + + "mode=" + WindowManagerPolicy.userRotationModeToString(mUserRotationMode) + + ", rotation=" + Surface.rotationToString(mUserRotation) + + ", caller=" + mCaller); + } + } + + private final ArrayDeque<RotationLockHistory.Record> mRecords = new ArrayDeque<>(MAX_SIZE); + + void addRecord(@WindowManagerPolicy.UserRotationMode int userRotationMode, + @Surface.Rotation int userRotation, String caller) { + if (mRecords.size() >= MAX_SIZE) { + mRecords.removeFirst(); + } + mRecords.addLast(new Record(userRotationMode, userRotation, caller)); + } + } + private static class RotationHistory { private static final int MAX_SIZE = 8; private static final int NO_FOLD_CONTROLLER = -2; diff --git a/services/core/java/com/android/server/wm/DisplayRotationReversionController.java b/services/core/java/com/android/server/wm/DisplayRotationReversionController.java index d3a8a82f8f87..4eb2d88cb4eb 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationReversionController.java +++ b/services/core/java/com/android/server/wm/DisplayRotationReversionController.java @@ -118,7 +118,8 @@ final class DisplayRotationReversionController { if (mDisplayContent.getDisplayRotation().isRotationFrozen()) { mDisplayContent.getDisplayRotation().setUserRotation( mUserRotationModeOverridden, - mUserRotationOverridden); + mUserRotationOverridden, + /* caller= */ "DisplayRotationReversionController#revertOverride"); return true; } else { return false; diff --git a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java index 6b33746ad3c4..e0d69b063573 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java +++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java @@ -19,6 +19,7 @@ package com.android.server.wm; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; +import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.Process; @@ -200,6 +201,17 @@ class DisplayWindowPolicyControllerHelper { return mDisplayWindowPolicyController.isEnteringPipAllowed(uid); } + /** + * @see DisplayWindowPolicyController#getCustomHomeComponent + */ + @Nullable + public ComponentName getCustomHomeComponent() { + if (mDisplayWindowPolicyController == null) { + return null; + } + return mDisplayWindowPolicyController.getCustomHomeComponent(); + } + void dump(String prefix, PrintWriter pw) { if (mDisplayWindowPolicyController != null) { pw.println(); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 2fdfec04b895..2a3391807a2c 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1513,10 +1513,30 @@ class RootWindowContainer extends WindowContainer<DisplayContent> throw new IllegalArgumentException( "resolveSecondaryHomeActivity: Should not be default task container"); } - // Resolve activities in the same package as currently selected primary home activity. + Intent homeIntent = mService.getHomeIntent(); ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent); - if (aInfo != null) { + boolean lookForSecondaryHomeActivityInPrimaryHomePackage = aInfo != null; + + if (android.companion.virtual.flags.Flags.vdmCustomHome()) { + // Resolve the externally set home activity for this display, if any. If it is unset or + // we fail to resolve it, fallback to the default secondary home activity. + final ComponentName customHomeComponent = + taskDisplayArea.getDisplayContent() != null + ? taskDisplayArea.getDisplayContent().getCustomHomeComponent() + : null; + if (customHomeComponent != null) { + homeIntent.setComponent(customHomeComponent); + ActivityInfo customHomeActivityInfo = resolveHomeActivity(userId, homeIntent); + if (customHomeActivityInfo != null) { + aInfo = customHomeActivityInfo; + lookForSecondaryHomeActivityInPrimaryHomePackage = false; + } + } + } + + if (lookForSecondaryHomeActivityInPrimaryHomePackage) { + // Resolve activities in the same package as currently selected primary home activity. if (ResolverActivity.class.getName().equals(aInfo.name)) { // Always fallback to secondary home component if default home is not set. aInfo = null; diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 2c142cbed585..bbb85636f1ee 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -40,9 +40,11 @@ import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.hardware.HardwareBuffer; +import android.os.IBinder; import android.os.Trace; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import android.view.DisplayAddress; import android.view.DisplayInfo; import android.view.Surface; import android.view.Surface.OutOfResourcesException; @@ -55,6 +57,7 @@ import android.window.ScreenCapture; import com.android.internal.R; import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.common.ProtoLog; +import com.android.server.display.DisplayControl; import com.android.server.wm.SurfaceAnimator.AnimationType; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; @@ -168,10 +171,32 @@ class ScreenRotationAnimation { try { final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer; if (isSizeChanged) { + final DisplayAddress address = displayInfo.address; + if (!(address instanceof DisplayAddress.Physical)) { + Slog.e(TAG, "Display does not have a physical address: " + displayId); + return; + } + final DisplayAddress.Physical physicalAddress = + (DisplayAddress.Physical) address; + final IBinder displayToken = DisplayControl.getPhysicalDisplayToken( + physicalAddress.getPhysicalDisplayId()); + if (displayToken == null) { + Slog.e(TAG, "Display token is null."); + return; + } // Temporarily not skip screenshot for the rounded corner overlays and screenshot // the whole display to include the rounded corner overlays. setSkipScreenshotForRoundedCornerOverlays(false, t); - } + mRoundedCornerOverlay = displayContent.findRoundedCornerOverlays(); + final ScreenCapture.DisplayCaptureArgs captureArgs = + new ScreenCapture.DisplayCaptureArgs.Builder(displayToken) + .setSourceCrop(new Rect(0, 0, width, height)) + .setAllowProtected(true) + .setCaptureSecureLayers(true) + .setHintForSeamlessTransition(true) + .build(); + screenshotBuffer = ScreenCapture.captureDisplay(captureArgs); + } else { ScreenCapture.LayerCaptureArgs captureArgs = new ScreenCapture.LayerCaptureArgs.Builder( displayContent.getSurfaceControl()) @@ -181,6 +206,7 @@ class ScreenRotationAnimation { .setHintForSeamlessTransition(true) .build(); screenshotBuffer = ScreenCapture.captureLayers(captureArgs); + } if (screenshotBuffer == null) { Slog.w(TAG, "Unable to take screenshot of display " + displayId); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 0674ec1e8b82..bbe44c540c39 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -42,6 +42,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PendingIntent; import android.content.ClipData; @@ -100,6 +101,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final IWindowSessionCallback mCallback; final int mUid; final int mPid; + @NonNull + final WindowProcessController mProcess; private final String mStringName; SurfaceSession mSurfaceSession; private int mNumWindow = 0; @@ -126,11 +129,23 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final boolean mSetsUnrestrictedKeepClearAreas; public Session(WindowManagerService service, IWindowSessionCallback callback) { + this(service, callback, Binder.getCallingPid(), Binder.getCallingUid()); + } + + @VisibleForTesting + Session(WindowManagerService service, IWindowSessionCallback callback, + int callingPid, int callingUid) { mService = service; mCallback = callback; - mUid = Binder.getCallingUid(); - mPid = Binder.getCallingPid(); - mLastReportedAnimatorScale = service.getCurrentAnimatorScale(); + mPid = callingPid; + mUid = callingUid; + synchronized (service.mGlobalLock) { + mLastReportedAnimatorScale = service.getCurrentAnimatorScale(); + mProcess = service.mAtmService.mProcessMap.getProcess(mPid); + } + if (mProcess == null) { + throw new IllegalStateException("Unknown pid=" + mPid + " uid=" + mUid); + } mCanAddInternalSystemWindow = service.mContext.checkCallingOrSelfPermission( INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED; mCanForceShowingInsets = service.mAtmService.isCallerRecents(mUid) @@ -715,13 +730,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { void windowAddedLocked() { if (mPackageName == null) { - final WindowProcessController wpc = mService.mAtmService.mProcessMap.getProcess(mPid); - if (wpc != null) { - mPackageName = wpc.mInfo.packageName; - mRelayoutTag = "relayoutWindow: " + mPackageName; - } else { - Slog.e(TAG_WM, "Unknown process pid=" + mPid); - } + mPackageName = mProcess.mInfo.packageName; + mRelayoutTag = "relayoutWindow: " + mPackageName; } if (mSurfaceSession == null) { if (DEBUG) { diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java index 2d281c476ec2..07ffa69e462a 100644 --- a/services/core/java/com/android/server/wm/StartingData.java +++ b/services/core/java/com/android/server/wm/StartingData.java @@ -108,4 +108,13 @@ public abstract class StartingData { boolean hasImeSurface() { return false; } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + Integer.toHexString(System.identityHashCode(this)) + + " waitForSyncTransactionCommit=" + mWaitForSyncTransactionCommit + + " removeAfterTransaction= " + mRemoveAfterTransaction + + "}"; + } } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index bbafa25eac5b..fac98b84d976 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1683,6 +1683,18 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } + @Override + public void onTransactionCommitTimeout() { + if (mCleanupTransaction == null) return; + for (int i = mTargetDisplays.size() - 1; i >= 0; --i) { + final DisplayContent dc = mTargetDisplays.get(i); + final AsyncRotationController asyncRotationController = dc.getAsyncRotationController(); + if (asyncRotationController != null && containsChangeFor(dc, mTargets)) { + asyncRotationController.onTransactionCommitTimeout(mCleanupTransaction); + } + } + } + /** * Collect tasks which moved-to-top as part of this transition. This also updates the * controller's latest-reported when relevant. diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index a4d43d8ef6d0..9f1bccb9a27a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -45,7 +45,6 @@ import android.view.SurfaceControlViewHost; import android.view.WindowInfo; import android.view.WindowManager.DisplayImePolicy; import android.view.inputmethod.ImeTracker; -import android.window.ScreenCapture; import com.android.internal.policy.KeyInterceptionInfo; import com.android.server.input.InputManagerService; @@ -957,14 +956,6 @@ public abstract class WindowManagerInternal { public abstract SurfaceControl getA11yOverlayLayer(int displayId); /** - * Captures the entire display specified by the displayId using the args provided. If the args - * are null or if the sourceCrop is invalid or null, the entire display bounds will be captured. - */ - public abstract void captureDisplay(int displayId, - @Nullable ScreenCapture.CaptureArgs captureArgs, - ScreenCapture.ScreenCaptureListener listener); - - /** * Device has a software navigation bar (separate from the status bar) on specific display. * * @param displayId the id of display to check if there is a software navigation bar. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8fe104c23312..a6d285a110f9 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4281,8 +4281,8 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void freezeRotation(int rotation) { - freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation); + public void freezeRotation(int rotation, String caller) { + freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation, caller); } /** @@ -4292,7 +4292,7 @@ public class WindowManagerService extends IWindowManager.Stub * @param rotation The desired rotation to freeze to, or -1 to use the current rotation. */ @Override - public void freezeDisplayRotation(int displayId, int rotation) { + public void freezeDisplayRotation(int displayId, int rotation, String caller) { // TODO(multi-display): Track which display is rotated. if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, "freezeRotation()")) { @@ -4302,6 +4302,9 @@ public class WindowManagerService extends IWindowManager.Stub throw new IllegalArgumentException("Rotation argument must be -1 or a valid " + "rotation constant."); } + ProtoLog.v(WM_DEBUG_ORIENTATION, + "freezeDisplayRotation: current rotation=%d, new rotation=%d, caller=%s", + getDefaultDisplayRotation(), rotation, caller); final long origId = Binder.clearCallingIdentity(); try { @@ -4311,7 +4314,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.w(TAG, "Trying to freeze rotation for a missing display."); return; } - display.getDisplayRotation().freezeRotation(rotation); + display.getDisplayRotation().freezeRotation(rotation, caller); } } finally { Binder.restoreCallingIdentity(origId); @@ -4321,8 +4324,8 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void thawRotation() { - thawDisplayRotation(Display.DEFAULT_DISPLAY); + public void thawRotation(String caller) { + thawDisplayRotation(Display.DEFAULT_DISPLAY, caller); } /** @@ -4330,13 +4333,14 @@ public class WindowManagerService extends IWindowManager.Stub * Persists across reboots. */ @Override - public void thawDisplayRotation(int displayId) { + public void thawDisplayRotation(int displayId, String caller) { if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, "thawRotation()")) { throw new SecurityException("Requires SET_ORIENTATION permission"); } - ProtoLog.v(WM_DEBUG_ORIENTATION, "thawRotation: mRotation=%d", getDefaultDisplayRotation()); + ProtoLog.v(WM_DEBUG_ORIENTATION, "thawRotation: mRotation=%d, caller=%s", + getDefaultDisplayRotation(), caller); final long origId = Binder.clearCallingIdentity(); try { @@ -4346,7 +4350,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.w(TAG, "Trying to thaw rotation for a missing display."); return; } - display.getDisplayRotation().thawRotation(); + display.getDisplayRotation().thawRotation(caller); } } finally { Binder.restoreCallingIdentity(origId); @@ -8415,12 +8419,6 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void captureDisplay(int displayId, @Nullable ScreenCapture.CaptureArgs captureArgs, - ScreenCapture.ScreenCaptureListener listener) { - WindowManagerService.this.captureDisplay(displayId, captureArgs, listener); - } - - @Override public boolean hasNavigationBar(int displayId) { return WindowManagerService.this.hasNavigationBar(displayId); } diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 74a0bafd3a4c..8fad9509af44 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -438,7 +438,8 @@ public class WindowManagerShellCommand extends ShellCommand { } if ("free".equals(lockMode)) { - mInternal.thawDisplayRotation(displayId); + mInternal.thawDisplayRotation(displayId, + /* caller= */ "WindowManagerShellCommand#free"); return 0; } @@ -451,7 +452,8 @@ public class WindowManagerShellCommand extends ShellCommand { try { final int rotation = arg != null ? Integer.parseInt(arg) : -1 /* lock to current rotation */; - mInternal.freezeDisplayRotation(displayId, rotation); + mInternal.freezeDisplayRotation(displayId, rotation, + /* caller= */ "WindowManagerShellCommand#lock"); return 0; } catch (IllegalArgumentException e) { getErrPrintWriter().println("Error: " + e.getMessage()); @@ -1433,7 +1435,8 @@ public class WindowManagerShellCommand extends ShellCommand { mInterface.setForcedDisplayScalingMode(displayId, DisplayContent.FORCE_SCALING_MODE_AUTO); // user-rotation - mInternal.thawDisplayRotation(displayId); + mInternal.thawDisplayRotation(displayId, + /* caller= */ "WindowManagerShellCommand#runReset"); // fixed-to-user-rotation mInterface.setFixedToUserRotation(displayId, IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index b12cc0b30f53..ebef606a8d60 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -754,8 +754,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */ - private final WindowProcessController mWpcForDisplayAreaConfigChanges; - class DrawHandler { Consumer<SurfaceControl.Transaction> mConsumer; int mSeqId; @@ -1129,7 +1127,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mBaseLayer = 0; mSubLayer = 0; mWinAnimator = null; - mWpcForDisplayAreaConfigChanges = null; mOverrideScale = 1f; return; } @@ -1186,11 +1183,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", this, parentWindow); parentWindow.addChild(this, sWindowSubLayerComparator); } - - // System process or invalid process cannot register to display area config change. - mWpcForDisplayAreaConfigChanges = (s.mPid == MY_PID || s.mPid < 0) - ? null - : service.mAtmService.getProcessController(s.mPid, s.mUid); } boolean shouldWindowHandleBeTrusted(Session s) { @@ -3621,14 +3613,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** @return {@code true} if the process registered to a display area as a config listener. */ private boolean registeredForDisplayAreaConfigChanges() { final WindowState parentWindow = getParentWindow(); - final WindowProcessController wpc = parentWindow != null - ? parentWindow.mWpcForDisplayAreaConfigChanges - : mWpcForDisplayAreaConfigChanges; - return wpc != null && wpc.registeredForDisplayAreaConfigChanges(); + final Session session = parentWindow != null ? parentWindow.mSession : mSession; + if (session.mPid == MY_PID) { + // System process cannot register to display area config change. + return false; + } + return session.mProcess.registeredForDisplayAreaConfigChanges(); } + @NonNull WindowProcessController getProcess() { - return mWpcForDisplayAreaConfigChanges; + return mSession.mProcess; } /** diff --git a/services/core/jni/TEST_MAPPING b/services/core/jni/TEST_MAPPING index ea44d06b9fc3..eb9db702f7f9 100644 --- a/services/core/jni/TEST_MAPPING +++ b/services/core/jni/TEST_MAPPING @@ -6,7 +6,7 @@ ], "name": "CtsVibratorTestCases", "options": [ - {"exclude-annotation": "android.platform.test.annotations.LargeTest"}, + {"exclude-annotation": "androidx.test.filters.LargeTest"}, {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"}, {"exclude-annotation": "org.junit.Ignore"} diff --git a/services/devicepolicy/TEST_MAPPING b/services/devicepolicy/TEST_MAPPING index 72bba11c5366..fccd1ecf23c0 100644 --- a/services/devicepolicy/TEST_MAPPING +++ b/services/devicepolicy/TEST_MAPPING @@ -7,7 +7,7 @@ "exclude-annotation": "android.platform.test.annotations.FlakyTest" }, { - "exclude-annotation": "android.platform.test.annotations.LargeTest" + "exclude-annotation": "androidx.test.filters.LargeTest" } ] } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 84d1a452fa7e..f60493289bfb 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -16017,16 +16017,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override - public PackagePolicy getCredentialManagerPolicy() { + public PackagePolicy getCredentialManagerPolicy(int userId) { if (!mHasFeature) { return null; } final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( canWriteCredentialManagerPolicy(caller) || canQueryAdminPolicy(caller)); + if (userId != caller.getUserId()) { + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS)); + } synchronized (getLockObject()) { - ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()); + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(userId); return (admin != null) ? admin.mCredentialManagerPolicy : null; } } diff --git a/services/foldables/devicestateprovider/Android.bp b/services/foldables/devicestateprovider/Android.bp new file mode 100644 index 000000000000..34737eff8e6d --- /dev/null +++ b/services/foldables/devicestateprovider/Android.bp @@ -0,0 +1,13 @@ +package { + default_applicable_licenses: ["frameworks_base_license"], +} + +java_library { + name: "foldable-device-state-provider", + srcs: [ + "src/**/*.java" + ], + libs: [ + "services", + ], +} diff --git a/services/foldables/devicestateprovider/OWNERS b/services/foldables/devicestateprovider/OWNERS new file mode 100644 index 000000000000..573284419a48 --- /dev/null +++ b/services/foldables/devicestateprovider/OWNERS @@ -0,0 +1,6 @@ +akulian@google.com +jiamingliu@google.com +kchyn@google.com +kennethford@google.com +nickchameyev@google.com +nicomazz@google.com
\ No newline at end of file diff --git a/services/foldables/devicestateprovider/README.md b/services/foldables/devicestateprovider/README.md new file mode 100644 index 000000000000..90174c0cdfc7 --- /dev/null +++ b/services/foldables/devicestateprovider/README.md @@ -0,0 +1,3 @@ +# Foldable Device State Provider library + +This library provides foldable-specific classes that could be used to implement a custom DeviceStateProvider.
\ No newline at end of file diff --git a/services/foldables/devicestateprovider/TEST_MAPPING b/services/foldables/devicestateprovider/TEST_MAPPING new file mode 100644 index 000000000000..47de131803c5 --- /dev/null +++ b/services/foldables/devicestateprovider/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "foldable-device-state-provider-tests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + } + ] +} diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java new file mode 100644 index 000000000000..aea46d1ce329 --- /dev/null +++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java @@ -0,0 +1,537 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.policy; + +import static android.hardware.SensorManager.SENSOR_DELAY_FASTEST; +import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; +import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE; +import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE; +import static android.view.Display.DEFAULT_DISPLAY; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.PowerManager; +import android.hardware.display.DisplayManager; +import android.os.Trace; +import android.util.Slog; +import android.util.SparseArray; +import android.view.Display; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; +import com.android.server.devicestate.DeviceState; +import com.android.server.devicestate.DeviceStateProvider; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.function.BooleanSupplier; +import java.util.function.Function; + +/** + * Device state provider for foldable devices. + * + * It is an implementation of {@link DeviceStateProvider} tailored specifically for + * foldable devices and allows simple callback-based configuration with hall sensor + * and hinge angle sensor values. + */ +public final class FoldableDeviceStateProvider implements DeviceStateProvider, + SensorEventListener, PowerManager.OnThermalStatusChangedListener, + DisplayManager.DisplayListener { + + private static final String TAG = "FoldableDeviceStateProvider"; + private static final boolean DEBUG = false; + + // Lock for internal state. + private final Object mLock = new Object(); + + // List of supported states in ascending order based on their identifier. + private final DeviceState[] mOrderedStates; + + // Map of state identifier to a boolean supplier that returns true when all required conditions + // are met for the device to be in the state. + private final SparseArray<BooleanSupplier> mStateConditions = new SparseArray<>(); + + private final Sensor mHingeAngleSensor; + private final DisplayManager mDisplayManager; + private final Sensor mHallSensor; + + @Nullable + @GuardedBy("mLock") + private Listener mListener = null; + @GuardedBy("mLock") + private int mLastReportedState = INVALID_DEVICE_STATE; + @GuardedBy("mLock") + private SensorEvent mLastHingeAngleSensorEvent = null; + @GuardedBy("mLock") + private SensorEvent mLastHallSensorEvent = null; + @GuardedBy("mLock") + private @PowerManager.ThermalStatus + int mThermalStatus = PowerManager.THERMAL_STATUS_NONE; + @GuardedBy("mLock") + private boolean mIsScreenOn = false; + + @GuardedBy("mLock") + private boolean mPowerSaveModeEnabled; + + public FoldableDeviceStateProvider(@NonNull Context context, + @NonNull SensorManager sensorManager, + @NonNull Sensor hingeAngleSensor, + @NonNull Sensor hallSensor, + @NonNull DisplayManager displayManager, + @NonNull DeviceStateConfiguration[] deviceStateConfigurations) { + + Preconditions.checkArgument(deviceStateConfigurations.length > 0, + "Device state configurations array must not be empty"); + + mHingeAngleSensor = hingeAngleSensor; + mHallSensor = hallSensor; + mDisplayManager = displayManager; + + sensorManager.registerListener(this, mHingeAngleSensor, SENSOR_DELAY_FASTEST); + sensorManager.registerListener(this, mHallSensor, SENSOR_DELAY_FASTEST); + + mOrderedStates = new DeviceState[deviceStateConfigurations.length]; + for (int i = 0; i < deviceStateConfigurations.length; i++) { + final DeviceStateConfiguration configuration = deviceStateConfigurations[i]; + mOrderedStates[i] = configuration.mDeviceState; + + if (mStateConditions.get(configuration.mDeviceState.getIdentifier()) != null) { + throw new IllegalArgumentException("Device state configurations must have unique" + + " device state identifiers, found duplicated identifier: " + + configuration.mDeviceState.getIdentifier()); + } + + mStateConditions.put(configuration.mDeviceState.getIdentifier(), () -> + configuration.mPredicate.apply(this)); + } + + mDisplayManager.registerDisplayListener( + /* listener = */ this, + /* handler= */ null, + /* eventsMask= */ DisplayManager.EVENT_FLAG_DISPLAY_CHANGED); + + Arrays.sort(mOrderedStates, Comparator.comparingInt(DeviceState::getIdentifier)); + + PowerManager powerManager = context.getSystemService(PowerManager.class); + if (powerManager != null) { + // If any of the device states are thermal sensitive, i.e. it should be disabled when + // the device is overheating, then we will update the list of supported states when + // thermal status changes. + if (hasThermalSensitiveState(deviceStateConfigurations)) { + powerManager.addThermalStatusListener(this); + } + + // If any of the device states are power sensitive, i.e. it should be disabled when + // power save mode is enabled, then we will update the list of supported states when + // power save mode is toggled. + if (hasPowerSaveSensitiveState(deviceStateConfigurations)) { + IntentFilter filter = new IntentFilter( + PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL); + BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL.equals( + intent.getAction())) { + onPowerSaveModeChanged(powerManager.isPowerSaveMode()); + } + } + }; + context.registerReceiver(receiver, filter); + } + } + } + + @Override + public void setListener(Listener listener) { + synchronized (mLock) { + if (mListener != null) { + throw new RuntimeException("Provider already has a listener set."); + } + mListener = listener; + } + notifySupportedStatesChanged(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED); + notifyDeviceStateChangedIfNeeded(); + } + + /** Notifies the listener that the set of supported device states has changed. */ + private void notifySupportedStatesChanged(@SupportedStatesUpdatedReason int reason) { + List<DeviceState> supportedStates = new ArrayList<>(); + Listener listener; + synchronized (mLock) { + if (mListener == null) { + return; + } + listener = mListener; + for (DeviceState deviceState : mOrderedStates) { + if (isThermalStatusCriticalOrAbove(mThermalStatus) + && deviceState.hasFlag( + DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) { + continue; + } + if (mPowerSaveModeEnabled && deviceState.hasFlag( + DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) { + continue; + } + supportedStates.add(deviceState); + } + } + + listener.onSupportedDeviceStatesChanged( + supportedStates.toArray(new DeviceState[supportedStates.size()]), reason); + } + + /** Computes the current device state and notifies the listener of a change, if needed. */ + void notifyDeviceStateChangedIfNeeded() { + int stateToReport = INVALID_DEVICE_STATE; + Listener listener; + synchronized (mLock) { + if (mListener == null) { + return; + } + + listener = mListener; + + int newState = INVALID_DEVICE_STATE; + for (int i = 0; i < mOrderedStates.length; i++) { + int state = mOrderedStates[i].getIdentifier(); + if (DEBUG) { + Slog.d(TAG, "Checking conditions for " + mOrderedStates[i].getName() + "(" + + i + ")"); + } + boolean conditionSatisfied; + try { + conditionSatisfied = mStateConditions.get(state).getAsBoolean(); + } catch (IllegalStateException e) { + // Failed to compute the current state based on current available data. Continue + // with the expectation that notifyDeviceStateChangedIfNeeded() will be called + // when a callback with the missing data is triggered. May trigger another state + // change if another state is satisfied currently. + Slog.w(TAG, "Unable to check current state = " + state, e); + dumpSensorValues(); + continue; + } + + if (conditionSatisfied) { + if (DEBUG) { + Slog.d(TAG, "Device State conditions satisfied, transition to " + state); + } + newState = state; + break; + } + } + if (newState == INVALID_DEVICE_STATE) { + Slog.e(TAG, "No declared device states match any of the required conditions."); + dumpSensorValues(); + } + + if (newState != INVALID_DEVICE_STATE && newState != mLastReportedState) { + mLastReportedState = newState; + stateToReport = newState; + } + } + + if (stateToReport != INVALID_DEVICE_STATE) { + listener.onStateChanged(stateToReport); + } + } + + @Override + public void onSensorChanged(SensorEvent event) { + synchronized (mLock) { + if (event.sensor == mHallSensor) { + mLastHallSensorEvent = event; + } else if (event.sensor == mHingeAngleSensor) { + mLastHingeAngleSensorEvent = event; + } + } + notifyDeviceStateChangedIfNeeded(); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Do nothing. + } + + private float getSensorValue(@Nullable SensorEvent sensorEvent) { + if (sensorEvent == null) { + throw new IllegalStateException("Have not received sensor event."); + } + + if (sensorEvent.values.length < 1) { + throw new IllegalStateException("Values in the sensor event are empty"); + } + + return sensorEvent.values[0]; + } + + @GuardedBy("mLock") + private void dumpSensorValues() { + Slog.i(TAG, "Sensor values:"); + dumpSensorValues("Hall Sensor", mHallSensor, mLastHallSensorEvent); + dumpSensorValues("Hinge Angle Sensor",mHingeAngleSensor, mLastHingeAngleSensorEvent); + Slog.i(TAG, "isScreenOn: " + isScreenOn()); + } + + @GuardedBy("mLock") + private void dumpSensorValues(String sensorType, Sensor sensor, @Nullable SensorEvent event) { + String sensorString = sensor == null ? "null" : sensor.getName(); + String eventValues = event == null ? "null" : Arrays.toString(event.values); + Slog.i(TAG, sensorType + " : " + sensorString + " : " + eventValues); + } + + @Override + public void onDisplayAdded(int displayId) { + + } + + @Override + public void onDisplayRemoved(int displayId) { + + } + + @Override + public void onDisplayChanged(int displayId) { + if (displayId == DEFAULT_DISPLAY) { + // Could potentially be moved to the background if needed. + try { + Trace.beginSection("FoldableDeviceStateProvider#onDisplayChanged()"); + int displayState = mDisplayManager.getDisplay(displayId).getState(); + synchronized (mLock) { + mIsScreenOn = displayState == Display.STATE_ON; + } + } finally { + Trace.endSection(); + } + } + } + + /** + * Configuration for a single device state, contains information about the state like + * identifier, name, flags and a predicate that should return true if the state should + * be selected. + */ + public static class DeviceStateConfiguration { + private final DeviceState mDeviceState; + private final Function<FoldableDeviceStateProvider, Boolean> mPredicate; + + private DeviceStateConfiguration(DeviceState deviceState, + Function<FoldableDeviceStateProvider, Boolean> predicate) { + mDeviceState = deviceState; + mPredicate = predicate; + } + + public static DeviceStateConfiguration createConfig( + @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier, + @NonNull String name, + @DeviceState.DeviceStateFlags int flags, + Function<FoldableDeviceStateProvider, Boolean> predicate + ) { + return new DeviceStateConfiguration(new DeviceState(identifier, name, flags), + predicate); + } + + public static DeviceStateConfiguration createConfig( + @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier, + @NonNull String name, + Function<FoldableDeviceStateProvider, Boolean> predicate + ) { + return new DeviceStateConfiguration(new DeviceState(identifier, name, /* flags= */ 0), + predicate); + } + + /** + * Creates a device state configuration for a closed tent-mode aware state. + * + * During tent mode: + * - The inner display is OFF + * - The outer display is ON + * - The device is partially unfolded (left and right edges could be on the table) + * In this mode the device the device so it could be used in a posture where both left + * and right edges of the unfolded device are on the table. + * + * The predicate returns false after the hinge angle reaches + * {@code tentModeSwitchAngleDegrees}. Then it switches back only when the hinge angle + * becomes less than {@code maxClosedAngleDegrees}. Hinge angle is 0 degrees when the device + * is fully closed and 180 degrees when it is fully unfolded. + * + * For example, when tentModeSwitchAngleDegrees = 90 and maxClosedAngleDegrees = 5 degrees: + * - when unfolding the device from fully closed posture (last state == closed or it is + * undefined yet) this state will become not matching after reaching the angle + * of 90 degrees, it allows the device to switch the outer display to the inner display + * only when reaching this threshold + * - when folding (last state != 'closed') this state will become matching after reaching + * the angle less than 5 degrees and when hall sensor detected that the device is closed, + * so the switch from the inner display to the outer will become only when the device + * is fully closed. + * + * @param identifier state identifier + * @param name state name + * @param flags state flags + * @param minClosedAngleDegrees minimum (inclusive) hinge angle value for the closed state + * @param maxClosedAngleDegrees maximum (non-inclusive) hinge angle value for the closed + * state + * @param tentModeSwitchAngleDegrees the angle when this state should switch when unfolding + * @return device state configuration + */ + public static DeviceStateConfiguration createTentModeClosedState( + @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier, + @NonNull String name, + @DeviceState.DeviceStateFlags int flags, + int minClosedAngleDegrees, + int maxClosedAngleDegrees, + int tentModeSwitchAngleDegrees + ) { + return new DeviceStateConfiguration(new DeviceState(identifier, name, flags), + (stateContext) -> { + final boolean hallSensorClosed = stateContext.isHallSensorClosed(); + final float hingeAngle = stateContext.getHingeAngle(); + final int lastState = stateContext.getLastReportedDeviceState(); + final boolean isScreenOn = stateContext.isScreenOn(); + + final int switchingDegrees = + isScreenOn ? tentModeSwitchAngleDegrees : maxClosedAngleDegrees; + + final int closedDeviceState = identifier; + final boolean isLastStateClosed = lastState == closedDeviceState + || lastState == INVALID_DEVICE_STATE; + + final boolean shouldBeClosedBecauseTentMode = isLastStateClosed + && hingeAngle >= minClosedAngleDegrees + && hingeAngle < switchingDegrees; + + final boolean shouldBeClosedBecauseFullyShut = hallSensorClosed + && hingeAngle >= minClosedAngleDegrees + && hingeAngle < maxClosedAngleDegrees; + + return shouldBeClosedBecauseFullyShut || shouldBeClosedBecauseTentMode; + }); + } + } + + /** + * @return Whether the screen is on. + */ + public boolean isScreenOn() { + synchronized (mLock) { + return mIsScreenOn; + } + } + /** + * @return current hinge angle value of a foldable device + */ + public float getHingeAngle() { + synchronized (mLock) { + return getSensorValue(mLastHingeAngleSensorEvent); + } + } + + /** + * @return true if hall sensor detected that the device is closed (fully shut) + */ + public boolean isHallSensorClosed() { + synchronized (mLock) { + return getSensorValue(mLastHallSensorEvent) > 0f; + } + } + + /** + * @return last reported device state + */ + public int getLastReportedDeviceState() { + synchronized (mLock) { + return mLastReportedState; + } + } + + @VisibleForTesting + void onPowerSaveModeChanged(boolean isPowerSaveModeEnabled) { + synchronized (mLock) { + if (mPowerSaveModeEnabled != isPowerSaveModeEnabled) { + mPowerSaveModeEnabled = isPowerSaveModeEnabled; + notifySupportedStatesChanged( + isPowerSaveModeEnabled ? SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED + : SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED); + } + } + } + + @Override + public void onThermalStatusChanged(@PowerManager.ThermalStatus int thermalStatus) { + int previousThermalStatus; + synchronized (mLock) { + previousThermalStatus = mThermalStatus; + mThermalStatus = thermalStatus; + } + + boolean isThermalStatusCriticalOrAbove = isThermalStatusCriticalOrAbove(thermalStatus); + boolean isPreviousThermalStatusCriticalOrAbove = + isThermalStatusCriticalOrAbove(previousThermalStatus); + if (isThermalStatusCriticalOrAbove != isPreviousThermalStatusCriticalOrAbove) { + Slog.i(TAG, "Updating supported device states due to thermal status change." + + " isThermalStatusCriticalOrAbove: " + isThermalStatusCriticalOrAbove); + notifySupportedStatesChanged( + isThermalStatusCriticalOrAbove + ? SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL + : SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL); + } + } + + private static boolean isThermalStatusCriticalOrAbove( + @PowerManager.ThermalStatus int thermalStatus) { + switch (thermalStatus) { + case PowerManager.THERMAL_STATUS_CRITICAL: + case PowerManager.THERMAL_STATUS_EMERGENCY: + case PowerManager.THERMAL_STATUS_SHUTDOWN: + return true; + default: + return false; + } + } + + private static boolean hasThermalSensitiveState(DeviceStateConfiguration[] deviceStates) { + for (int i = 0; i < deviceStates.length; i++) { + DeviceStateConfiguration state = deviceStates[i]; + if (state.mDeviceState + .hasFlag(DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) { + return true; + } + } + return false; + } + + private static boolean hasPowerSaveSensitiveState(DeviceStateConfiguration[] deviceStates) { + for (int i = 0; i < deviceStates.length; i++) { + if (deviceStates[i].mDeviceState + .hasFlag(DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) { + return true; + } + } + return false; + } +} diff --git a/services/foldables/devicestateprovider/tests/Android.bp b/services/foldables/devicestateprovider/tests/Android.bp new file mode 100644 index 000000000000..a8db05e99179 --- /dev/null +++ b/services/foldables/devicestateprovider/tests/Android.bp @@ -0,0 +1,30 @@ +package { + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "foldable-device-state-provider-tests", + srcs: ["src/**/*.java"], + libs: [ + "android.test.runner", + "android.test.base", + "android.test.mock", + ], + jni_libs: [ + "libdexmakerjvmtiagent", + "libmultiplejvmtiagentsinterferenceagent", + "libstaticjvmtiagent", + ], + static_libs: [ + "services", + "foldable-device-state-provider", + "androidx.test.rules", + "junit", + "truth-prebuilt", + "mockito-target-extended-minus-junit4", + "androidx.test.uiautomator_uiautomator", + "androidx.test.ext.junit", + "testables", + ], + test_suites: ["device-tests"] +} diff --git a/services/foldables/devicestateprovider/tests/AndroidManifest.xml b/services/foldables/devicestateprovider/tests/AndroidManifest.xml new file mode 100644 index 000000000000..736613d9ad90 --- /dev/null +++ b/services/foldables/devicestateprovider/tests/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?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.foldablesdevicestatelib.tests"> + + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" /> + + <application android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.foldablesdevicestatelib.tests" + android:label="Tests for foldable-device-state-provider library"> + </instrumentation> + +</manifest>
\ No newline at end of file diff --git a/services/foldables/devicestateprovider/tests/AndroidTest.xml b/services/foldables/devicestateprovider/tests/AndroidTest.xml new file mode 100644 index 000000000000..f5fdac75f7cd --- /dev/null +++ b/services/foldables/devicestateprovider/tests/AndroidTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> +<configuration description="foldable-device-state-provider tests"> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="foldable-device-state-provider-tests.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.foldablesdevicestatelib.tests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false" /> + </test> +</configuration>
\ No newline at end of file diff --git a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java new file mode 100644 index 000000000000..8fa4ce592777 --- /dev/null +++ b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.policy; + + +import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED; +import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED; +import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED; +import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL; +import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL; +import com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.STATE_OFF; +import static android.view.Display.STATE_ON; + +import static com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration.createConfig; +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.nullable; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorManager; +import android.hardware.display.DisplayManager; +import android.hardware.input.InputSensorInfo; +import android.os.PowerManager; +import android.os.Handler; +import android.testing.AndroidTestingRunner; +import android.view.Display; + +import com.android.server.devicestate.DeviceState; +import com.android.server.devicestate.DeviceStateProvider; +import com.android.server.devicestate.DeviceStateProvider.Listener; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.internal.util.reflection.FieldSetter; + +/** + * Unit tests for {@link FoldableDeviceStateProvider}. + * <p/> + * Run with <code>atest FoldableDeviceStateProviderTest</code>. + */ +@RunWith(AndroidTestingRunner.class) +public final class FoldableDeviceStateProviderTest { + + private final ArgumentCaptor<DeviceState[]> mDeviceStateArrayCaptor = ArgumentCaptor.forClass( + DeviceState[].class); + @Captor + private ArgumentCaptor<Integer> mIntegerCaptor; + @Captor + private ArgumentCaptor<DisplayManager.DisplayListener> mDisplayListenerCaptor; + @Mock + private SensorManager mSensorManager; + @Mock + private Context mContext; + @Mock + private InputSensorInfo mInputSensorInfo; + private Sensor mHallSensor; + private Sensor mHingeAngleSensor; + @Mock + private DisplayManager mDisplayManager; + private FoldableDeviceStateProvider mProvider; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + mHallSensor = new Sensor(mInputSensorInfo); + mHingeAngleSensor = new Sensor(mInputSensorInfo); + } + + @Test + public void create_emptyConfiguration_throwsException() { + assertThrows(IllegalArgumentException.class, this::createProvider); + } + + @Test + public void create_duplicatedDeviceStateIdentifiers_throwsException() { + assertThrows(IllegalArgumentException.class, + () -> createProvider( + createConfig( + /* identifier= */ 0, /* name= */ "ONE", (c) -> true), + createConfig( + /* identifier= */ 0, /* name= */ "TWO", (c) -> true) + )); + } + + @Test + public void create_allMatchingStatesDefaultsToTheFirstIdentifier() { + createProvider( + createConfig( + /* identifier= */ 1, /* name= */ "ONE", (c) -> true), + createConfig( + /* identifier= */ 2, /* name= */ "TWO", (c) -> true), + createConfig( + /* identifier= */ 3, /* name= */ "THREE", (c) -> true) + ); + + Listener listener = mock(Listener.class); + mProvider.setListener(listener); + + verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(), + eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED)); + final DeviceState[] expectedStates = new DeviceState[]{ + new DeviceState(1, "ONE", /* flags= */ 0), + new DeviceState(2, "TWO", /* flags= */ 0), + new DeviceState(3, "THREE", /* flags= */ 0), + }; + assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue()); + + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(1, mIntegerCaptor.getValue().intValue()); + } + + @Test + public void create_multipleMatchingStatesDefaultsToTheLowestIdentifier() { + createProvider( + createConfig( + /* identifier= */ 1, /* name= */ "ONE", (c) -> false), + createConfig( + /* identifier= */ 3, /* name= */ "THREE", (c) -> false), + createConfig( + /* identifier= */ 4, /* name= */ "FOUR", (c) -> true), + createConfig( + /* identifier= */ 2, /* name= */ "TWO", (c) -> true) + ); + + Listener listener = mock(Listener.class); + mProvider.setListener(listener); + + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(2, mIntegerCaptor.getValue().intValue()); + } + + @Test + public void test_hingeAngleUpdatedFirstTime_switchesToMatchingState() throws Exception { + createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE", + (c) -> c.getHingeAngle() < 90f), + createConfig(/* identifier= */ 2, /* name= */ "TWO", + (c) -> c.getHingeAngle() >= 90f)); + Listener listener = mock(Listener.class); + mProvider.setListener(listener); + verify(listener, never()).onStateChanged(anyInt()); + clearInvocations(listener); + + sendSensorEvent(mHingeAngleSensor, /* value= */ 100f); + + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(2, mIntegerCaptor.getValue().intValue()); + } + + @Test + public void test_hallSensorUpdatedFirstTime_switchesToMatchingState() throws Exception { + createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE", + (c) -> !c.isHallSensorClosed()), + createConfig(/* identifier= */ 2, /* name= */ "TWO", + FoldableDeviceStateProvider::isHallSensorClosed)); + Listener listener = mock(Listener.class); + mProvider.setListener(listener); + verify(listener, never()).onStateChanged(anyInt()); + clearInvocations(listener); + + // Hall sensor value '1f' is for the closed state + sendSensorEvent(mHallSensor, /* value= */ 1f); + + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(2, mIntegerCaptor.getValue().intValue()); + } + + @Test + public void test_hingeAngleUpdatedSecondTime_switchesToMatchingState() throws Exception { + createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE", + (c) -> c.getHingeAngle() < 90f), + createConfig(/* identifier= */ 2, /* name= */ "TWO", + (c) -> c.getHingeAngle() >= 90f)); + sendSensorEvent(mHingeAngleSensor, /* value= */ 30f); + Listener listener = mock(Listener.class); + mProvider.setListener(listener); + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(1, mIntegerCaptor.getValue().intValue()); + clearInvocations(listener); + + sendSensorEvent(mHingeAngleSensor, /* value= */ 100f); + + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(2, mIntegerCaptor.getValue().intValue()); + } + + @Test + public void test_hallSensorUpdatedSecondTime_switchesToMatchingState() throws Exception { + createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE", + (c) -> !c.isHallSensorClosed()), + createConfig(/* identifier= */ 2, /* name= */ "TWO", + FoldableDeviceStateProvider::isHallSensorClosed)); + sendSensorEvent(mHallSensor, /* value= */ 0f); + Listener listener = mock(Listener.class); + mProvider.setListener(listener); + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(1, mIntegerCaptor.getValue().intValue()); + clearInvocations(listener); + + // Hall sensor value '1f' is for the closed state + sendSensorEvent(mHallSensor, /* value= */ 1f); + + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(2, mIntegerCaptor.getValue().intValue()); + } + + @Test + public void test_invalidSensorValues_onStateChangedIsNotTriggered() throws Exception { + createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE", + (c) -> c.getHingeAngle() < 90f), + createConfig(/* identifier= */ 2, /* name= */ "TWO", + (c) -> c.getHingeAngle() >= 90f)); + Listener listener = mock(Listener.class); + mProvider.setListener(listener); + clearInvocations(listener); + + // First, switch to a non-default state. + sendSensorEvent(mHingeAngleSensor, /* value= */ 100f); + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(2, mIntegerCaptor.getValue().intValue()); + + clearInvocations(listener); + + // Then, send an invalid sensor event, verify that onStateChanged() is not triggered. + sendInvalidSensorEvent(mHingeAngleSensor); + + verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(), + eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED)); + verify(listener, never()).onStateChanged(mIntegerCaptor.capture()); + } + + @Test + public void test_nullSensorValues_noExceptionThrown() throws Exception { + createProvider(createConfig( /* identifier= */ 1, /* name= */ "ONE", + (c) -> c.getHingeAngle() < 90f)); + sendInvalidSensorEvent(null); + } + + @Test + public void test_flagDisableWhenThermalStatusCritical() throws Exception { + createProvider(createConfig(/* identifier= */ 1, /* name= */ "CLOSED", + (c) -> c.getHingeAngle() < 5f), + createConfig(/* identifier= */ 2, /* name= */ "HALF_OPENED", + (c) -> c.getHingeAngle() < 90f), + createConfig(/* identifier= */ 3, /* name= */ "OPENED", + (c) -> c.getHingeAngle() < 180f), + createConfig(/* identifier= */ 4, /* name= */ "THERMAL_TEST", + DeviceState.FLAG_EMULATED_ONLY + | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL + | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE, + (c) -> true)); + Listener listener = mock(Listener.class); + mProvider.setListener(listener); + verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(), + eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED)); + assertArrayEquals( + new DeviceState[]{ + new DeviceState(1, "CLOSED", 0 /* flags */), + new DeviceState(2, "HALF_OPENED", 0 /* flags */), + new DeviceState(3, "OPENED", 0 /* flags */), + new DeviceState(4, "THERMAL_TEST", + DeviceState.FLAG_EMULATED_ONLY + | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL + | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)}, + mDeviceStateArrayCaptor.getValue()); + clearInvocations(listener); + + mProvider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_MODERATE); + verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(), + eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED)); + clearInvocations(listener); + + // The THERMAL_TEST state should be disabled. + mProvider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_CRITICAL); + verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(), + eq(SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL)); + assertArrayEquals( + new DeviceState[]{ + new DeviceState(1, "CLOSED", 0 /* flags */), + new DeviceState(2, "HALF_OPENED", 0 /* flags */), + new DeviceState(3, "OPENED", 0 /* flags */)}, + mDeviceStateArrayCaptor.getValue()); + clearInvocations(listener); + + // The THERMAL_TEST state should be re-enabled. + mProvider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_LIGHT); + verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(), + eq(SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL)); + assertArrayEquals( + new DeviceState[]{ + new DeviceState(1, "CLOSED", 0 /* flags */), + new DeviceState(2, "HALF_OPENED", 0 /* flags */), + new DeviceState(3, "OPENED", 0 /* flags */), + new DeviceState(4, "THERMAL_TEST", + DeviceState.FLAG_EMULATED_ONLY + | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL + | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)}, + mDeviceStateArrayCaptor.getValue()); + } + + @Test + public void test_flagDisableWhenPowerSaveEnabled() throws Exception { + createProvider(createConfig(/* identifier= */ 1, /* name= */ "CLOSED", + (c) -> c.getHingeAngle() < 5f), + createConfig(/* identifier= */ 2, /* name= */ "HALF_OPENED", + (c) -> c.getHingeAngle() < 90f), + createConfig(/* identifier= */ 3, /* name= */ "OPENED", + (c) -> c.getHingeAngle() < 180f), + createConfig(/* identifier= */ 4, /* name= */ "THERMAL_TEST", + DeviceState.FLAG_EMULATED_ONLY + | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL + | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE, + (c) -> true)); + mProvider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */); + Listener listener = mock(Listener.class); + mProvider.setListener(listener); + + verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(), + eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED)); + assertArrayEquals( + new DeviceState[]{ + new DeviceState(1, "CLOSED", 0 /* flags */), + new DeviceState(2, "HALF_OPENED", 0 /* flags */), + new DeviceState(3, "OPENED", 0 /* flags */), + new DeviceState(4, "THERMAL_TEST", + DeviceState.FLAG_EMULATED_ONLY + | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL + | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) }, + mDeviceStateArrayCaptor.getValue()); + clearInvocations(listener); + + mProvider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */); + verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(), + eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED)); + clearInvocations(listener); + + // The THERMAL_TEST state should be disabled due to power save being enabled. + mProvider.onPowerSaveModeChanged(true /* isPowerSaveModeEnabled */); + verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(), + eq(SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED)); + assertArrayEquals( + new DeviceState[]{ + new DeviceState(1, "CLOSED", 0 /* flags */), + new DeviceState(2, "HALF_OPENED", 0 /* flags */), + new DeviceState(3, "OPENED", 0 /* flags */) }, + mDeviceStateArrayCaptor.getValue()); + clearInvocations(listener); + + // The THERMAL_TEST state should be re-enabled. + mProvider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */); + verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(), + eq(SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED)); + assertArrayEquals( + new DeviceState[]{ + new DeviceState(1, "CLOSED", 0 /* flags */), + new DeviceState(2, "HALF_OPENED", 0 /* flags */), + new DeviceState(3, "OPENED", 0 /* flags */), + new DeviceState(4, "THERMAL_TEST", + DeviceState.FLAG_EMULATED_ONLY + | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL + | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) }, + mDeviceStateArrayCaptor.getValue()); + } + + @Test + public void test_previousStateBasedPredicate() { + // Create a configuration where state TWO could be matched only if + // the previous state was 'THREE' + createProvider( + createConfig( + /* identifier= */ 1, /* name= */ "ONE", (c) -> c.getHingeAngle() < 30f), + createConfig( + /* identifier= */ 2, /* name= */ "TWO", + (c) -> c.getLastReportedDeviceState() == 3 && c.getHingeAngle() > 120f), + createConfig( + /* identifier= */ 3, /* name= */ "THREE", + (c) -> c.getHingeAngle() > 90f) + ); + sendSensorEvent(mHingeAngleSensor, /* value= */ 0f); + Listener listener = mock(Listener.class); + mProvider.setListener(listener); + + // Check that the initial state is 'ONE' + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(1, mIntegerCaptor.getValue().intValue()); + clearInvocations(listener); + + // Should not match state 'TWO', it should match only state 'THREE' + // (because the previous state is not 'THREE', it is 'ONE') + sendSensorEvent(mHingeAngleSensor, /* value= */ 180f); + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(3, mIntegerCaptor.getValue().intValue()); + clearInvocations(listener); + + // Now it should match state 'TWO' + // (because the previous state is 'THREE' now) + sendSensorEvent(mHingeAngleSensor, /* value= */ 180f); + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(2, mIntegerCaptor.getValue().intValue()); + } + + @Test + public void isScreenOn_afterDisplayChangedToOn_returnsTrue() { + createProvider( + createConfig( + /* identifier= */ 1, /* name= */ "ONE", (c) -> true) + ); + + setScreenOn(true); + + assertThat(mProvider.isScreenOn()).isTrue(); + } + + @Test + public void isScreenOn_afterDisplayChangedToOff_returnsFalse() { + createProvider( + createConfig( + /* identifier= */ 1, /* name= */ "ONE", (c) -> true) + ); + + setScreenOn(false); + + assertThat(mProvider.isScreenOn()).isFalse(); + } + + @Test + public void isScreenOn_afterDisplayChangedToOnThenOff_returnsFalse() { + createProvider( + createConfig( + /* identifier= */ 1, /* name= */ "ONE", (c) -> true) + ); + + setScreenOn(true); + setScreenOn(false); + + assertThat(mProvider.isScreenOn()).isFalse(); + } + + private void setScreenOn(boolean isOn) { + Display mockDisplay = mock(Display.class); + int state = isOn ? STATE_ON : STATE_OFF; + when(mockDisplay.getState()).thenReturn(state); + when(mDisplayManager.getDisplay(eq(DEFAULT_DISPLAY))).thenReturn(mockDisplay); + mDisplayListenerCaptor.getValue().onDisplayChanged(DEFAULT_DISPLAY); + } + + private void sendSensorEvent(Sensor sensor, float value) { + SensorEvent event = mock(SensorEvent.class); + event.sensor = sensor; + try { + FieldSetter.setField(event, event.getClass().getField("values"), + new float[]{value}); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + + mProvider.onSensorChanged(event); + } + + private void sendInvalidSensorEvent(Sensor sensor) { + SensorEvent event = mock(SensorEvent.class); + event.sensor = sensor; + try { + // Set empty values array to make the event invalid + FieldSetter.setField(event, event.getClass().getField("values"), + new float[]{}); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + mProvider.onSensorChanged(event); + } + + private void createProvider(DeviceStateConfiguration... configurations) { + mProvider = new FoldableDeviceStateProvider(mContext, mSensorManager, mHingeAngleSensor, + mHallSensor, mDisplayManager, configurations); + verify(mDisplayManager) + .registerDisplayListener( + mDisplayListenerCaptor.capture(), + nullable(Handler.class), + anyLong()); + } +} diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING index 4af880ded953..4c9403c9b21a 100644 --- a/services/incremental/TEST_MAPPING +++ b/services/incremental/TEST_MAPPING @@ -12,7 +12,7 @@ "name": "CtsPackageManagerIncrementalStatsHostTestCases", "options": [ { - "exclude-annotation": "android.platform.test.annotations.LargeTest" + "exclude-annotation": "androidx.test.filters.LargeTest" } ] }, @@ -55,7 +55,7 @@ "name": "CtsPackageManagerIncrementalStatsHostTestCases", "options": [ { - "include-annotation": "android.platform.test.annotations.LargeTest" + "include-annotation": "androidx.test.filters.LargeTest" } ] } diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java index 4a2bf75b4d2c..5d3eba86a725 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java @@ -17,24 +17,22 @@ package com.android.server.pm; import static com.android.compatibility.common.util.ShellUtils.runShellCommand; + import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; + import static java.lang.reflect.Modifier.isFinal; import static java.lang.reflect.Modifier.isPublic; import static java.lang.reflect.Modifier.isStatic; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.AppGlobals; -import android.content.IIntentReceiver; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Postsubmit; -import android.util.SparseArray; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; @@ -87,18 +85,6 @@ public class PackageManagerServiceTest { @Test public void testPackageRemoval() { class PackageSenderImpl implements PackageSender { - public void sendPackageBroadcast(final String action, final String pkg, - final Bundle extras, final int flags, final String targetPkg, - final IIntentReceiver finishedReceiver, final int[] userIds, - int[] instantUserIds, SparseArray<int[]> broadcastAllowList, - @Nullable Bundle bOptions) { - } - - public void sendPackageAddedForNewUsers(@NonNull Computer snapshot, String packageName, - boolean sendBootComplete, boolean includeStopped, int appId, - int[] userIds, int[] instantUserIds, boolean isArchived, int dataLoaderType) { - } - @Override public void notifyPackageAdded(String packageName, int uid) { } @@ -113,9 +99,8 @@ public class PackageManagerServiceTest { } } - PackageSenderImpl sender = new PackageSenderImpl(); PackageSetting setting = null; - PackageRemovedInfo pri = new PackageRemovedInfo(sender); + PackageRemovedInfo pri = new PackageRemovedInfo(); // Initial conditions: nothing there Assert.assertNull(pri.mRemovedUsers); diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionDefinitionsTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionDefinitionsTest.kt new file mode 100644 index 000000000000..832136cec792 --- /dev/null +++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionDefinitionsTest.kt @@ -0,0 +1,764 @@ +/* + * 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.permission.test + +import android.content.pm.PermissionInfo +import android.os.Build +import com.android.server.permission.access.MutateStateScope +import com.android.server.permission.access.permission.Permission +import com.android.server.permission.access.permission.PermissionFlags +import com.android.server.pm.pkg.PackageState +import com.google.common.truth.Truth.assertWithMessage +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +/** + * Parameterized test for testing permission definitions (adopt permissions, add permission groups, + * add permissions, trim permissions, trim permission states and revoke permissions on + * package update) for onStorageVolumeAdded() and onPackageAdded() in AppIdPermissionPolicy. + * + * Note that the evaluatePermissionState() call with permission changes + * (i.e. changedPermissionNames in AppIdPermissionPolicy) and the evaluatePermissionState() call + * with an installedPackageState is put in this test instead of + * AppIdPermissionPolicyPermissionStatesTest because these concepts don't apply to onUserAdded(). + */ +@RunWith(Parameterized::class) +class AppIdPermissionPolicyPermissionDefinitionsTest : BaseAppIdPermissionPolicyTest() { + @Parameterized.Parameter(0) lateinit var action: Action + + @Test + fun testAdoptPermissions_permissionsOfMissingSystemApp_getsAdopted() { + testAdoptPermissions(hasMissingPackage = true, isSystem = true) + + assertWithMessage( + "After $action is called for a null adopt permission package," + + " the permission package name: ${getPermission(PERMISSION_NAME_0)?.packageName}" + + " did not match the expected package name: $PACKAGE_NAME_0" + ) + .that(getPermission(PERMISSION_NAME_0)?.packageName) + .isEqualTo(PACKAGE_NAME_0) + } + + @Test + fun testAdoptPermissions_permissionsOfExistingSystemApp_notAdopted() { + testAdoptPermissions(isSystem = true) + + assertWithMessage( + "After $action is called for a non-null adopt permission" + + " package, the permission package name:" + + " ${getPermission(PERMISSION_NAME_0)?.packageName} should not match the" + + " package name: $PACKAGE_NAME_0" + ) + .that(getPermission(PERMISSION_NAME_0)?.packageName) + .isNotEqualTo(PACKAGE_NAME_0) + } + + @Test + fun testAdoptPermissions_permissionsOfNonSystemApp_notAdopted() { + testAdoptPermissions(hasMissingPackage = true) + + assertWithMessage( + "After $action is called for a non-system adopt permission" + + " package, the permission package name:" + + " ${getPermission(PERMISSION_NAME_0)?.packageName} should not match the" + + " package name: $PACKAGE_NAME_0" + ) + .that(getPermission(PERMISSION_NAME_0)?.packageName) + .isNotEqualTo(PACKAGE_NAME_0) + } + + private fun testAdoptPermissions( + hasMissingPackage: Boolean = false, + isSystem: Boolean = false + ) { + val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_1) + val packageToAdoptPermission = if (hasMissingPackage) { + mockPackageState(APP_ID_1, PACKAGE_NAME_1, isSystem = isSystem) + } else { + mockPackageState( + APP_ID_1, + mockAndroidPackage( + PACKAGE_NAME_1, + permissions = listOf(parsedPermission) + ), + isSystem = isSystem + ) + } + addPackageState(packageToAdoptPermission) + addPermission(parsedPermission) + + mutateState { + val installedPackage = mockPackageState( + APP_ID_0, + mockAndroidPackage( + PACKAGE_NAME_0, + permissions = listOf(defaultPermission), + adoptPermissions = listOf(PACKAGE_NAME_1) + ) + ) + addPackageState(installedPackage, newState) + testAction(installedPackage) + } + } + + @Test + fun testPermissionGroupDefinition_newPermissionGroup_getsDeclared() { + mutateState { + val packageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) + addPackageState(packageState, newState) + testAction(packageState) + } + + assertWithMessage( + "After $action is called when there is no existing" + + " permission groups, the new permission group $PERMISSION_GROUP_NAME_0 is not added" + ) + .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.name) + .isEqualTo(PERMISSION_GROUP_NAME_0) + } + + @Test + fun testPermissionGroupDefinition_systemAppTakingOverPermissionGroupDefinition_getsTakenOver() { + testTakingOverPermissionAndPermissionGroupDefinitions(newPermissionOwnerIsSystem = true) + + assertWithMessage( + "After $action is called when $PERMISSION_GROUP_NAME_0 already" + + " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover the" + + " ownership of this permission group" + ) + .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName) + .isEqualTo(PACKAGE_NAME_0) + } + + @Test + fun testPermissionGroupDefinition_instantApps_remainsUnchanged() { + testTakingOverPermissionAndPermissionGroupDefinitions( + newPermissionOwnerIsInstant = true, + permissionGroupAlreadyExists = false + ) + + assertWithMessage( + "After $action is called for an instant app," + + " the new permission group $PERMISSION_GROUP_NAME_0 should not be added" + ) + .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)) + .isNull() + } + + @Test + fun testPermissionGroupDefinition_nonSystemAppTakingOverGroupDefinition_remainsUnchanged() { + testTakingOverPermissionAndPermissionGroupDefinitions() + + assertWithMessage( + "After $action is called when $PERMISSION_GROUP_NAME_0 already" + + " exists in the system, non-system app $PACKAGE_NAME_0 shouldn't takeover" + + " ownership of this permission group" + ) + .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName) + .isEqualTo(PACKAGE_NAME_1) + } + + @Test + fun testPermissionGroupDefinition_takingOverGroupDeclaredBySystemApp_remainsUnchanged() { + testTakingOverPermissionAndPermissionGroupDefinitions(oldPermissionOwnerIsSystem = true) + + assertWithMessage( + "After $action is called when $PERMISSION_GROUP_NAME_0 already" + + " exists in the system and is owned by a system app, app $PACKAGE_NAME_0" + + " shouldn't takeover ownership of this permission group" + ) + .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName) + .isEqualTo(PACKAGE_NAME_1) + } + + @Test + fun testPermissionDefinition_newPermission_getsDeclared() { + mutateState { + val packageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) + addPackageState(packageState, newState) + testAction(packageState) + } + + assertWithMessage( + "After $action is called when there is no existing" + + " permissions, the new permission $PERMISSION_NAME_0 is not added" + ) + .that(getPermission(PERMISSION_NAME_0)?.name) + .isEqualTo(PERMISSION_NAME_0) + } + + @Test + fun testPermissionDefinition_configPermission_getsTakenOver() { + testTakingOverPermissionAndPermissionGroupDefinitions( + oldPermissionOwnerIsSystem = true, + newPermissionOwnerIsSystem = true, + type = Permission.TYPE_CONFIG, + isReconciled = false + ) + + assertWithMessage( + "After $action is called for a config permission with" + + " no owner, the ownership is not taken over by a system app $PACKAGE_NAME_0" + ) + .that(getPermission(PERMISSION_NAME_0)?.packageName) + .isEqualTo(PACKAGE_NAME_0) + } + + @Test + fun testPermissionDefinition_systemAppTakingOverPermissionDefinition_getsTakenOver() { + testTakingOverPermissionAndPermissionGroupDefinitions(newPermissionOwnerIsSystem = true) + + assertWithMessage( + "After $action is called when $PERMISSION_NAME_0 already" + + " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover ownership" + + " of this permission" + ) + .that(getPermission(PERMISSION_NAME_0)?.packageName) + .isEqualTo(PACKAGE_NAME_0) + } + + @Test + fun testPermissionDefinition_nonSystemAppTakingOverPermissionDefinition_remainsUnchanged() { + testTakingOverPermissionAndPermissionGroupDefinitions() + + assertWithMessage( + "After $action is called when $PERMISSION_NAME_0 already" + + " exists in the system, the non-system app $PACKAGE_NAME_0 shouldn't takeover" + + " ownership of this permission" + ) + .that(getPermission(PERMISSION_NAME_0)?.packageName) + .isEqualTo(PACKAGE_NAME_1) + } + + @Test + fun testPermissionDefinition_takingOverPermissionDeclaredBySystemApp_remainsUnchanged() { + testTakingOverPermissionAndPermissionGroupDefinitions(oldPermissionOwnerIsSystem = true) + + assertWithMessage( + "After $action is called when $PERMISSION_NAME_0 already" + + " exists in system and is owned by a system app, the $PACKAGE_NAME_0 shouldn't" + + " takeover ownership of this permission" + ) + .that(getPermission(PERMISSION_NAME_0)?.packageName) + .isEqualTo(PACKAGE_NAME_1) + } + + private fun testTakingOverPermissionAndPermissionGroupDefinitions( + oldPermissionOwnerIsSystem: Boolean = false, + newPermissionOwnerIsSystem: Boolean = false, + newPermissionOwnerIsInstant: Boolean = false, + permissionGroupAlreadyExists: Boolean = true, + permissionAlreadyExists: Boolean = true, + type: Int = Permission.TYPE_MANIFEST, + isReconciled: Boolean = true, + ) { + val oldPermissionOwnerPackageState = mockPackageState( + APP_ID_1, + PACKAGE_NAME_1, + isSystem = oldPermissionOwnerIsSystem + ) + addPackageState(oldPermissionOwnerPackageState) + if (permissionGroupAlreadyExists) { + addPermissionGroup(mockParsedPermissionGroup(PERMISSION_GROUP_NAME_0, PACKAGE_NAME_1)) + } + if (permissionAlreadyExists) { + addPermission( + mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_1), + type = type, + isReconciled = isReconciled + ) + } + + mutateState { + val newPermissionOwnerPackageState = mockPackageState( + APP_ID_0, + mockSimpleAndroidPackage(), + isSystem = newPermissionOwnerIsSystem, + isInstantApp = newPermissionOwnerIsInstant + ) + addPackageState(newPermissionOwnerPackageState, newState) + testAction(newPermissionOwnerPackageState) + } + } + + @Test + fun testPermissionChanged_permissionGroupChanged_getsRevoked() { + testPermissionChanged( + oldPermissionGroup = PERMISSION_GROUP_NAME_1, + newPermissionGroup = PERMISSION_GROUP_NAME_0 + ) + + val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0) + val expectedNewFlags = 0 + assertWithMessage( + "After $action is called for a package that has a permission group change" + + " for a permission it defines, the actual permission flags $actualFlags" + + " should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testPermissionChanged_protectionLevelChanged_getsRevoked() { + testPermissionChanged(newProtectionLevel = PermissionInfo.PROTECTION_INTERNAL) + + val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0) + val expectedNewFlags = 0 + assertWithMessage( + "After $action is called for a package that has a protection level change" + + " for a permission it defines, the actual permission flags $actualFlags" + + " should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + private fun testPermissionChanged( + oldPermissionGroup: String? = null, + newPermissionGroup: String? = null, + newProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS + ) { + val oldPermission = mockParsedPermission( + PERMISSION_NAME_0, + PACKAGE_NAME_0, + group = oldPermissionGroup, + protectionLevel = PermissionInfo.PROTECTION_DANGEROUS + ) + val oldPackageState = mockPackageState( + APP_ID_0, + mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(oldPermission)) + ) + addPackageState(oldPackageState) + addPermission(oldPermission) + setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED) + + mutateState { + val newPermission = mockParsedPermission( + PERMISSION_NAME_0, + PACKAGE_NAME_0, + group = newPermissionGroup, + protectionLevel = newProtectionLevel + ) + val newPackageState = mockPackageState( + APP_ID_0, + mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(newPermission)) + ) + addPackageState(newPackageState, newState) + testAction(newPackageState) + } + } + + @Test + fun testPermissionDeclaration_permissionTreeNoLongerDeclared_getsDefinitionRemoved() { + testPermissionDeclaration {} + + assertWithMessage( + "After $action is called for a package that no longer defines a permission" + + " tree, the permission tree: $PERMISSION_NAME_0 in system state should be removed" + ) + .that(getPermissionTree(PERMISSION_NAME_0)) + .isNull() + } + + @Test + fun testPermissionDeclaration_permissionTreeByDisabledSystemPackage_remainsUnchanged() { + testPermissionDeclaration { + val disabledSystemPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) + addDisabledSystemPackageState(disabledSystemPackageState) + } + + assertWithMessage( + "After $action is called for a package that no longer defines" + + " a permission tree while this permission tree is still defined by" + + " a disabled system package, the permission tree: $PERMISSION_NAME_0 in" + + " system state should not be removed" + ) + .that(getPermissionTree(PERMISSION_TREE_NAME)) + .isNotNull() + } + + @Test + fun testPermissionDeclaration_permissionNoLongerDeclared_getsDefinitionRemoved() { + testPermissionDeclaration {} + + assertWithMessage( + "After $action is called for a package that no longer defines a permission," + + " the permission: $PERMISSION_NAME_0 in system state should be removed" + ) + .that(getPermission(PERMISSION_NAME_0)) + .isNull() + } + + @Test + fun testPermissionDeclaration_permissionByDisabledSystemPackage_remainsUnchanged() { + testPermissionDeclaration { + val disabledSystemPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) + addDisabledSystemPackageState(disabledSystemPackageState) + } + + assertWithMessage( + "After $action is called for a disabled system package and it's updated apk" + + " no longer defines a permission, the permission: $PERMISSION_NAME_0 in" + + " system state should not be removed" + ) + .that(getPermission(PERMISSION_NAME_0)) + .isNotNull() + } + + private fun testPermissionDeclaration(additionalSetup: () -> Unit) { + val oldPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) + addPackageState(oldPackageState) + addPermission(defaultPermissionTree) + addPermission(defaultPermission) + + additionalSetup() + + mutateState { + val newPackageState = mockPackageState(APP_ID_0, mockAndroidPackage(PACKAGE_NAME_0)) + addPackageState(newPackageState, newState) + testAction(newPackageState) + } + } + + @Test + fun testTrimPermissionStates_permissionsNoLongerRequested_getsFlagsRevoked() { + val parsedPermission = mockParsedPermission( + PERMISSION_NAME_0, + PACKAGE_NAME_0, + protectionLevel = PermissionInfo.PROTECTION_DANGEROUS + ) + val oldPackageState = mockPackageState( + APP_ID_0, + mockAndroidPackage( + PACKAGE_NAME_0, + permissions = listOf(parsedPermission), + requestedPermissions = setOf(PERMISSION_NAME_0) + ) + ) + addPackageState(oldPackageState) + addPermission(parsedPermission) + setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED) + + mutateState { + val newPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) + addPackageState(newPackageState, newState) + testAction(newPackageState) + } + + val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0) + val expectedNewFlags = 0 + assertWithMessage( + "After $action is called for a package that no longer requests a permission" + + " the actual permission flags $actualFlags should match the" + + " expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testRevokePermissionsOnPackageUpdate_storageAndMediaDowngradingPastQ_getsRuntimeRevoked() { + testRevokePermissionsOnPackageUpdate( + PermissionFlags.RUNTIME_GRANTED, + newTargetSdkVersion = Build.VERSION_CODES.P + ) + + val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE) + val expectedNewFlags = 0 + assertWithMessage( + "After $action is called for a package that's downgrading past Q" + + " the actual permission flags $actualFlags should match the" + + " expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testRevokePermissionsOnPackageUpdate_storageAndMediaNotDowngradingPastQ_remainsUnchanged() { + val oldFlags = PermissionFlags.RUNTIME_GRANTED + testRevokePermissionsOnPackageUpdate( + oldFlags, + oldTargetSdkVersion = Build.VERSION_CODES.P, + newTargetSdkVersion = Build.VERSION_CODES.P + ) + + val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE) + val expectedNewFlags = oldFlags + assertWithMessage( + "After $action is called for a package that's not downgrading past Q" + + " the actual permission flags $actualFlags should match the" + + " expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testRevokePermissionsOnPackageUpdate_policyFixedDowngradingPastQ_remainsUnchanged() { + val oldFlags = PermissionFlags.RUNTIME_GRANTED and PermissionFlags.POLICY_FIXED + testRevokePermissionsOnPackageUpdate(oldFlags, newTargetSdkVersion = Build.VERSION_CODES.P) + + val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE) + val expectedNewFlags = oldFlags + assertWithMessage( + "After $action is called for a package that's downgrading past Q" + + " the actual permission flags with PermissionFlags.POLICY_FIXED $actualFlags" + + " should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testRevokePermissionsOnPackageUpdate_newlyRequestingLegacyExternalStorage_runtimeRevoked() { + testRevokePermissionsOnPackageUpdate( + PermissionFlags.RUNTIME_GRANTED, + oldTargetSdkVersion = Build.VERSION_CODES.P, + newTargetSdkVersion = Build.VERSION_CODES.P, + oldIsRequestLegacyExternalStorage = false + ) + + val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE) + val expectedNewFlags = 0 + assertWithMessage( + "After $action is called for a package with" + + " newlyRequestingLegacyExternalStorage, the actual permission flags $actualFlags" + + " should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testRevokePermissionsOnPackageUpdate_missingOldPackage_remainsUnchanged() { + val oldFlags = PermissionFlags.RUNTIME_GRANTED + testRevokePermissionsOnPackageUpdate( + oldFlags, + newTargetSdkVersion = Build.VERSION_CODES.P, + isOldPackageMissing = true + ) + + val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE) + val expectedNewFlags = oldFlags + assertWithMessage( + "After $action is called for a package that's downgrading past Q" + + " and doesn't have the oldPackage, the actual permission flags $actualFlags" + + " should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + private fun testRevokePermissionsOnPackageUpdate( + oldFlags: Int, + oldTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, + newTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, + oldIsRequestLegacyExternalStorage: Boolean = true, + newIsRequestLegacyExternalStorage: Boolean = true, + isOldPackageMissing: Boolean = false + ) { + val parsedPermission = mockParsedPermission( + PERMISSION_READ_EXTERNAL_STORAGE, + PACKAGE_NAME_0, + protectionLevel = PermissionInfo.PROTECTION_DANGEROUS + ) + val oldPackageState = if (isOldPackageMissing) { + mockPackageState(APP_ID_0, PACKAGE_NAME_0) + } else { + mockPackageState( + APP_ID_0, + mockAndroidPackage( + PACKAGE_NAME_0, + targetSdkVersion = oldTargetSdkVersion, + isRequestLegacyExternalStorage = oldIsRequestLegacyExternalStorage, + requestedPermissions = setOf(PERMISSION_READ_EXTERNAL_STORAGE), + permissions = listOf(parsedPermission) + ) + ) + } + addPackageState(oldPackageState) + addPermission(parsedPermission) + setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE, oldFlags) + + mutateState { + val newPackageState = mockPackageState( + APP_ID_0, + mockAndroidPackage( + PACKAGE_NAME_0, + targetSdkVersion = newTargetSdkVersion, + isRequestLegacyExternalStorage = newIsRequestLegacyExternalStorage, + requestedPermissions = setOf(PERMISSION_READ_EXTERNAL_STORAGE), + permissions = listOf(parsedPermission) + ) + ) + addPackageState(newPackageState, newState) + testAction(newPackageState) + } + } + + @Test + fun testEvaluatePermissionState_normalPermissionRequestedByInstalledPackage_getsGranted() { + val oldFlags = PermissionFlags.INSTALL_REVOKED + val permissionOwnerPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) + val installedPackageState = mockPackageState( + APP_ID_1, + mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0)) + ) + addPackageState(permissionOwnerPackageState) + addPackageState(installedPackageState) + addPermission(defaultPermission) + setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags) + + mutateState { + testAction(installedPackageState) + } + + val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.INSTALL_GRANTED + assertWithMessage( + "After $action is called for a package that requests a normal permission" + + " with the INSTALL_REVOKED flag, the actual permission flags $actualFlags" + + " should match the expected flags $expectedNewFlags since it's a new install" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + /** + * We set up a permission protection level change from SIGNATURE to NORMAL in order to make + * the permission a "changed permission" in order to test evaluatePermissionState() called by + * evaluatePermissionStateForAllPackages(). This makes the requestingPackageState not the + * installedPackageState so that we can test whether requesting by system package will give us + * the expected permission flags. + * + * Besides, this also helps us test evaluatePermissionStateForAllPackages(). Since both + * evaluatePermissionStateForAllPackages() and evaluateAllPermissionStatesForPackage() call + * evaluatePermissionState() in their implementations, we use these tests as the only tests + * that test evaluatePermissionStateForAllPackages() + */ + @Test + fun testEvaluatePermissionState_normalPermissionRequestedBySystemPackage_getsGranted() { + testEvaluateNormalPermissionStateWithPermissionChanges(requestingPackageIsSystem = true) + + val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.INSTALL_GRANTED + assertWithMessage( + "After $action is called for a system package that requests a normal" + + " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" + + " should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_normalCompatibilityPermission_getsGranted() { + testEvaluateNormalPermissionStateWithPermissionChanges( + permissionName = PERMISSION_POST_NOTIFICATIONS, + requestingPackageTargetSdkVersion = Build.VERSION_CODES.S + ) + + val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_POST_NOTIFICATIONS) + val expectedNewFlags = PermissionFlags.INSTALL_GRANTED + assertWithMessage( + "After $action is called for a package that requests a normal compatibility" + + " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" + + " should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_normalPermissionPreviouslyRevoked_getsInstallRevoked() { + testEvaluateNormalPermissionStateWithPermissionChanges() + + val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.INSTALL_REVOKED + assertWithMessage( + "After $action is called for a package that requests a normal" + + " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" + + " should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + private fun testEvaluateNormalPermissionStateWithPermissionChanges( + permissionName: String = PERMISSION_NAME_0, + requestingPackageTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, + requestingPackageIsSystem: Boolean = false + ) { + val oldParsedPermission = mockParsedPermission( + permissionName, + PACKAGE_NAME_0, + protectionLevel = PermissionInfo.PROTECTION_SIGNATURE + ) + val oldPermissionOwnerPackageState = mockPackageState( + APP_ID_0, + mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(oldParsedPermission)) + ) + val requestingPackageState = mockPackageState( + APP_ID_1, + mockAndroidPackage( + PACKAGE_NAME_1, + requestedPermissions = setOf(permissionName), + targetSdkVersion = requestingPackageTargetSdkVersion + ), + isSystem = requestingPackageIsSystem, + ) + addPackageState(oldPermissionOwnerPackageState) + addPackageState(requestingPackageState) + addPermission(oldParsedPermission) + val oldFlags = PermissionFlags.INSTALL_REVOKED + setPermissionFlags(APP_ID_1, USER_ID_0, permissionName, oldFlags) + + mutateState { + val newParsedPermission = mockParsedPermission(permissionName, PACKAGE_NAME_0) + val newPermissionOwnerPackageState = mockPackageState( + APP_ID_0, + mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(newParsedPermission)) + ) + addPackageState(newPermissionOwnerPackageState, newState) + testAction(newPermissionOwnerPackageState) + } + } + + private fun MutateStateScope.testAction(packageState: PackageState) { + with(appIdPermissionPolicy) { + when (action) { + Action.ON_PACKAGE_ADDED -> onPackageAdded(packageState) + Action.ON_STORAGE_VOLUME_ADDED -> onStorageVolumeMounted( + null, + listOf(packageState.packageName), + true + ) + } + } + } + + enum class Action { ON_PACKAGE_ADDED, ON_STORAGE_VOLUME_ADDED } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun data(): Array<Action> = Action.values() + } +} diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt new file mode 100644 index 000000000000..823ce4555ca8 --- /dev/null +++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt @@ -0,0 +1,115 @@ +/* + * 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.permission.test + +import android.content.pm.PermissionInfo +import com.android.server.permission.access.MutateStateScope +import com.android.server.permission.access.permission.PermissionFlags +import com.google.common.truth.Truth.assertWithMessage +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +/** + * A parameterized test for testing resetting runtime permissions for onPackageUninstalled() + * and resetRuntimePermissions() in AppIdPermissionPolicy + */ +@RunWith(Parameterized::class) +class AppIdPermissionPolicyPermissionResetTest : BaseAppIdPermissionPolicyTest() { + @Parameterized.Parameter(0) lateinit var action: Action + + @Test + fun testResetRuntimePermissions_runtimeGranted_getsRevoked() { + val oldFlags = PermissionFlags.RUNTIME_GRANTED + val expectedNewFlags = 0 + testResetRuntimePermissions(oldFlags, expectedNewFlags) + } + + @Test + fun testResetRuntimePermissions_roleGranted_getsGranted() { + val oldFlags = PermissionFlags.ROLE + val expectedNewFlags = PermissionFlags.ROLE or PermissionFlags.RUNTIME_GRANTED + testResetRuntimePermissions(oldFlags, expectedNewFlags) + } + + @Test + fun testResetRuntimePermissions_nullAndroidPackage_remainsUnchanged() { + val oldFlags = PermissionFlags.RUNTIME_GRANTED + val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED + testResetRuntimePermissions(oldFlags, expectedNewFlags, isAndroidPackageMissing = true) + } + + private fun testResetRuntimePermissions( + oldFlags: Int, + expectedNewFlags: Int, + isAndroidPackageMissing: Boolean = false + ) { + val parsedPermission = mockParsedPermission( + PERMISSION_NAME_0, + PACKAGE_NAME_0, + protectionLevel = PermissionInfo.PROTECTION_DANGEROUS, + ) + val permissionOwnerPackageState = mockPackageState( + APP_ID_0, + mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission)) + ) + val requestingPackageState = if (isAndroidPackageMissing) { + mockPackageState(APP_ID_1, PACKAGE_NAME_1) + } else { + mockPackageState( + APP_ID_1, + mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0)) + ) + } + setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags) + addPackageState(permissionOwnerPackageState) + addPackageState(requestingPackageState) + addPermission(parsedPermission) + + mutateState { testAction() } + + val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) + assertWithMessage( + "After resetting runtime permissions, permission flags did not match" + + " expected values: expectedNewFlags is $expectedNewFlags," + + " actualFlags is $actualFlags, while the oldFlags is $oldFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + private fun MutateStateScope.testAction( + packageName: String = PACKAGE_NAME_1, + appId: Int = APP_ID_1, + userId: Int = USER_ID_0 + ) { + with(appIdPermissionPolicy) { + when (action) { + Action.ON_PACKAGE_UNINSTALLED -> onPackageUninstalled(packageName, appId, userId) + Action.RESET_RUNTIME_PERMISSIONS -> resetRuntimePermissions(packageName, userId) + } + } + } + + enum class Action { ON_PACKAGE_UNINSTALLED, RESET_RUNTIME_PERMISSIONS } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun data(): Array<Action> = Action.values() + } +}
\ No newline at end of file diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt new file mode 100644 index 000000000000..f085bd7fd7f0 --- /dev/null +++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt @@ -0,0 +1,884 @@ +/* + * 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.permission.test + +import android.content.pm.PermissionInfo +import android.os.Build +import com.android.server.permission.access.MutableAccessState +import com.android.server.permission.access.MutateStateScope +import com.android.server.permission.access.immutable.IndexedListSet +import com.android.server.permission.access.immutable.MutableIndexedListSet +import com.android.server.permission.access.immutable.MutableIndexedMap +import com.android.server.permission.access.permission.PermissionFlags +import com.android.server.pm.permission.PermissionAllowlist +import com.android.server.pm.pkg.PackageState +import com.android.server.testutils.mock +import com.android.server.testutils.whenever +import com.google.common.truth.Truth.assertWithMessage +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +/** + * A parameterized test for testing evaluating permission states and inheriting implicit permission + * states for onUserAdded(), onStorageVolumeAdded() and onPackageAdded() in AppIdPermissionPolicy + */ +@RunWith(Parameterized::class) +class AppIdPermissionPolicyPermissionStatesTest : BaseAppIdPermissionPolicyTest() { + @Parameterized.Parameter(0) lateinit var action: Action + + @Before + override fun setUp() { + super.setUp() + if (action == Action.ON_USER_ADDED) { + createUserState(USER_ID_NEW) + } + } + + @Test + fun testEvaluatePermissionState_normalPermissionAlreadyGranted_remainsUnchanged() { + val oldFlags = PermissionFlags.INSTALL_GRANTED or PermissionFlags.INSTALL_REVOKED + testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_NORMAL) {} + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = oldFlags + assertWithMessage( + "After $action is called for a package that requests a normal permission" + + " with an existing INSTALL_GRANTED flag, the actual permission flags $actualFlags" + + " should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_normalPermissionNotInstallRevoked_getsGranted() { + val oldFlags = 0 + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_NORMAL, + isNewInstall = true + ) {} + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.INSTALL_GRANTED + assertWithMessage( + "After $action is called for a package that requests a normal permission" + + " with no existing flags, the actual permission flags $actualFlags" + + " should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_normalAppOpPermission_getsRoleAndUserSetFlagsPreserved() { + val oldFlags = PermissionFlags.ROLE or PermissionFlags.USER_SET + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_NORMAL or PermissionInfo.PROTECTION_FLAG_APPOP + ) {} + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.INSTALL_GRANTED or oldFlags + assertWithMessage( + "After $action is called for a package that requests a normal app op" + + " permission with existing ROLE and USER_SET flags, the actual permission flags" + + " $actualFlags should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_internalWasGrantedWithMissingPackage_getsProtectionGranted() { + val oldFlags = PermissionFlags.PROTECTION_GRANTED + testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_INTERNAL) { + val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE) + addPackageState(packageStateWithMissingPackage) + } + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = oldFlags + assertWithMessage( + "After $action is called for a package that requests an internal permission" + + " with missing android package and $oldFlags flag, the actual permission flags" + + " $actualFlags should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_internalAppOpPermission_getsRoleAndUserSetFlagsPreserved() { + val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or + PermissionFlags.USER_SET + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_APPOP + ) { + val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE) + addPackageState(packageStateWithMissingPackage) + } + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = oldFlags + assertWithMessage( + "After $action is called for a package that requests an internal permission" + + " with missing android package and $oldFlags flag and the permission isAppOp," + + " the actual permission flags $actualFlags should match the expected" + + " flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_internalDevelopmentPermission_getsRuntimeGrantedPreserved() { + val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.RUNTIME_GRANTED + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_DEVELOPMENT + ) { + val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE) + addPackageState(packageStateWithMissingPackage) + } + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = oldFlags + assertWithMessage( + "After $action is called for a package that requests an internal permission" + + " with missing android package and $oldFlags flag and permission isDevelopment," + + " the actual permission flags $actualFlags should match the expected" + + " flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_internalRolePermission_getsRoleAndRuntimeGrantedPreserved() { + val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or + PermissionFlags.RUNTIME_GRANTED + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_ROLE + ) { + val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE) + addPackageState(packageStateWithMissingPackage) + } + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = oldFlags + assertWithMessage( + "After $action is called for a package that requests an internal permission" + + " with missing android package and $oldFlags flag and the permission isRole," + + " the actual permission flags $actualFlags should match the expected" + + " flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_signaturePrivilegedPermissionNotAllowlisted_isNotGranted() { + val oldFlags = 0 + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_SIGNATURE or PermissionInfo.PROTECTION_FLAG_PRIVILEGED, + isInstalledPackageSystem = true, + isInstalledPackagePrivileged = true, + isInstalledPackageProduct = true, + // To mock the return value of shouldGrantPrivilegedOrOemPermission() + isInstalledPackageVendor = true, + isNewInstall = true + ) { + val platformPackage = mockPackageState( + PLATFORM_APP_ID, + mockAndroidPackage(PLATFORM_PACKAGE_NAME) + ) + setupAllowlist(PACKAGE_NAME_1, false) + addPackageState(platformPackage) + } + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = oldFlags + assertWithMessage( + "After $action is called for a package that requests a signature privileged" + + " permission that's not allowlisted, the actual permission" + + " flags $actualFlags should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_nonPrivilegedShouldGrantBySignature_getsProtectionGranted() { + val oldFlags = 0 + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_SIGNATURE, + isInstalledPackageSystem = true, + isInstalledPackagePrivileged = true, + isInstalledPackageProduct = true, + isInstalledPackageSignatureMatching = true, + isInstalledPackageVendor = true, + isNewInstall = true + ) { + val platformPackage = mockPackageState( + PLATFORM_APP_ID, + mockAndroidPackage(PLATFORM_PACKAGE_NAME, isSignatureMatching = true) + ) + setupAllowlist(PACKAGE_NAME_1, false) + addPackageState(platformPackage) + } + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.PROTECTION_GRANTED + assertWithMessage( + "After $action is called for a package that requests a signature" + + " non-privileged permission, the actual permission" + + " flags $actualFlags should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_privilegedAllowlistShouldGrantByProtectionFlags_getsGranted() { + val oldFlags = 0 + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_SIGNATURE or PermissionInfo.PROTECTION_FLAG_PRIVILEGED, + isInstalledPackageSystem = true, + isInstalledPackagePrivileged = true, + isInstalledPackageProduct = true, + isNewInstall = true + ) { + val platformPackage = mockPackageState( + PLATFORM_APP_ID, + mockAndroidPackage(PLATFORM_PACKAGE_NAME) + ) + setupAllowlist(PACKAGE_NAME_1, true) + addPackageState(platformPackage) + } + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.PROTECTION_GRANTED + assertWithMessage( + "After $action is called for a package that requests a signature privileged" + + " permission that's allowlisted and should grant by protection flags, the actual" + + " permission flags $actualFlags should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + private fun setupAllowlist( + packageName: String, + allowlistState: Boolean, + state: MutableAccessState = oldState + ) { + state.mutateExternalState().setPrivilegedPermissionAllowlistPackages( + MutableIndexedListSet<String>().apply { add(packageName) } + ) + val mockAllowlist = mock<PermissionAllowlist> { + whenever( + getProductPrivilegedAppAllowlistState(packageName, PERMISSION_NAME_0) + ).thenReturn(allowlistState) + } + state.mutateExternalState().setPermissionAllowlist(mockAllowlist) + } + + @Test + fun testEvaluatePermissionState_nonRuntimeFlagsOnRuntimePermissions_getsCleared() { + val oldFlags = PermissionFlags.INSTALL_GRANTED or PermissionFlags.PREGRANT or + PermissionFlags.RUNTIME_GRANTED + testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {} + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.PREGRANT or PermissionFlags.RUNTIME_GRANTED + assertWithMessage( + "After $action is called for a package that requests a runtime permission" + + " with existing $oldFlags flags, the actual permission flags $actualFlags should" + + " match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_newPermissionsForPreM_requiresUserReview() { + val oldFlags = 0 + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_DANGEROUS, + installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP, + isNewInstall = true + ) {} + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT + assertWithMessage( + "After $action is called for a package that requests a runtime permission" + + " with no existing flags in pre M, actual permission flags $actualFlags should" + + " match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_legacyOrImplicitGrantedPreviouslyRevoked_getsAppOpRevoked() { + val oldFlags = PermissionFlags.USER_FIXED + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_DANGEROUS, + installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP + ) { + setPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0, oldFlags) + } + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.USER_FIXED or + PermissionFlags.APP_OP_REVOKED + assertWithMessage( + "After $action is called for a package that requests a runtime permission" + + " that should be LEGACY_GRANTED or IMPLICIT_GRANTED that was previously revoked," + + " the actual permission flags $actualFlags should" + + " match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_legacyGrantedForPostM_userReviewRequirementRemoved() { + val oldFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT + testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {} + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = 0 + assertWithMessage( + "After $action is called for a package that requests a runtime permission" + + " that used to require user review, the user review requirement should be removed" + + " if it's upgraded to post M. The actual permission flags $actualFlags should" + + " match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_legacyGrantedPermissionsAlreadyReviewedForPostM_getsGranted() { + val oldFlags = PermissionFlags.LEGACY_GRANTED + testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {} + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED + assertWithMessage( + "After $action is called for a package that requests a runtime permission" + + " that was already reviewed by the user, the permission should be RUNTIME_GRANTED" + + " if it's upgraded to post M. The actual permission flags $actualFlags should" + + " match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_leanbackNotificationPermissionsForPostM_getsImplicitGranted() { + val oldFlags = 0 + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_DANGEROUS, + permissionName = PERMISSION_POST_NOTIFICATIONS, + isNewInstall = true + ) { + oldState.mutateExternalState().setLeanback(true) + } + + val actualFlags = getPermissionFlags( + APP_ID_1, + getUserIdEvaluated(), + PERMISSION_POST_NOTIFICATIONS + ) + val expectedNewFlags = PermissionFlags.IMPLICIT_GRANTED + assertWithMessage( + "After $action is called for a package that requests a runtime notification" + + " permission when isLeanback, the actual permission flags $actualFlags should" + + " match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_implicitSourceFromNonRuntime_getsImplicitGranted() { + val oldFlags = 0 + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_DANGEROUS, + implicitPermissions = setOf(PERMISSION_NAME_0), + isNewInstall = true + ) { + oldState.mutateExternalState().setImplicitToSourcePermissions( + MutableIndexedMap<String, IndexedListSet<String>>().apply { + put(PERMISSION_NAME_0, MutableIndexedListSet<String>().apply { + add(PERMISSION_NAME_1) + }) + } + ) + addPermission(mockParsedPermission(PERMISSION_NAME_1, PACKAGE_NAME_0)) + } + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT + assertWithMessage( + "After $action is called for a package that requests a runtime implicit" + + " permission that's source from a non-runtime permission, the actual permission" + + " flags $actualFlags should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + /** + * For a legacy granted or implicit permission during the app upgrade, when the permission + * should no longer be legacy or implicit granted, we want to remove the APP_OP_REVOKED flag + * so that the app can request the permission. + */ + @Test + fun testEvaluatePermissionState_noLongerLegacyOrImplicitGranted_canBeRequested() { + val oldFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.APP_OP_REVOKED or + PermissionFlags.RUNTIME_GRANTED + testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {} + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = 0 + assertWithMessage( + "After $action is called for a package that requests a runtime permission" + + " that is no longer LEGACY_GRANTED or IMPLICIT_GRANTED, the actual permission" + + " flags $actualFlags should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_noLongerImplicit_getsRuntimeAndImplicitFlagsRemoved() { + val oldFlags = PermissionFlags.IMPLICIT or PermissionFlags.RUNTIME_GRANTED or + PermissionFlags.USER_SET or PermissionFlags.USER_FIXED + testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {} + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = 0 + assertWithMessage( + "After $action is called for a package that requests a runtime permission" + + " that is no longer implicit and we shouldn't retain as nearby device" + + " permissions, the actual permission flags $actualFlags should match the expected" + + " flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_noLongerImplicitNearbyWasGranted_getsRuntimeGranted() { + val oldFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_DANGEROUS, + permissionName = PERMISSION_BLUETOOTH_CONNECT, + requestedPermissions = setOf( + PERMISSION_BLUETOOTH_CONNECT, + PERMISSION_ACCESS_BACKGROUND_LOCATION + ) + ) { + setPermissionFlags( + APP_ID_1, + getUserIdEvaluated(), + PERMISSION_ACCESS_BACKGROUND_LOCATION, + PermissionFlags.RUNTIME_GRANTED + ) + } + + val actualFlags = getPermissionFlags( + APP_ID_1, + getUserIdEvaluated(), + PERMISSION_BLUETOOTH_CONNECT + ) + val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED + assertWithMessage( + "After $action is called for a package that requests a runtime nearby device" + + " permission that was granted by implicit, the actual permission flags" + + " $actualFlags should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_noLongerImplicitSystemOrPolicyFixedWasGranted_runtimeGranted() { + val oldFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT or + PermissionFlags.SYSTEM_FIXED + testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {} + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.SYSTEM_FIXED + assertWithMessage( + "After $action is called for a package that requests a runtime permission" + + " that was granted and is no longer implicit and is SYSTEM_FIXED or POLICY_FIXED," + + " the actual permission flags $actualFlags should match the expected" + + " flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_restrictedPermissionsNotExempt_getsRestrictionFlags() { + val oldFlags = PermissionFlags.RESTRICTION_REVOKED + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_DANGEROUS, + permissionInfoFlags = PermissionInfo.FLAG_HARD_RESTRICTED + ) {} + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = oldFlags + assertWithMessage( + "After $action is called for a package that requests a runtime hard" + + " restricted permission that is not exempted, the actual permission flags" + + " $actualFlags should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testEvaluatePermissionState_restrictedPermissionsIsExempted_clearsRestrictionFlags() { + val oldFlags = 0 + testEvaluatePermissionState( + oldFlags, + PermissionInfo.PROTECTION_DANGEROUS, + permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED + ) {} + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.UPGRADE_EXEMPT + assertWithMessage( + "After $action is called for a package that requests a runtime soft" + + " restricted permission that is exempted, the actual permission flags" + + " $actualFlags should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testInheritImplicitPermissionStates_runtimeExistingImplicit_sourceFlagsNotInherited() { + val oldImplicitPermissionFlags = PermissionFlags.USER_FIXED + testInheritImplicitPermissionStates( + implicitPermissionFlags = oldImplicitPermissionFlags, + isNewInstallAndNewPermission = false + ) + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = oldImplicitPermissionFlags or PermissionFlags.IMPLICIT_GRANTED or + PermissionFlags.APP_OP_REVOKED + assertWithMessage( + "After $action is called for a package that requests a permission that is" + + " implicit, existing and runtime, it should not inherit the runtime flags from" + + " the source permission. Hence the actual permission flags $actualFlags should" + + " match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testInheritImplicitPermissionStates_nonRuntimeNewImplicit_sourceFlagsNotInherited() { + testInheritImplicitPermissionStates( + implicitPermissionProtectionLevel = PermissionInfo.PROTECTION_NORMAL + ) + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = PermissionFlags.INSTALL_GRANTED + assertWithMessage( + "After $action is called for a package that requests a permission that is" + + " implicit, new and non-runtime, it should not inherit the runtime flags from" + + " the source permission. Hence the actual permission flags $actualFlags should" + + " match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testInheritImplicitPermissionStates_runtimeNewImplicitPermissions_sourceFlagsInherited() { + val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET + testInheritImplicitPermissionStates(sourceRuntimeFlags = sourceRuntimeFlags) + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED or + PermissionFlags.IMPLICIT + assertWithMessage( + "After $action is called for a package that requests a permission that is" + + " implicit, new and runtime, it should inherit the runtime flags from" + + " the source permission. Hence the actual permission flags $actualFlags should" + + " match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testInheritImplicitPermissionStates_grantingNewFromRevokeImplicit_onlyInheritFromSource() { + val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET + testInheritImplicitPermissionStates( + implicitPermissionFlags = PermissionFlags.POLICY_FIXED, + sourceRuntimeFlags = sourceRuntimeFlags, + isAnySourcePermissionNonRuntime = false + ) + + val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0) + val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT + assertWithMessage( + "After $action is called for a package that requests a permission that is" + + " implicit, existing, runtime and revoked, it should only inherit runtime flags" + + " from source permission. Hence the actual permission flags $actualFlags should" + + " match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + /** + * If it's a media implicit permission (one of RETAIN_IMPLICIT_FLAGS_PERMISSIONS), we want to + * remove the IMPLICIT flag so that they will be granted when they are no longer implicit. + * (instead of revoking it) + */ + @Test + fun testInheritImplicitPermissionStates_mediaImplicitPermissions_getsImplicitFlagRemoved() { + val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET + testInheritImplicitPermissionStates( + implicitPermissionName = PERMISSION_ACCESS_MEDIA_LOCATION, + sourceRuntimeFlags = sourceRuntimeFlags + ) + + val actualFlags = getPermissionFlags( + APP_ID_1, + getUserIdEvaluated(), + PERMISSION_ACCESS_MEDIA_LOCATION + ) + val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED + assertWithMessage( + "After $action is called for a package that requests a media permission that" + + " is implicit, new and runtime, it should inherit the runtime flags from" + + " the source permission and have the IMPLICIT flag removed. Hence the actual" + + " permission flags $actualFlags should match the expected flags $expectedNewFlags" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + private fun testInheritImplicitPermissionStates( + implicitPermissionName: String = PERMISSION_NAME_0, + implicitPermissionFlags: Int = 0, + implicitPermissionProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS, + sourceRuntimeFlags: Int = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET, + isAnySourcePermissionNonRuntime: Boolean = true, + isNewInstallAndNewPermission: Boolean = true + ) { + val userId = getUserIdEvaluated() + val implicitPermission = mockParsedPermission( + implicitPermissionName, + PACKAGE_NAME_0, + protectionLevel = implicitPermissionProtectionLevel, + ) + // For source from non-runtime in order to grant by implicit + val sourcePermission1 = mockParsedPermission( + PERMISSION_NAME_1, + PACKAGE_NAME_0, + protectionLevel = if (isAnySourcePermissionNonRuntime) { + PermissionInfo.PROTECTION_NORMAL + } else { + PermissionInfo.PROTECTION_DANGEROUS + } + ) + // For inheriting runtime flags + val sourcePermission2 = mockParsedPermission( + PERMISSION_NAME_2, + PACKAGE_NAME_0, + protectionLevel = PermissionInfo.PROTECTION_DANGEROUS, + ) + val permissionOwnerPackageState = mockPackageState( + APP_ID_0, + mockAndroidPackage( + PACKAGE_NAME_0, + permissions = listOf(implicitPermission, sourcePermission1, sourcePermission2) + ) + ) + val installedPackageState = mockPackageState( + APP_ID_1, + mockAndroidPackage( + PACKAGE_NAME_1, + requestedPermissions = setOf( + implicitPermissionName, + PERMISSION_NAME_1, + PERMISSION_NAME_2 + ), + implicitPermissions = setOf(implicitPermissionName) + ) + ) + oldState.mutateExternalState().setImplicitToSourcePermissions( + MutableIndexedMap<String, IndexedListSet<String>>().apply { + put(implicitPermissionName, MutableIndexedListSet<String>().apply { + add(PERMISSION_NAME_1) + add(PERMISSION_NAME_2) + }) + } + ) + addPackageState(permissionOwnerPackageState) + addPermission(implicitPermission) + addPermission(sourcePermission1) + addPermission(sourcePermission2) + if (!isNewInstallAndNewPermission) { + addPackageState(installedPackageState) + setPermissionFlags(APP_ID_1, userId, implicitPermissionName, implicitPermissionFlags) + } + setPermissionFlags(APP_ID_1, userId, PERMISSION_NAME_2, sourceRuntimeFlags) + + mutateState { + if (isNewInstallAndNewPermission) { + addPackageState(installedPackageState) + setPermissionFlags( + APP_ID_1, + userId, + implicitPermissionName, + implicitPermissionFlags, + newState + ) + } + testAction(installedPackageState) + } + } + + /** + * Setup simple package states for testing evaluatePermissionState(). + * permissionOwnerPackageState is definer of permissionName with APP_ID_0. + * installedPackageState is the installed package that requests permissionName with APP_ID_1. + * + * @param oldFlags the existing permission flags for APP_ID_1, userId, permissionName + * @param protectionLevel the protectionLevel for the permission + * @param permissionName the name of the permission (1) being defined (2) of the oldFlags, and + * (3) requested by installedPackageState + * @param requestedPermissions the permissions requested by installedPackageState + * @param implicitPermissions the implicit permissions of installedPackageState + * @param permissionInfoFlags the flags for the permission itself + * @param isInstalledPackageSystem whether installedPackageState is a system package + * + * @return installedPackageState + */ + private fun testEvaluatePermissionState( + oldFlags: Int, + protectionLevel: Int, + permissionName: String = PERMISSION_NAME_0, + requestedPermissions: Set<String> = setOf(permissionName), + implicitPermissions: Set<String> = emptySet(), + permissionInfoFlags: Int = 0, + isInstalledPackageSystem: Boolean = false, + isInstalledPackagePrivileged: Boolean = false, + isInstalledPackageProduct: Boolean = false, + isInstalledPackageSignatureMatching: Boolean = false, + isInstalledPackageVendor: Boolean = false, + installedPackageTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, + isNewInstall: Boolean = false, + additionalSetup: () -> Unit + ) { + val userId = getUserIdEvaluated() + val parsedPermission = mockParsedPermission( + permissionName, + PACKAGE_NAME_0, + protectionLevel = protectionLevel, + flags = permissionInfoFlags + ) + val permissionOwnerPackageState = mockPackageState( + APP_ID_0, + mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission)) + ) + val installedPackageState = mockPackageState( + APP_ID_1, + mockAndroidPackage( + PACKAGE_NAME_1, + requestedPermissions = requestedPermissions, + implicitPermissions = implicitPermissions, + targetSdkVersion = installedPackageTargetSdkVersion, + isSignatureMatching = isInstalledPackageSignatureMatching + ), + isSystem = isInstalledPackageSystem, + isPrivileged = isInstalledPackagePrivileged, + isProduct = isInstalledPackageProduct, + isVendor = isInstalledPackageVendor + ) + addPackageState(permissionOwnerPackageState) + if (!isNewInstall) { + addPackageState(installedPackageState) + setPermissionFlags(APP_ID_1, userId, permissionName, oldFlags) + } + addPermission(parsedPermission) + + additionalSetup() + + mutateState { + if (isNewInstall) { + addPackageState(installedPackageState, newState) + setPermissionFlags(APP_ID_1, userId, permissionName, oldFlags, newState) + } + testAction(installedPackageState) + } + } + + private fun getUserIdEvaluated(): Int = when (action) { + Action.ON_USER_ADDED -> USER_ID_NEW + Action.ON_STORAGE_VOLUME_ADDED, Action.ON_PACKAGE_ADDED -> USER_ID_0 + } + + private fun MutateStateScope.testAction(packageState: PackageState) { + with(appIdPermissionPolicy) { + when (action) { + Action.ON_USER_ADDED -> onUserAdded(getUserIdEvaluated()) + Action.ON_STORAGE_VOLUME_ADDED -> onStorageVolumeMounted( + null, + listOf(packageState.packageName), + true + ) + Action.ON_PACKAGE_ADDED -> onPackageAdded(packageState) + } + } + } + + enum class Action { ON_USER_ADDED, ON_STORAGE_VOLUME_ADDED, ON_PACKAGE_ADDED } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun data(): Array<Action> = Action.values() + } +}
\ No newline at end of file diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt deleted file mode 100644 index 3cf57a3cf57a..000000000000 --- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt +++ /dev/null @@ -1,1937 +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.server.permission.test - -import android.Manifest -import android.content.pm.PackageManager -import android.content.pm.PermissionGroupInfo -import android.content.pm.PermissionInfo -import android.content.pm.SigningDetails -import android.os.Build -import android.os.Bundle -import android.util.ArrayMap -import android.util.SparseArray -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.modules.utils.testing.ExtendedMockitoRule -import com.android.server.extendedtestutils.wheneverStatic -import com.android.server.permission.access.MutableAccessState -import com.android.server.permission.access.MutableUserState -import com.android.server.permission.access.MutateStateScope -import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports -import com.android.server.permission.access.permission.AppIdPermissionPolicy -import com.android.server.permission.access.permission.Permission -import com.android.server.permission.access.permission.PermissionFlags -import com.android.server.permission.access.util.hasBits -import com.android.server.pm.parsing.PackageInfoUtils -import com.android.server.pm.permission.PermissionAllowlist -import com.android.server.pm.pkg.AndroidPackage -import com.android.server.pm.pkg.PackageState -import com.android.server.pm.pkg.PackageUserState -import com.android.server.pm.pkg.component.ParsedPermission -import com.android.server.pm.pkg.component.ParsedPermissionGroup -import com.android.server.testutils.any -import com.android.server.testutils.mock -import com.android.server.testutils.whenever -import com.google.common.truth.Truth.assertWithMessage -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.anyLong - -/** - * Mocking unit test for AppIdPermissionPolicy. - */ -@RunWith(AndroidJUnit4::class) -class AppIdPermissionPolicyTest { - private lateinit var oldState: MutableAccessState - private lateinit var newState: MutableAccessState - - private val defaultPermissionGroup = mockParsedPermissionGroup( - PERMISSION_GROUP_NAME_0, - PACKAGE_NAME_0 - ) - private val defaultPermissionTree = mockParsedPermission( - PERMISSION_TREE_NAME, - PACKAGE_NAME_0, - isTree = true - ) - private val defaultPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0) - - private val appIdPermissionPolicy = AppIdPermissionPolicy() - - @Rule - @JvmField - val extendedMockitoRule = ExtendedMockitoRule.Builder(this) - .spyStatic(PackageInfoUtils::class.java) - .build() - - @Before - fun setUp() { - oldState = MutableAccessState() - createUserState(USER_ID_0) - oldState.mutateExternalState().setPackageStates(ArrayMap()) - oldState.mutateExternalState().setDisabledSystemPackageStates(ArrayMap()) - mockPackageInfoUtilsGeneratePermissionInfo() - mockPackageInfoUtilsGeneratePermissionGroupInfo() - } - - private fun createUserState(userId: Int) { - oldState.mutateExternalState().mutateUserIds().add(userId) - oldState.mutateUserStatesNoWrite().put(userId, MutableUserState()) - } - - private fun mockPackageInfoUtilsGeneratePermissionInfo() { - wheneverStatic { - PackageInfoUtils.generatePermissionInfo(any(ParsedPermission::class.java), anyLong()) - }.thenAnswer { invocation -> - val parsedPermission = invocation.getArgument<ParsedPermission>(0) - val generateFlags = invocation.getArgument<Long>(1) - PermissionInfo(parsedPermission.backgroundPermission).apply { - name = parsedPermission.name - packageName = parsedPermission.packageName - metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) { - parsedPermission.metaData - } else { - null - } - @Suppress("DEPRECATION") - protectionLevel = parsedPermission.protectionLevel - group = parsedPermission.group - flags = parsedPermission.flags - } - } - } - - private fun mockPackageInfoUtilsGeneratePermissionGroupInfo() { - wheneverStatic { - PackageInfoUtils.generatePermissionGroupInfo( - any(ParsedPermissionGroup::class.java), - anyLong() - ) - }.thenAnswer { invocation -> - val parsedPermissionGroup = invocation.getArgument<ParsedPermissionGroup>(0) - val generateFlags = invocation.getArgument<Long>(1) - @Suppress("DEPRECATION") - PermissionGroupInfo().apply { - name = parsedPermissionGroup.name - packageName = parsedPermissionGroup.packageName - metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) { - parsedPermissionGroup.metaData - } else { - null - } - flags = parsedPermissionGroup.flags - } - } - } - - @Test - fun testResetRuntimePermissions_runtimeGranted_getsRevoked() { - val oldFlags = PermissionFlags.RUNTIME_GRANTED - val expectedNewFlags = 0 - testResetRuntimePermissions(oldFlags, expectedNewFlags) - } - - @Test - fun testResetRuntimePermissions_roleGranted_getsGranted() { - val oldFlags = PermissionFlags.ROLE - val expectedNewFlags = PermissionFlags.ROLE or PermissionFlags.RUNTIME_GRANTED - testResetRuntimePermissions(oldFlags, expectedNewFlags) - } - - @Test - fun testResetRuntimePermissions_nullAndroidPackage_remainsUnchanged() { - val oldFlags = PermissionFlags.RUNTIME_GRANTED - val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED - testResetRuntimePermissions(oldFlags, expectedNewFlags, isAndroidPackageMissing = true) - } - - private fun testResetRuntimePermissions( - oldFlags: Int, - expectedNewFlags: Int, - isAndroidPackageMissing: Boolean = false - ) { - val parsedPermission = mockParsedPermission( - PERMISSION_NAME_0, - PACKAGE_NAME_0, - protectionLevel = PermissionInfo.PROTECTION_DANGEROUS, - ) - val permissionOwnerPackageState = mockPackageState( - APP_ID_0, - mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission)) - ) - val requestingPackageState = if (isAndroidPackageMissing) { - mockPackageState(APP_ID_1, PACKAGE_NAME_1) - } else { - mockPackageState( - APP_ID_1, - mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0)) - ) - } - setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags) - addPackageState(permissionOwnerPackageState) - addPackageState(requestingPackageState) - addPermission(parsedPermission) - - mutateState { - with(appIdPermissionPolicy) { - resetRuntimePermissions(PACKAGE_NAME_1, USER_ID_0) - } - } - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - assertWithMessage( - "After resetting runtime permissions, permission flags did not match" + - " expected values: expectedNewFlags is $expectedNewFlags," + - " actualFlags is $actualFlags, while the oldFlags is $oldFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_permissionsOfMissingSystemApp_getsAdopted() { - testAdoptPermissions(hasMissingPackage = true, isSystem = true) - - assertWithMessage( - "After onPackageAdded() is called for a null adopt permission package," + - " the permission package name: ${getPermission(PERMISSION_NAME_0)?.packageName}" + - " did not match the expected package name: $PACKAGE_NAME_0" - ) - .that(getPermission(PERMISSION_NAME_0)?.packageName) - .isEqualTo(PACKAGE_NAME_0) - } - - @Test - fun testOnPackageAdded_permissionsOfExistingSystemApp_notAdopted() { - testAdoptPermissions(isSystem = true) - - assertWithMessage( - "After onPackageAdded() is called for a non-null adopt permission" + - " package, the permission package name:" + - " ${getPermission(PERMISSION_NAME_0)?.packageName} should not match the" + - " package name: $PACKAGE_NAME_0" - ) - .that(getPermission(PERMISSION_NAME_0)?.packageName) - .isNotEqualTo(PACKAGE_NAME_0) - } - - @Test - fun testOnPackageAdded_permissionsOfNonSystemApp_notAdopted() { - testAdoptPermissions(hasMissingPackage = true) - - assertWithMessage( - "After onPackageAdded() is called for a non-system adopt permission" + - " package, the permission package name:" + - " ${getPermission(PERMISSION_NAME_0)?.packageName} should not match the" + - " package name: $PACKAGE_NAME_0" - ) - .that(getPermission(PERMISSION_NAME_0)?.packageName) - .isNotEqualTo(PACKAGE_NAME_0) - } - - private fun testAdoptPermissions( - hasMissingPackage: Boolean = false, - isSystem: Boolean = false - ) { - val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_1) - val packageToAdoptPermission = if (hasMissingPackage) { - mockPackageState(APP_ID_1, PACKAGE_NAME_1, isSystem = isSystem) - } else { - mockPackageState( - APP_ID_1, - mockAndroidPackage( - PACKAGE_NAME_1, - permissions = listOf(parsedPermission) - ), - isSystem = isSystem - ) - } - addPackageState(packageToAdoptPermission) - addPermission(parsedPermission) - - mutateState { - val installedPackage = mockPackageState( - APP_ID_0, - mockAndroidPackage( - PACKAGE_NAME_0, - permissions = listOf(defaultPermission), - adoptPermissions = listOf(PACKAGE_NAME_1) - ) - ) - addPackageState(installedPackage, newState) - with(appIdPermissionPolicy) { - onPackageAdded(installedPackage) - } - } - } - - @Test - fun testOnPackageAdded_newPermissionGroup_getsDeclared() { - mutateState { - val packageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) - addPackageState(packageState, newState) - with(appIdPermissionPolicy) { - onPackageAdded(packageState) - } - } - - assertWithMessage( - "After onPackageAdded() is called when there is no existing" + - " permission groups, the new permission group $PERMISSION_GROUP_NAME_0 is not added" - ) - .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.name) - .isEqualTo(PERMISSION_GROUP_NAME_0) - } - - @Test - fun testOnPackageAdded_systemAppTakingOverPermissionGroupDefinition_getsTakenOver() { - testTakingOverPermissionAndPermissionGroupDefinitions(newPermissionOwnerIsSystem = true) - - assertWithMessage( - "After onPackageAdded() is called when $PERMISSION_GROUP_NAME_0 already" + - " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover the" + - " ownership of this permission group" - ) - .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName) - .isEqualTo(PACKAGE_NAME_0) - } - - @Test - fun testOnPackageAdded_instantApps_remainsUnchanged() { - testTakingOverPermissionAndPermissionGroupDefinitions( - newPermissionOwnerIsInstant = true, - permissionGroupAlreadyExists = false - ) - - assertWithMessage( - "After onPackageAdded() is called for an instant app," + - " the new permission group $PERMISSION_GROUP_NAME_0 should not be added" - ) - .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)) - .isNull() - } - - @Test - fun testOnPackageAdded_nonSystemAppTakingOverPermissionGroupDefinition_remainsUnchanged() { - testTakingOverPermissionAndPermissionGroupDefinitions() - - assertWithMessage( - "After onPackageAdded() is called when $PERMISSION_GROUP_NAME_0 already" + - " exists in the system, non-system app $PACKAGE_NAME_0 shouldn't takeover" + - " ownership of this permission group" - ) - .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName) - .isEqualTo(PACKAGE_NAME_1) - } - - @Test - fun testOnPackageAdded_takingOverPermissionGroupDeclaredBySystemApp_remainsUnchanged() { - testTakingOverPermissionAndPermissionGroupDefinitions(oldPermissionOwnerIsSystem = true) - - assertWithMessage( - "After onPackageAdded() is called when $PERMISSION_GROUP_NAME_0 already" + - " exists in the system and is owned by a system app, app $PACKAGE_NAME_0" + - " shouldn't takeover ownership of this permission group" - ) - .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName) - .isEqualTo(PACKAGE_NAME_1) - } - - @Test - fun testOnPackageAdded_newPermission_getsDeclared() { - mutateState { - val packageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) - addPackageState(packageState, newState) - with(appIdPermissionPolicy) { - onPackageAdded(packageState) - } - } - - assertWithMessage( - "After onPackageAdded() is called when there is no existing" + - " permissions, the new permission $PERMISSION_NAME_0 is not added" - ) - .that(getPermission(PERMISSION_NAME_0)?.name) - .isEqualTo(PERMISSION_NAME_0) - } - - @Test - fun testOnPackageAdded_configPermission_getsTakenOver() { - testTakingOverPermissionAndPermissionGroupDefinitions( - oldPermissionOwnerIsSystem = true, - newPermissionOwnerIsSystem = true, - type = Permission.TYPE_CONFIG, - isReconciled = false - ) - - assertWithMessage( - "After onPackageAdded() is called for a config permission with" + - " no owner, the ownership is not taken over by a system app $PACKAGE_NAME_0" - ) - .that(getPermission(PERMISSION_NAME_0)?.packageName) - .isEqualTo(PACKAGE_NAME_0) - } - - @Test - fun testOnPackageAdded_systemAppTakingOverPermissionDefinition_getsTakenOver() { - testTakingOverPermissionAndPermissionGroupDefinitions(newPermissionOwnerIsSystem = true) - - assertWithMessage( - "After onPackageAdded() is called when $PERMISSION_NAME_0 already" + - " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover ownership" + - " of this permission" - ) - .that(getPermission(PERMISSION_NAME_0)?.packageName) - .isEqualTo(PACKAGE_NAME_0) - } - - @Test - fun testOnPackageAdded_nonSystemAppTakingOverPermissionDefinition_remainsUnchanged() { - testTakingOverPermissionAndPermissionGroupDefinitions() - - assertWithMessage( - "After onPackageAdded() is called when $PERMISSION_NAME_0 already" + - " exists in the system, the non-system app $PACKAGE_NAME_0 shouldn't takeover" + - " ownership of this permission" - ) - .that(getPermission(PERMISSION_NAME_0)?.packageName) - .isEqualTo(PACKAGE_NAME_1) - } - - @Test - fun testOnPackageAdded_takingOverPermissionDeclaredBySystemApp_remainsUnchanged() { - testTakingOverPermissionAndPermissionGroupDefinitions(oldPermissionOwnerIsSystem = true) - - assertWithMessage( - "After onPackageAdded() is called when $PERMISSION_NAME_0 already" + - " exists in system and is owned by a system app, the $PACKAGE_NAME_0 shouldn't" + - " takeover ownership of this permission" - ) - .that(getPermission(PERMISSION_NAME_0)?.packageName) - .isEqualTo(PACKAGE_NAME_1) - } - - private fun testTakingOverPermissionAndPermissionGroupDefinitions( - oldPermissionOwnerIsSystem: Boolean = false, - newPermissionOwnerIsSystem: Boolean = false, - newPermissionOwnerIsInstant: Boolean = false, - permissionGroupAlreadyExists: Boolean = true, - permissionAlreadyExists: Boolean = true, - type: Int = Permission.TYPE_MANIFEST, - isReconciled: Boolean = true, - ) { - val oldPermissionOwnerPackageState = mockPackageState( - APP_ID_1, - PACKAGE_NAME_1, - isSystem = oldPermissionOwnerIsSystem - ) - addPackageState(oldPermissionOwnerPackageState) - if (permissionGroupAlreadyExists) { - addPermissionGroup(mockParsedPermissionGroup(PERMISSION_GROUP_NAME_0, PACKAGE_NAME_1)) - } - if (permissionAlreadyExists) { - addPermission( - mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_1), - type = type, - isReconciled = isReconciled - ) - } - - mutateState { - val newPermissionOwnerPackageState = mockPackageState( - APP_ID_0, - mockSimpleAndroidPackage(), - isSystem = newPermissionOwnerIsSystem, - isInstantApp = newPermissionOwnerIsInstant - ) - addPackageState(newPermissionOwnerPackageState, newState) - with(appIdPermissionPolicy) { - onPackageAdded(newPermissionOwnerPackageState) - } - } - } - - @Test - fun testOnPackageAdded_permissionGroupChanged_getsRevoked() { - testPermissionChanged( - oldPermissionGroup = PERMISSION_GROUP_NAME_1, - newPermissionGroup = PERMISSION_GROUP_NAME_0 - ) - - val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = 0 - assertWithMessage( - "After onPackageAdded() is called for a package that has a permission group change" + - " for a permission it defines, the actual permission flags $actualFlags" + - " should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_protectionLevelChanged_getsRevoked() { - testPermissionChanged(newProtectionLevel = PermissionInfo.PROTECTION_INTERNAL) - - val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = 0 - assertWithMessage( - "After onPackageAdded() is called for a package that has a protection level change" + - " for a permission it defines, the actual permission flags $actualFlags" + - " should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - private fun testPermissionChanged( - oldPermissionGroup: String? = null, - newPermissionGroup: String? = null, - newProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS - ) { - val oldPermission = mockParsedPermission( - PERMISSION_NAME_0, - PACKAGE_NAME_0, - group = oldPermissionGroup, - protectionLevel = PermissionInfo.PROTECTION_DANGEROUS - ) - val oldPackageState = mockPackageState( - APP_ID_0, - mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(oldPermission)) - ) - addPackageState(oldPackageState) - addPermission(oldPermission) - setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED) - - mutateState { - val newPermission = mockParsedPermission( - PERMISSION_NAME_0, - PACKAGE_NAME_0, - group = newPermissionGroup, - protectionLevel = newProtectionLevel - ) - val newPackageState = mockPackageState( - APP_ID_0, - mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(newPermission)) - ) - addPackageState(newPackageState, newState) - with(appIdPermissionPolicy) { - onPackageAdded(newPackageState) - } - } - } - - @Test - fun testOnPackageAdded_permissionTreeNoLongerDeclared_getsDefinitionRemoved() { - testPermissionDeclaration {} - - assertWithMessage( - "After onPackageAdded() is called for a package that no longer defines a permission" + - " tree, the permission tree: $PERMISSION_NAME_0 in system state should be removed" - ) - .that(getPermissionTree(PERMISSION_NAME_0)) - .isNull() - } - - @Test - fun testOnPackageAdded_permissionTreeByDisabledSystemPackage_remainsUnchanged() { - testPermissionDeclaration { - val disabledSystemPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) - addDisabledSystemPackageState(disabledSystemPackageState) - } - - assertWithMessage( - "After onPackageAdded() is called for a package that no longer defines" + - " a permission tree while this permission tree is still defined by" + - " a disabled system package, the permission tree: $PERMISSION_NAME_0 in" + - " system state should not be removed" - ) - .that(getPermissionTree(PERMISSION_TREE_NAME)) - .isNotNull() - } - - @Test - fun testOnPackageAdded_permissionNoLongerDeclared_getsDefinitionRemoved() { - testPermissionDeclaration {} - - assertWithMessage( - "After onPackageAdded() is called for a package that no longer defines a permission," + - " the permission: $PERMISSION_NAME_0 in system state should be removed" - ) - .that(getPermission(PERMISSION_NAME_0)) - .isNull() - } - - @Test - fun testOnPackageAdded_permissionByDisabledSystemPackage_remainsUnchanged() { - testPermissionDeclaration { - val disabledSystemPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) - addDisabledSystemPackageState(disabledSystemPackageState) - } - - assertWithMessage( - "After onPackageAdded() is called for a disabled system package and it's updated apk" + - " no longer defines a permission, the permission: $PERMISSION_NAME_0 in" + - " system state should not be removed" - ) - .that(getPermission(PERMISSION_NAME_0)) - .isNotNull() - } - - private fun testPermissionDeclaration(additionalSetup: () -> Unit) { - val oldPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) - addPackageState(oldPackageState) - addPermission(defaultPermissionTree) - addPermission(defaultPermission) - - additionalSetup() - - mutateState { - val newPackageState = mockPackageState(APP_ID_0, mockAndroidPackage(PACKAGE_NAME_0)) - addPackageState(newPackageState, newState) - with(appIdPermissionPolicy) { - onPackageAdded(newPackageState) - } - } - } - - @Test - fun testOnPackageAdded_permissionsNoLongerRequested_getsFlagsRevoked() { - val parsedPermission = mockParsedPermission( - PERMISSION_NAME_0, - PACKAGE_NAME_0, - protectionLevel = PermissionInfo.PROTECTION_DANGEROUS - ) - val oldPackageState = mockPackageState( - APP_ID_0, - mockAndroidPackage( - PACKAGE_NAME_0, - permissions = listOf(parsedPermission), - requestedPermissions = setOf(PERMISSION_NAME_0) - ) - ) - addPackageState(oldPackageState) - addPermission(parsedPermission) - setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED) - - mutateState { - val newPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage()) - addPackageState(newPackageState, newState) - with(appIdPermissionPolicy) { - onPackageAdded(newPackageState) - } - } - - val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = 0 - assertWithMessage( - "After onPackageAdded() is called for a package that no longer requests a permission" + - " the actual permission flags $actualFlags should match the" + - " expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_storageAndMediaPermissionsDowngradingPastQ_getsRuntimeRevoked() { - testRevokePermissionsOnPackageUpdate( - PermissionFlags.RUNTIME_GRANTED, - newTargetSdkVersion = Build.VERSION_CODES.P - ) - - val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE) - val expectedNewFlags = 0 - assertWithMessage( - "After onPackageAdded() is called for a package that's downgrading past Q" + - " the actual permission flags $actualFlags should match the" + - " expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_storageAndMediaPermissionsNotDowngradingPastQ_remainsUnchanged() { - val oldFlags = PermissionFlags.RUNTIME_GRANTED - testRevokePermissionsOnPackageUpdate( - oldFlags, - oldTargetSdkVersion = Build.VERSION_CODES.P, - newTargetSdkVersion = Build.VERSION_CODES.P - ) - - val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE) - val expectedNewFlags = oldFlags - assertWithMessage( - "After onPackageAdded() is called for a package that's not downgrading past Q" + - " the actual permission flags $actualFlags should match the" + - " expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_policyFixedDowngradingPastQ_remainsUnchanged() { - val oldFlags = PermissionFlags.RUNTIME_GRANTED and PermissionFlags.POLICY_FIXED - testRevokePermissionsOnPackageUpdate(oldFlags, newTargetSdkVersion = Build.VERSION_CODES.P) - - val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE) - val expectedNewFlags = oldFlags - assertWithMessage( - "After onPackageAdded() is called for a package that's downgrading past Q" + - " the actual permission flags with PermissionFlags.POLICY_FIXED $actualFlags" + - " should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_newlyRequestingLegacyExternalStorage_getsRuntimeRevoked() { - testRevokePermissionsOnPackageUpdate( - PermissionFlags.RUNTIME_GRANTED, - oldTargetSdkVersion = Build.VERSION_CODES.P, - newTargetSdkVersion = Build.VERSION_CODES.P, - oldIsRequestLegacyExternalStorage = false - ) - - val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE) - val expectedNewFlags = 0 - assertWithMessage( - "After onPackageAdded() is called for a package with" + - " newlyRequestingLegacyExternalStorage, the actual permission flags $actualFlags" + - " should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_missingOldPackage_remainsUnchanged() { - val oldFlags = PermissionFlags.RUNTIME_GRANTED - testRevokePermissionsOnPackageUpdate( - oldFlags, - newTargetSdkVersion = Build.VERSION_CODES.P, - isOldPackageMissing = true - ) - - val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE) - val expectedNewFlags = oldFlags - assertWithMessage( - "After onPackageAdded() is called for a package that's downgrading past Q" + - " and doesn't have the oldPackage, the actual permission flags $actualFlags" + - " should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - private fun testRevokePermissionsOnPackageUpdate( - oldFlags: Int, - oldTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, - newTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, - oldIsRequestLegacyExternalStorage: Boolean = true, - newIsRequestLegacyExternalStorage: Boolean = true, - isOldPackageMissing: Boolean = false - ) { - val parsedPermission = mockParsedPermission( - PERMISSION_READ_EXTERNAL_STORAGE, - PACKAGE_NAME_0, - protectionLevel = PermissionInfo.PROTECTION_DANGEROUS - ) - val oldPackageState = if (isOldPackageMissing) { - mockPackageState(APP_ID_0, PACKAGE_NAME_0) - } else { - mockPackageState( - APP_ID_0, - mockAndroidPackage( - PACKAGE_NAME_0, - targetSdkVersion = oldTargetSdkVersion, - isRequestLegacyExternalStorage = oldIsRequestLegacyExternalStorage, - requestedPermissions = setOf(PERMISSION_READ_EXTERNAL_STORAGE), - permissions = listOf(parsedPermission) - ) - ) - } - addPackageState(oldPackageState) - addPermission(parsedPermission) - setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE, oldFlags) - - mutateState { - val newPackageState = mockPackageState( - APP_ID_0, - mockAndroidPackage( - PACKAGE_NAME_0, - targetSdkVersion = newTargetSdkVersion, - isRequestLegacyExternalStorage = newIsRequestLegacyExternalStorage, - requestedPermissions = setOf(PERMISSION_READ_EXTERNAL_STORAGE), - permissions = listOf(parsedPermission) - ) - ) - addPackageState(newPackageState, newState) - with(appIdPermissionPolicy) { - onPackageAdded(newPackageState) - } - } - } - - @Test - fun testOnPackageAdded_normalPermissionAlreadyGranted_remainsUnchanged() { - val oldFlags = PermissionFlags.INSTALL_GRANTED or PermissionFlags.INSTALL_REVOKED - testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_NORMAL) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = oldFlags - assertWithMessage( - "After onPackageAdded() is called for a package that requests a normal permission" + - " with an existing INSTALL_GRANTED flag, the actual permission flags $actualFlags" + - " should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_normalPermissionNotInstallRevoked_getsGranted() { - val oldFlags = 0 - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_NORMAL, - isNewInstall = true - ) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.INSTALL_GRANTED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a normal permission" + - " with no existing flags, the actual permission flags $actualFlags" + - " should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_normalPermissionRequestedByInstalledPackage_getsGranted() { - val oldFlags = PermissionFlags.INSTALL_REVOKED - testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_NORMAL) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.INSTALL_GRANTED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a normal permission" + - " with the INSTALL_REVOKED flag, the actual permission flags $actualFlags" + - " should match the expected flags $expectedNewFlags since it's a new install" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - /** - * We setup a permission protection level change from SIGNATURE to NORMAL in order to make - * the permission a "changed permission" in order to test evaluatePermissionState() called by - * evaluatePermissionStateForAllPackages(). This makes the requestingPackageState not the - * installedPackageState so that we can test whether requesting by system package will give us - * the expected permission flags. - * - * Besides, this also helps us test evaluatePermissionStateForAllPackages(). Since both - * evaluatePermissionStateForAllPackages() and evaluateAllPermissionStatesForPackage() call - * evaluatePermissionState() in their implementations, we use these tests as the only tests - * that test evaluatePermissionStateForAllPackages() - */ - @Test - fun testOnPackageAdded_normalPermissionRequestedBySystemPackage_getsGranted() { - testEvaluateNormalPermissionStateWithPermissionChanges(requestingPackageIsSystem = true) - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.INSTALL_GRANTED - assertWithMessage( - "After onPackageAdded() is called for a system package that requests a normal" + - " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" + - " should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_normalCompatibilityPermission_getsGranted() { - testEvaluateNormalPermissionStateWithPermissionChanges( - permissionName = PERMISSION_POST_NOTIFICATIONS, - requestingPackageTargetSdkVersion = Build.VERSION_CODES.S - ) - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_POST_NOTIFICATIONS) - val expectedNewFlags = PermissionFlags.INSTALL_GRANTED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a normal compatibility" + - " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" + - " should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_normalPermissionPreviouslyRevoked_getsInstallRevoked() { - testEvaluateNormalPermissionStateWithPermissionChanges() - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.INSTALL_REVOKED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a normal" + - " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" + - " should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - private fun testEvaluateNormalPermissionStateWithPermissionChanges( - permissionName: String = PERMISSION_NAME_0, - requestingPackageTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, - requestingPackageIsSystem: Boolean = false - ) { - val oldParsedPermission = mockParsedPermission( - permissionName, - PACKAGE_NAME_0, - protectionLevel = PermissionInfo.PROTECTION_SIGNATURE - ) - val oldPermissionOwnerPackageState = mockPackageState( - APP_ID_0, - mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(oldParsedPermission)) - ) - val requestingPackageState = mockPackageState( - APP_ID_1, - mockAndroidPackage( - PACKAGE_NAME_1, - requestedPermissions = setOf(permissionName), - targetSdkVersion = requestingPackageTargetSdkVersion - ), - isSystem = requestingPackageIsSystem, - ) - addPackageState(oldPermissionOwnerPackageState) - addPackageState(requestingPackageState) - addPermission(oldParsedPermission) - val oldFlags = PermissionFlags.INSTALL_REVOKED - setPermissionFlags(APP_ID_1, USER_ID_0, permissionName, oldFlags) - - mutateState { - val newPermissionOwnerPackageState = mockPackageState( - APP_ID_0, - mockAndroidPackage( - PACKAGE_NAME_0, - permissions = listOf(mockParsedPermission(permissionName, PACKAGE_NAME_0)) - ) - ) - addPackageState(newPermissionOwnerPackageState, newState) - with(appIdPermissionPolicy) { - onPackageAdded(newPermissionOwnerPackageState) - } - } - } - - @Test - fun testOnPackageAdded_normalAppOpPermission_getsRoleAndUserSetFlagsPreserved() { - val oldFlags = PermissionFlags.ROLE or PermissionFlags.USER_SET - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_NORMAL or PermissionInfo.PROTECTION_FLAG_APPOP - ) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.INSTALL_GRANTED or oldFlags - assertWithMessage( - "After onPackageAdded() is called for a package that requests a normal app op" + - " permission with existing ROLE and USER_SET flags, the actual permission flags" + - " $actualFlags should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_internalPermissionWasGrantedWithMissingPackage_getsProtectionGranted() { - val oldFlags = PermissionFlags.PROTECTION_GRANTED - testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_INTERNAL) { - val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE) - addPackageState(packageStateWithMissingPackage) - } - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = oldFlags - assertWithMessage( - "After onPackageAdded() is called for a package that requests an internal permission" + - " with missing android package and $oldFlags flag, the actual permission flags" + - " $actualFlags should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_internalAppOpPermission_getsRoleAndUserSetFlagsPreserved() { - val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or - PermissionFlags.USER_SET - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_APPOP - ) { - val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE) - addPackageState(packageStateWithMissingPackage) - } - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = oldFlags - assertWithMessage( - "After onPackageAdded() is called for a package that requests an internal permission" + - " with missing android package and $oldFlags flag and the permission isAppOp," + - " the actual permission flags $actualFlags should match the expected" + - " flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_internalDevelopmentPermission_getsRuntimeGrantedPreserved() { - val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.RUNTIME_GRANTED - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_DEVELOPMENT - ) { - val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE) - addPackageState(packageStateWithMissingPackage) - } - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = oldFlags - assertWithMessage( - "After onPackageAdded() is called for a package that requests an internal permission" + - " with missing android package and $oldFlags flag and permission isDevelopment," + - " the actual permission flags $actualFlags should match the expected" + - " flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_internalRolePermission_getsRoleAndRuntimeGrantedPreserved() { - val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or - PermissionFlags.RUNTIME_GRANTED - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_ROLE - ) { - val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE) - addPackageState(packageStateWithMissingPackage) - } - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = oldFlags - assertWithMessage( - "After onPackageAdded() is called for a package that requests an internal permission" + - " with missing android package and $oldFlags flag and the permission isRole," + - " the actual permission flags $actualFlags should match the expected" + - " flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_signaturePrivilegedPermissionNotAllowlisted_isNotGranted() { - val oldFlags = 0 - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_SIGNATURE or PermissionInfo.PROTECTION_FLAG_PRIVILEGED, - isInstalledPackageSystem = true, - isInstalledPackagePrivileged = true, - isInstalledPackageProduct = true, - // To mock the return value of shouldGrantPrivilegedOrOemPermission() - isInstalledPackageVendor = true, - isNewInstall = true - ) { - val platformPackage = mockPackageState( - PLATFORM_APP_ID, - mockAndroidPackage(PLATFORM_PACKAGE_NAME) - ) - setupAllowlist(PACKAGE_NAME_1, false) - addPackageState(platformPackage) - } - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = oldFlags - assertWithMessage( - "After onPackageAdded() is called for a package that requests a signature privileged" + - " permission that's not allowlisted, the actual permission" + - " flags $actualFlags should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_nonPrivilegedPermissionShouldGrantBySignature_getsProtectionGranted() { - val oldFlags = 0 - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_SIGNATURE, - isInstalledPackageSystem = true, - isInstalledPackagePrivileged = true, - isInstalledPackageProduct = true, - isInstalledPackageSignatureMatching = true, - isInstalledPackageVendor = true, - isNewInstall = true - ) { - val platformPackage = mockPackageState( - PLATFORM_APP_ID, - mockAndroidPackage(PLATFORM_PACKAGE_NAME, isSignatureMatching = true) - ) - setupAllowlist(PACKAGE_NAME_1, false) - addPackageState(platformPackage) - } - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.PROTECTION_GRANTED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a signature" + - " non-privileged permission, the actual permission" + - " flags $actualFlags should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_privilegedAllowlistPermissionShouldGrantByProtectionFlags_getsGranted() { - val oldFlags = 0 - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_SIGNATURE or PermissionInfo.PROTECTION_FLAG_PRIVILEGED, - isInstalledPackageSystem = true, - isInstalledPackagePrivileged = true, - isInstalledPackageProduct = true, - isNewInstall = true - ) { - val platformPackage = mockPackageState( - PLATFORM_APP_ID, - mockAndroidPackage(PLATFORM_PACKAGE_NAME) - ) - setupAllowlist(PACKAGE_NAME_1, true) - addPackageState(platformPackage) - } - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.PROTECTION_GRANTED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a signature privileged" + - " permission that's allowlisted and should grant by protection flags, the actual" + - " permission flags $actualFlags should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - private fun setupAllowlist( - packageName: String, - allowlistState: Boolean, - state: MutableAccessState = oldState - ) { - state.mutateExternalState().setPrivilegedPermissionAllowlistPackages( - MutableIndexedListSet<String>().apply { add(packageName) } - ) - val mockAllowlist = mock<PermissionAllowlist> { - whenever( - getProductPrivilegedAppAllowlistState(packageName, PERMISSION_NAME_0) - ).thenReturn(allowlistState) - } - state.mutateExternalState().setPermissionAllowlist(mockAllowlist) - } - - @Test - fun testOnPackageAdded_nonRuntimeFlagsOnRuntimePermissions_getsCleared() { - val oldFlags = PermissionFlags.INSTALL_GRANTED or PermissionFlags.PREGRANT or - PermissionFlags.RUNTIME_GRANTED - testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.PREGRANT or PermissionFlags.RUNTIME_GRANTED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime permission" + - " with existing $oldFlags flags, the actual permission flags $actualFlags should" + - " match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_newPermissionsForPreM_requiresUserReview() { - val oldFlags = 0 - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_DANGEROUS, - installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP, - isNewInstall = true - ) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime permission" + - " with no existing flags in pre M, actual permission flags $actualFlags should" + - " match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_legacyOrImplicitGrantedPermissionPreviouslyRevoked_getsAppOpRevoked() { - val oldFlags = PermissionFlags.USER_FIXED - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_DANGEROUS, - installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP - ) { - setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags) - } - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.USER_FIXED or - PermissionFlags.APP_OP_REVOKED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime permission" + - " that should be LEGACY_GRANTED or IMPLICIT_GRANTED that was previously revoked," + - " the actual permission flags $actualFlags should" + - " match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_legacyGrantedPermissionsForPostM_userReviewRequirementRemoved() { - val oldFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT - testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = 0 - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime permission" + - " that used to require user review, the user review requirement should be removed" + - " if it's upgraded to post M. The actual permission flags $actualFlags should" + - " match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_legacyGrantedPermissionsAlreadyReviewedForPostM_getsGranted() { - val oldFlags = PermissionFlags.LEGACY_GRANTED - testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime permission" + - " that was already reviewed by the user, the permission should be RUNTIME_GRANTED" + - " if it's upgraded to post M. The actual permission flags $actualFlags should" + - " match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_leanbackNotificationPermissionsForPostM_getsImplicitGranted() { - val oldFlags = 0 - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_DANGEROUS, - permissionName = PERMISSION_POST_NOTIFICATIONS, - isNewInstall = true - ) { - oldState.mutateExternalState().setLeanback(true) - } - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_POST_NOTIFICATIONS) - val expectedNewFlags = PermissionFlags.IMPLICIT_GRANTED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime notification" + - " permission when isLeanback, the actual permission flags $actualFlags should" + - " match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_implicitSourceFromNonRuntime_getsImplicitGranted() { - val oldFlags = 0 - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_DANGEROUS, - implicitPermissions = setOf(PERMISSION_NAME_0), - isNewInstall = true - ) { - oldState.mutateExternalState().setImplicitToSourcePermissions( - MutableIndexedMap<String, IndexedListSet<String>>().apply { - put(PERMISSION_NAME_0, MutableIndexedListSet<String>().apply { - add(PERMISSION_NAME_1) - }) - } - ) - addPermission(mockParsedPermission(PERMISSION_NAME_1, PACKAGE_NAME_0)) - } - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime implicit" + - " permission that's source from a non-runtime permission, the actual permission" + - " flags $actualFlags should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - /** - * For a legacy granted or implicit permission during the app upgrade, when the permission - * should no longer be legacy or implicit granted, we want to remove the APP_OP_REVOKED flag - * so that the app can request the permission. - */ - @Test - fun testOnPackageAdded_noLongerLegacyOrImplicitGranted_canBeRequested() { - val oldFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.APP_OP_REVOKED or - PermissionFlags.RUNTIME_GRANTED - testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = 0 - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime permission" + - " that is no longer LEGACY_GRANTED or IMPLICIT_GRANTED, the actual permission" + - " flags $actualFlags should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_noLongerImplicitPermissions_getsRuntimeAndImplicitFlagsRemoved() { - val oldFlags = PermissionFlags.IMPLICIT or PermissionFlags.RUNTIME_GRANTED or - PermissionFlags.USER_SET or PermissionFlags.USER_FIXED - testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = 0 - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime permission" + - " that is no longer implicit and we shouldn't retain as nearby device" + - " permissions, the actual permission flags $actualFlags should match the expected" + - " flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_noLongerImplicitNearbyPermissionsWasGranted_getsRuntimeGranted() { - val oldFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_DANGEROUS, - permissionName = PERMISSION_BLUETOOTH_CONNECT, - requestedPermissions = setOf( - PERMISSION_BLUETOOTH_CONNECT, - PERMISSION_ACCESS_BACKGROUND_LOCATION - ) - ) { - setPermissionFlags( - APP_ID_1, - USER_ID_0, - PERMISSION_ACCESS_BACKGROUND_LOCATION, - PermissionFlags.RUNTIME_GRANTED - ) - } - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_BLUETOOTH_CONNECT) - val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime nearby device" + - " permission that was granted by implicit, the actual permission flags" + - " $actualFlags should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_noLongerImplicitSystemOrPolicyFixedWasGranted_getsRuntimeGranted() { - val oldFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT or - PermissionFlags.SYSTEM_FIXED - testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.SYSTEM_FIXED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime permission" + - " that was granted and is no longer implicit and is SYSTEM_FIXED or POLICY_FIXED," + - " the actual permission flags $actualFlags should match the expected" + - " flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_restrictedPermissionsNotExempt_getsRestrictionFlags() { - val oldFlags = PermissionFlags.RESTRICTION_REVOKED - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_DANGEROUS, - permissionInfoFlags = PermissionInfo.FLAG_HARD_RESTRICTED - ) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = oldFlags - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime hard" + - " restricted permission that is not exempted, the actual permission flags" + - " $actualFlags should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_restrictedPermissionsIsExempted_clearsRestrictionFlags() { - val oldFlags = 0 - testEvaluatePermissionState( - oldFlags, - PermissionInfo.PROTECTION_DANGEROUS, - permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED - ) {} - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.UPGRADE_EXEMPT - assertWithMessage( - "After onPackageAdded() is called for a package that requests a runtime soft" + - " restricted permission that is exempted, the actual permission flags" + - " $actualFlags should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_runtimeExistingImplicitPermissions_sourceFlagsNotInherited() { - val oldImplicitPermissionFlags = PermissionFlags.USER_FIXED - testInheritImplicitPermissionStates( - implicitPermissionFlags = oldImplicitPermissionFlags, - isNewInstallAndNewPermission = false - ) - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = oldImplicitPermissionFlags or PermissionFlags.IMPLICIT_GRANTED or - PermissionFlags.APP_OP_REVOKED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a permission that is" + - " implicit, existing and runtime, it should not inherit the runtime flags from" + - " the source permission. Hence the actual permission flags $actualFlags should" + - " match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_nonRuntimeNewImplicitPermissions_sourceFlagsNotInherited() { - testInheritImplicitPermissionStates( - implicitPermissionProtectionLevel = PermissionInfo.PROTECTION_NORMAL - ) - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = PermissionFlags.INSTALL_GRANTED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a permission that is" + - " implicit, new and non-runtime, it should not inherit the runtime flags from" + - " the source permission. Hence the actual permission flags $actualFlags should" + - " match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_runtimeNewImplicitPermissions_sourceFlagsInherited() { - val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET - testInheritImplicitPermissionStates(sourceRuntimeFlags = sourceRuntimeFlags) - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED or - PermissionFlags.IMPLICIT - assertWithMessage( - "After onPackageAdded() is called for a package that requests a permission that is" + - " implicit, new and runtime, it should inherit the runtime flags from" + - " the source permission. Hence the actual permission flags $actualFlags should" + - " match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - @Test - fun testOnPackageAdded_grantingNewFromRevokeImplicitPermissions_onlySourceFlagsInherited() { - val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET - testInheritImplicitPermissionStates( - implicitPermissionFlags = PermissionFlags.POLICY_FIXED, - sourceRuntimeFlags = sourceRuntimeFlags, - isAnySourcePermissionNonRuntime = false - ) - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) - val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT - assertWithMessage( - "After onPackageAdded() is called for a package that requests a permission that is" + - " implicit, existing, runtime and revoked, it should only inherit runtime flags" + - " from source permission. Hence the actual permission flags $actualFlags should" + - " match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - /** - * If it's a media implicit permission (one of RETAIN_IMPLICIT_FLAGS_PERMISSIONS), we want to - * remove the IMPLICIT flag so that they will be granted when they are no longer implicit. - * (instead of revoking it) - */ - @Test - fun testOnPackageAdded_mediaImplicitPermissions_getsImplicitFlagRemoved() { - val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET - testInheritImplicitPermissionStates( - implicitPermissionName = PERMISSION_ACCESS_MEDIA_LOCATION, - sourceRuntimeFlags = sourceRuntimeFlags - ) - - val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_ACCESS_MEDIA_LOCATION) - val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED - assertWithMessage( - "After onPackageAdded() is called for a package that requests a media permission that" + - " is implicit, new and runtime, it should inherit the runtime flags from" + - " the source permission and have the IMPLICIT flag removed. Hence the actual" + - " permission flags $actualFlags should match the expected flags $expectedNewFlags" - ) - .that(actualFlags) - .isEqualTo(expectedNewFlags) - } - - private fun testInheritImplicitPermissionStates( - implicitPermissionName: String = PERMISSION_NAME_0, - implicitPermissionFlags: Int = 0, - implicitPermissionProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS, - sourceRuntimeFlags: Int = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET, - isAnySourcePermissionNonRuntime: Boolean = true, - isNewInstallAndNewPermission: Boolean = true - ) { - val implicitPermission = mockParsedPermission( - implicitPermissionName, - PACKAGE_NAME_0, - protectionLevel = implicitPermissionProtectionLevel, - ) - // For source from non-runtime in order to grant by implicit - val sourcePermission1 = mockParsedPermission( - PERMISSION_NAME_1, - PACKAGE_NAME_0, - protectionLevel = if (isAnySourcePermissionNonRuntime) { - PermissionInfo.PROTECTION_NORMAL - } else { - PermissionInfo.PROTECTION_DANGEROUS - } - ) - // For inheriting runtime flags - val sourcePermission2 = mockParsedPermission( - PERMISSION_NAME_2, - PACKAGE_NAME_0, - protectionLevel = PermissionInfo.PROTECTION_DANGEROUS, - ) - val permissionOwnerPackageState = mockPackageState( - APP_ID_0, - mockAndroidPackage( - PACKAGE_NAME_0, - permissions = listOf(implicitPermission, sourcePermission1, sourcePermission2) - ) - ) - val installedPackageState = mockPackageState( - APP_ID_1, - mockAndroidPackage( - PACKAGE_NAME_1, - requestedPermissions = setOf( - implicitPermissionName, - PERMISSION_NAME_1, - PERMISSION_NAME_2 - ), - implicitPermissions = setOf(implicitPermissionName) - ) - ) - oldState.mutateExternalState().setImplicitToSourcePermissions( - MutableIndexedMap<String, IndexedListSet<String>>().apply { - put(implicitPermissionName, MutableIndexedListSet<String>().apply { - add(PERMISSION_NAME_1) - add(PERMISSION_NAME_2) - }) - } - ) - addPackageState(permissionOwnerPackageState) - addPermission(implicitPermission) - addPermission(sourcePermission1) - addPermission(sourcePermission2) - if (!isNewInstallAndNewPermission) { - addPackageState(installedPackageState) - setPermissionFlags(APP_ID_1, USER_ID_0, implicitPermissionName, implicitPermissionFlags) - } - setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_2, sourceRuntimeFlags) - - mutateState { - if (isNewInstallAndNewPermission) { - addPackageState(installedPackageState) - setPermissionFlags( - APP_ID_1, - USER_ID_0, - implicitPermissionName, - implicitPermissionFlags, - newState - ) - } - with(appIdPermissionPolicy) { - onPackageAdded(installedPackageState) - } - } - } - - /** - * Setup simple package states for testing evaluatePermissionState(). - * permissionOwnerPackageState is definer of permissionName with APP_ID_0. - * installedPackageState is the installed package that requests permissionName with APP_ID_1. - * - * @param oldFlags the existing permission flags for APP_ID_1, USER_ID_0, permissionName - * @param protectionLevel the protectionLevel for the permission - * @param permissionName the name of the permission (1) being defined (2) of the oldFlags, and - * (3) requested by installedPackageState - * @param requestedPermissions the permissions requested by installedPackageState - * @param implicitPermissions the implicit permissions of installedPackageState - * @param permissionInfoFlags the flags for the permission itself - * @param isInstalledPackageSystem whether installedPackageState is a system package - * - * @return installedPackageState - */ - fun testEvaluatePermissionState( - oldFlags: Int, - protectionLevel: Int, - permissionName: String = PERMISSION_NAME_0, - requestedPermissions: Set<String> = setOf(permissionName), - implicitPermissions: Set<String> = emptySet(), - permissionInfoFlags: Int = 0, - isInstalledPackageSystem: Boolean = false, - isInstalledPackagePrivileged: Boolean = false, - isInstalledPackageProduct: Boolean = false, - isInstalledPackageSignatureMatching: Boolean = false, - isInstalledPackageVendor: Boolean = false, - installedPackageTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, - isNewInstall: Boolean = false, - additionalSetup: () -> Unit - ) { - val parsedPermission = mockParsedPermission( - permissionName, - PACKAGE_NAME_0, - protectionLevel = protectionLevel, - flags = permissionInfoFlags - ) - val permissionOwnerPackageState = mockPackageState( - APP_ID_0, - mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission)) - ) - val installedPackageState = mockPackageState( - APP_ID_1, - mockAndroidPackage( - PACKAGE_NAME_1, - requestedPermissions = requestedPermissions, - implicitPermissions = implicitPermissions, - targetSdkVersion = installedPackageTargetSdkVersion, - isSignatureMatching = isInstalledPackageSignatureMatching - ), - isSystem = isInstalledPackageSystem, - isPrivileged = isInstalledPackagePrivileged, - isProduct = isInstalledPackageProduct, - isVendor = isInstalledPackageVendor - ) - addPackageState(permissionOwnerPackageState) - if (!isNewInstall) { - addPackageState(installedPackageState) - setPermissionFlags(APP_ID_1, USER_ID_0, permissionName, oldFlags) - } - addPermission(parsedPermission) - - additionalSetup() - - mutateState { - if (isNewInstall) { - addPackageState(installedPackageState, newState) - setPermissionFlags(APP_ID_1, USER_ID_0, permissionName, oldFlags, newState) - } - with(appIdPermissionPolicy) { - onPackageAdded(installedPackageState) - } - } - } - - /** - * Mock an AndroidPackage with PACKAGE_NAME_0, PERMISSION_NAME_0 and PERMISSION_GROUP_NAME_0 - */ - private fun mockSimpleAndroidPackage(): AndroidPackage = - mockAndroidPackage( - PACKAGE_NAME_0, - permissionGroups = listOf(defaultPermissionGroup), - permissions = listOf(defaultPermissionTree, defaultPermission) - ) - - private inline fun mutateState(action: MutateStateScope.() -> Unit) { - newState = oldState.toMutable() - MutateStateScope(oldState, newState).action() - } - - private fun mockPackageState( - appId: Int, - packageName: String, - isSystem: Boolean = false, - ): PackageState = - mock { - whenever(this.appId).thenReturn(appId) - whenever(this.packageName).thenReturn(packageName) - whenever(androidPackage).thenReturn(null) - whenever(this.isSystem).thenReturn(isSystem) - } - - private fun mockPackageState( - appId: Int, - androidPackage: AndroidPackage, - isSystem: Boolean = false, - isPrivileged: Boolean = false, - isProduct: Boolean = false, - isInstantApp: Boolean = false, - isVendor: Boolean = false - ): PackageState = - mock { - whenever(this.appId).thenReturn(appId) - whenever(this.androidPackage).thenReturn(androidPackage) - val packageName = androidPackage.packageName - whenever(this.packageName).thenReturn(packageName) - whenever(this.isSystem).thenReturn(isSystem) - whenever(this.isPrivileged).thenReturn(isPrivileged) - whenever(this.isProduct).thenReturn(isProduct) - whenever(this.isVendor).thenReturn(isVendor) - val userStates = SparseArray<PackageUserState>().apply { - put(USER_ID_0, mock { whenever(this.isInstantApp).thenReturn(isInstantApp) }) - } - whenever(this.userStates).thenReturn(userStates) - } - - private fun mockAndroidPackage( - packageName: String, - targetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, - isRequestLegacyExternalStorage: Boolean = false, - adoptPermissions: List<String> = emptyList(), - implicitPermissions: Set<String> = emptySet(), - requestedPermissions: Set<String> = emptySet(), - permissionGroups: List<ParsedPermissionGroup> = emptyList(), - permissions: List<ParsedPermission> = emptyList(), - isSignatureMatching: Boolean = false - ): AndroidPackage = - mock { - whenever(this.packageName).thenReturn(packageName) - whenever(this.targetSdkVersion).thenReturn(targetSdkVersion) - whenever(this.isRequestLegacyExternalStorage).thenReturn(isRequestLegacyExternalStorage) - whenever(this.adoptPermissions).thenReturn(adoptPermissions) - whenever(this.implicitPermissions).thenReturn(implicitPermissions) - whenever(this.requestedPermissions).thenReturn(requestedPermissions) - whenever(this.permissionGroups).thenReturn(permissionGroups) - whenever(this.permissions).thenReturn(permissions) - val signingDetails = mock<SigningDetails> { - whenever( - hasCommonSignerWithCapability(any(), any()) - ).thenReturn(isSignatureMatching) - whenever(hasAncestorOrSelf(any())).thenReturn(isSignatureMatching) - whenever( - checkCapability(any<SigningDetails>(), any()) - ).thenReturn(isSignatureMatching) - } - whenever(this.signingDetails).thenReturn(signingDetails) - } - - private fun mockParsedPermission( - permissionName: String, - packageName: String, - backgroundPermission: String? = null, - group: String? = null, - protectionLevel: Int = PermissionInfo.PROTECTION_NORMAL, - flags: Int = 0, - isTree: Boolean = false - ): ParsedPermission = - mock { - whenever(name).thenReturn(permissionName) - whenever(this.packageName).thenReturn(packageName) - whenever(metaData).thenReturn(Bundle()) - whenever(this.backgroundPermission).thenReturn(backgroundPermission) - whenever(this.group).thenReturn(group) - whenever(this.protectionLevel).thenReturn(protectionLevel) - whenever(this.flags).thenReturn(flags) - whenever(this.isTree).thenReturn(isTree) - } - - private fun mockParsedPermissionGroup( - permissionGroupName: String, - packageName: String, - ): ParsedPermissionGroup = - mock { - whenever(name).thenReturn(permissionGroupName) - whenever(this.packageName).thenReturn(packageName) - whenever(metaData).thenReturn(Bundle()) - } - - private fun addPackageState(packageState: PackageState, state: MutableAccessState = oldState) { - state.mutateExternalState().apply { - setPackageStates( - packageStates.toMutableMap().apply { - put(packageState.packageName, packageState) - } - ) - mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() } - .add(packageState.packageName) - } - } - - private fun addDisabledSystemPackageState( - packageState: PackageState, - state: MutableAccessState = oldState - ) = state.mutateExternalState().apply { - (disabledSystemPackageStates as ArrayMap)[packageState.packageName] = packageState - } - - private fun addPermission( - parsedPermission: ParsedPermission, - type: Int = Permission.TYPE_MANIFEST, - isReconciled: Boolean = true, - state: MutableAccessState = oldState - ) { - val permissionInfo = PackageInfoUtils.generatePermissionInfo( - parsedPermission, - PackageManager.GET_META_DATA.toLong() - )!! - val appId = state.externalState.packageStates[permissionInfo.packageName]!!.appId - val permission = Permission(permissionInfo, isReconciled, type, appId) - if (parsedPermission.isTree) { - state.mutateSystemState().mutatePermissionTrees()[permission.name] = permission - } else { - state.mutateSystemState().mutatePermissions()[permission.name] = permission - } - } - - private fun addPermissionGroup( - parsedPermissionGroup: ParsedPermissionGroup, - state: MutableAccessState = oldState - ) { - state.mutateSystemState().mutatePermissionGroups()[parsedPermissionGroup.name] = - PackageInfoUtils.generatePermissionGroupInfo( - parsedPermissionGroup, - PackageManager.GET_META_DATA.toLong() - )!! - } - - private fun getPermission( - permissionName: String, - state: MutableAccessState = newState - ): Permission? = state.systemState.permissions[permissionName] - - private fun getPermissionTree( - permissionTreeName: String, - state: MutableAccessState = newState - ): Permission? = state.systemState.permissionTrees[permissionTreeName] - - private fun getPermissionGroup( - permissionGroupName: String, - state: MutableAccessState = newState - ): PermissionGroupInfo? = state.systemState.permissionGroups[permissionGroupName] - - private fun getPermissionFlags( - appId: Int, - userId: Int, - permissionName: String, - state: MutableAccessState = newState - ): Int = - state.userStates[userId]?.appIdPermissionFlags?.get(appId).getWithDefault(permissionName, 0) - - private fun setPermissionFlags( - appId: Int, - userId: Int, - permissionName: String, - flags: Int, - state: MutableAccessState = oldState - ) = - state.mutateUserState(userId)!!.mutateAppIdPermissionFlags().mutateOrPut(appId) { - MutableIndexedMap() - }.put(permissionName, flags) - - companion object { - private const val PACKAGE_NAME_0 = "packageName0" - private const val PACKAGE_NAME_1 = "packageName1" - private const val MISSING_ANDROID_PACKAGE = "missingAndroidPackage" - private const val PLATFORM_PACKAGE_NAME = "android" - - private const val APP_ID_0 = 0 - private const val APP_ID_1 = 1 - private const val PLATFORM_APP_ID = 2 - - private const val PERMISSION_GROUP_NAME_0 = "permissionGroupName0" - private const val PERMISSION_GROUP_NAME_1 = "permissionGroupName1" - - private const val PERMISSION_TREE_NAME = "permissionTree" - - private const val PERMISSION_NAME_0 = "permissionName0" - private const val PERMISSION_NAME_1 = "permissionName1" - private const val PERMISSION_NAME_2 = "permissionName2" - private const val PERMISSION_READ_EXTERNAL_STORAGE = - Manifest.permission.READ_EXTERNAL_STORAGE - private const val PERMISSION_POST_NOTIFICATIONS = - Manifest.permission.POST_NOTIFICATIONS - private const val PERMISSION_BLUETOOTH_CONNECT = - Manifest.permission.BLUETOOTH_CONNECT - private const val PERMISSION_ACCESS_BACKGROUND_LOCATION = - Manifest.permission.ACCESS_BACKGROUND_LOCATION - private const val PERMISSION_ACCESS_MEDIA_LOCATION = - Manifest.permission.ACCESS_MEDIA_LOCATION - - private const val USER_ID_0 = 0 - } -} diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt new file mode 100644 index 000000000000..7966c5c4d961 --- /dev/null +++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt @@ -0,0 +1,446 @@ +/* + * 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.permission.test + +import android.Manifest +import android.content.pm.PackageManager +import android.content.pm.PermissionGroupInfo +import android.content.pm.PermissionInfo +import android.content.pm.SigningDetails +import android.os.Build +import android.os.Bundle +import android.util.ArrayMap +import android.util.SparseArray +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.modules.utils.testing.ExtendedMockitoRule +import com.android.server.extendedtestutils.wheneverStatic +import com.android.server.permission.access.MutableAccessState +import com.android.server.permission.access.MutableUserState +import com.android.server.permission.access.MutateStateScope +import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports +import com.android.server.permission.access.permission.AppIdPermissionPolicy +import com.android.server.permission.access.permission.Permission +import com.android.server.permission.access.permission.PermissionFlags +import com.android.server.permission.access.util.hasBits +import com.android.server.pm.parsing.PackageInfoUtils +import com.android.server.pm.pkg.AndroidPackage +import com.android.server.pm.pkg.PackageState +import com.android.server.pm.pkg.PackageUserState +import com.android.server.pm.pkg.component.ParsedPermission +import com.android.server.pm.pkg.component.ParsedPermissionGroup +import com.android.server.testutils.any +import com.android.server.testutils.mock +import com.android.server.testutils.whenever +import com.google.common.truth.Truth.assertWithMessage +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyLong + +/** + * Mocking unit test for AppIdPermissionPolicy. + */ +@RunWith(AndroidJUnit4::class) +open class BaseAppIdPermissionPolicyTest { + protected lateinit var oldState: MutableAccessState + protected lateinit var newState: MutableAccessState + + protected val defaultPermissionGroup = mockParsedPermissionGroup( + PERMISSION_GROUP_NAME_0, + PACKAGE_NAME_0 + ) + protected val defaultPermissionTree = mockParsedPermission( + PERMISSION_TREE_NAME, + PACKAGE_NAME_0, + isTree = true + ) + protected val defaultPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0) + + protected val appIdPermissionPolicy = AppIdPermissionPolicy() + + @Rule + @JvmField + val extendedMockitoRule = ExtendedMockitoRule.Builder(this) + .spyStatic(PackageInfoUtils::class.java) + .build() + + @Before + open fun setUp() { + oldState = MutableAccessState() + createUserState(USER_ID_0) + oldState.mutateExternalState().setPackageStates(ArrayMap()) + oldState.mutateExternalState().setDisabledSystemPackageStates(ArrayMap()) + mockPackageInfoUtilsGeneratePermissionInfo() + mockPackageInfoUtilsGeneratePermissionGroupInfo() + } + + protected fun createUserState(userId: Int) { + oldState.mutateExternalState().mutateUserIds().add(userId) + oldState.mutateUserStatesNoWrite().put(userId, MutableUserState()) + } + + private fun mockPackageInfoUtilsGeneratePermissionInfo() { + wheneverStatic { + PackageInfoUtils.generatePermissionInfo(any(ParsedPermission::class.java), anyLong()) + }.thenAnswer { invocation -> + val parsedPermission = invocation.getArgument<ParsedPermission>(0) + val generateFlags = invocation.getArgument<Long>(1) + PermissionInfo(parsedPermission.backgroundPermission).apply { + name = parsedPermission.name + packageName = parsedPermission.packageName + metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) { + parsedPermission.metaData + } else { + null + } + @Suppress("DEPRECATION") + protectionLevel = parsedPermission.protectionLevel + group = parsedPermission.group + flags = parsedPermission.flags + } + } + } + + private fun mockPackageInfoUtilsGeneratePermissionGroupInfo() { + wheneverStatic { + PackageInfoUtils.generatePermissionGroupInfo( + any(ParsedPermissionGroup::class.java), + anyLong() + ) + }.thenAnswer { invocation -> + val parsedPermissionGroup = invocation.getArgument<ParsedPermissionGroup>(0) + val generateFlags = invocation.getArgument<Long>(1) + @Suppress("DEPRECATION") + PermissionGroupInfo().apply { + name = parsedPermissionGroup.name + packageName = parsedPermissionGroup.packageName + metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) { + parsedPermissionGroup.metaData + } else { + null + } + flags = parsedPermissionGroup.flags + } + } + } + + @Test + fun testOnAppIdRemoved_appIdIsRemoved_permissionFlagsCleared() { + val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0) + val permissionOwnerPackageState = mockPackageState( + APP_ID_0, + mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission)) + ) + val requestingPackageState = mockPackageState( + APP_ID_1, + mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0)) + ) + addPackageState(permissionOwnerPackageState) + addPackageState(requestingPackageState) + addPermission(parsedPermission) + setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.INSTALL_GRANTED) + + mutateState { + with(appIdPermissionPolicy) { + onAppIdRemoved(APP_ID_1) + } + } + + val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0) + val expectedNewFlags = 0 + assertWithMessage( + "After onAppIdRemoved() is called for appId $APP_ID_1 that requests a permission" + + " owns by appId $APP_ID_0 with existing permission flags. The actual permission" + + " flags $actualFlags should be null" + ) + .that(actualFlags) + .isEqualTo(expectedNewFlags) + } + + @Test + fun testOnPackageRemoved_packageIsRemoved_permissionsAreTrimmedAndStatesAreEvaluated() { + // TODO + // shouldn't reuse test cases because it's really different despite it's also for + // trim permission states. It's different because it's package removal + } + + @Test + fun testOnPackageInstalled_nonSystemAppIsInstalled_upgradeExemptFlagIsCleared() { + // TODO + // should be fine for it to be its own test cases and not to re-use + // clearRestrictedPermissionImplicitExemption + } + + @Test + fun testOnPackageInstalled_systemAppIsInstalled_upgradeExemptFlagIsRetained() { + // TODO + } + + @Test + fun testOnPackageInstalled_requestedPermissionAlsoRequestedBySystemApp_exemptFlagIsRetained() { + // TODO + } + + @Test + fun testOnPackageInstalled_restrictedPermissionsNotExempt_getsRestrictionFlags() { + // TODO + } + + @Test + fun testOnPackageInstalled_restrictedPermissionsIsExempted_clearsRestrictionFlags() { + // TODO + } + + @Test + fun testOnStateMutated_notEmpty_isCalledForEachListener() { + // TODO + } + + /** + * Mock an AndroidPackage with PACKAGE_NAME_0, PERMISSION_NAME_0 and PERMISSION_GROUP_NAME_0 + */ + protected fun mockSimpleAndroidPackage(): AndroidPackage = + mockAndroidPackage( + PACKAGE_NAME_0, + permissionGroups = listOf(defaultPermissionGroup), + permissions = listOf(defaultPermissionTree, defaultPermission) + ) + + protected inline fun mutateState(action: MutateStateScope.() -> Unit) { + newState = oldState.toMutable() + MutateStateScope(oldState, newState).action() + } + + protected fun mockPackageState( + appId: Int, + packageName: String, + isSystem: Boolean = false, + ): PackageState = + mock { + whenever(this.appId).thenReturn(appId) + whenever(this.packageName).thenReturn(packageName) + whenever(androidPackage).thenReturn(null) + whenever(this.isSystem).thenReturn(isSystem) + } + + protected fun mockPackageState( + appId: Int, + androidPackage: AndroidPackage, + isSystem: Boolean = false, + isPrivileged: Boolean = false, + isProduct: Boolean = false, + isInstantApp: Boolean = false, + isVendor: Boolean = false + ): PackageState = + mock { + whenever(this.appId).thenReturn(appId) + whenever(this.androidPackage).thenReturn(androidPackage) + val packageName = androidPackage.packageName + whenever(this.packageName).thenReturn(packageName) + whenever(this.isSystem).thenReturn(isSystem) + whenever(this.isPrivileged).thenReturn(isPrivileged) + whenever(this.isProduct).thenReturn(isProduct) + whenever(this.isVendor).thenReturn(isVendor) + val userStates = SparseArray<PackageUserState>().apply { + put(USER_ID_0, mock { whenever(this.isInstantApp).thenReturn(isInstantApp) }) + } + whenever(this.userStates).thenReturn(userStates) + } + + protected fun mockAndroidPackage( + packageName: String, + targetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, + isRequestLegacyExternalStorage: Boolean = false, + adoptPermissions: List<String> = emptyList(), + implicitPermissions: Set<String> = emptySet(), + requestedPermissions: Set<String> = emptySet(), + permissionGroups: List<ParsedPermissionGroup> = emptyList(), + permissions: List<ParsedPermission> = emptyList(), + isSignatureMatching: Boolean = false + ): AndroidPackage = + mock { + whenever(this.packageName).thenReturn(packageName) + whenever(this.targetSdkVersion).thenReturn(targetSdkVersion) + whenever(this.isRequestLegacyExternalStorage).thenReturn(isRequestLegacyExternalStorage) + whenever(this.adoptPermissions).thenReturn(adoptPermissions) + whenever(this.implicitPermissions).thenReturn(implicitPermissions) + whenever(this.requestedPermissions).thenReturn(requestedPermissions) + whenever(this.permissionGroups).thenReturn(permissionGroups) + whenever(this.permissions).thenReturn(permissions) + val signingDetails = mock<SigningDetails> { + whenever( + hasCommonSignerWithCapability(any(), any()) + ).thenReturn(isSignatureMatching) + whenever(hasAncestorOrSelf(any())).thenReturn(isSignatureMatching) + whenever( + checkCapability(any<SigningDetails>(), any()) + ).thenReturn(isSignatureMatching) + } + whenever(this.signingDetails).thenReturn(signingDetails) + } + + protected fun mockParsedPermission( + permissionName: String, + packageName: String, + backgroundPermission: String? = null, + group: String? = null, + protectionLevel: Int = PermissionInfo.PROTECTION_NORMAL, + flags: Int = 0, + isTree: Boolean = false + ): ParsedPermission = + mock { + whenever(name).thenReturn(permissionName) + whenever(this.packageName).thenReturn(packageName) + whenever(metaData).thenReturn(Bundle()) + whenever(this.backgroundPermission).thenReturn(backgroundPermission) + whenever(this.group).thenReturn(group) + whenever(this.protectionLevel).thenReturn(protectionLevel) + whenever(this.flags).thenReturn(flags) + whenever(this.isTree).thenReturn(isTree) + } + + protected fun mockParsedPermissionGroup( + permissionGroupName: String, + packageName: String, + ): ParsedPermissionGroup = + mock { + whenever(name).thenReturn(permissionGroupName) + whenever(this.packageName).thenReturn(packageName) + whenever(metaData).thenReturn(Bundle()) + } + + protected fun addPackageState( + packageState: PackageState, + state: MutableAccessState = oldState + ) { + state.mutateExternalState().apply { + setPackageStates( + packageStates.toMutableMap().apply { + put(packageState.packageName, packageState) + } + ) + mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() } + .add(packageState.packageName) + } + } + + protected fun addDisabledSystemPackageState( + packageState: PackageState, + state: MutableAccessState = oldState + ) = state.mutateExternalState().apply { + (disabledSystemPackageStates as ArrayMap)[packageState.packageName] = packageState + } + + protected fun addPermission( + parsedPermission: ParsedPermission, + type: Int = Permission.TYPE_MANIFEST, + isReconciled: Boolean = true, + state: MutableAccessState = oldState + ) { + val permissionInfo = PackageInfoUtils.generatePermissionInfo( + parsedPermission, + PackageManager.GET_META_DATA.toLong() + )!! + val appId = state.externalState.packageStates[permissionInfo.packageName]!!.appId + val permission = Permission(permissionInfo, isReconciled, type, appId) + if (parsedPermission.isTree) { + state.mutateSystemState().mutatePermissionTrees()[permission.name] = permission + } else { + state.mutateSystemState().mutatePermissions()[permission.name] = permission + } + } + + protected fun addPermissionGroup( + parsedPermissionGroup: ParsedPermissionGroup, + state: MutableAccessState = oldState + ) { + state.mutateSystemState().mutatePermissionGroups()[parsedPermissionGroup.name] = + PackageInfoUtils.generatePermissionGroupInfo( + parsedPermissionGroup, + PackageManager.GET_META_DATA.toLong() + )!! + } + + protected fun getPermission( + permissionName: String, + state: MutableAccessState = newState + ): Permission? = state.systemState.permissions[permissionName] + + protected fun getPermissionTree( + permissionTreeName: String, + state: MutableAccessState = newState + ): Permission? = state.systemState.permissionTrees[permissionTreeName] + + protected fun getPermissionGroup( + permissionGroupName: String, + state: MutableAccessState = newState + ): PermissionGroupInfo? = state.systemState.permissionGroups[permissionGroupName] + + protected fun getPermissionFlags( + appId: Int, + userId: Int, + permissionName: String, + state: MutableAccessState = newState + ): Int = + state.userStates[userId]?.appIdPermissionFlags?.get(appId).getWithDefault(permissionName, 0) + + protected fun setPermissionFlags( + appId: Int, + userId: Int, + permissionName: String, + flags: Int, + state: MutableAccessState = oldState + ) = + state.mutateUserState(userId)!!.mutateAppIdPermissionFlags().mutateOrPut(appId) { + MutableIndexedMap() + }.put(permissionName, flags) + + companion object { + @JvmStatic protected val PACKAGE_NAME_0 = "packageName0" + @JvmStatic protected val PACKAGE_NAME_1 = "packageName1" + @JvmStatic protected val PACKAGE_NAME_2 = "packageName2" + @JvmStatic protected val MISSING_ANDROID_PACKAGE = "missingAndroidPackage" + @JvmStatic protected val PLATFORM_PACKAGE_NAME = "android" + + @JvmStatic protected val APP_ID_0 = 0 + @JvmStatic protected val APP_ID_1 = 1 + @JvmStatic protected val PLATFORM_APP_ID = 2 + + @JvmStatic protected val PERMISSION_GROUP_NAME_0 = "permissionGroupName0" + @JvmStatic protected val PERMISSION_GROUP_NAME_1 = "permissionGroupName1" + + @JvmStatic protected val PERMISSION_TREE_NAME = "permissionTree" + + @JvmStatic protected val PERMISSION_NAME_0 = "permissionName0" + @JvmStatic protected val PERMISSION_NAME_1 = "permissionName1" + @JvmStatic protected val PERMISSION_NAME_2 = "permissionName2" + @JvmStatic protected val PERMISSION_READ_EXTERNAL_STORAGE = + Manifest.permission.READ_EXTERNAL_STORAGE + @JvmStatic protected val PERMISSION_POST_NOTIFICATIONS = + Manifest.permission.POST_NOTIFICATIONS + @JvmStatic protected val PERMISSION_BLUETOOTH_CONNECT = + Manifest.permission.BLUETOOTH_CONNECT + @JvmStatic protected val PERMISSION_ACCESS_BACKGROUND_LOCATION = + Manifest.permission.ACCESS_BACKGROUND_LOCATION + @JvmStatic protected val PERMISSION_ACCESS_MEDIA_LOCATION = + Manifest.permission.ACCESS_MEDIA_LOCATION + + @JvmStatic protected val USER_ID_0 = 0 + @JvmStatic protected val USER_ID_NEW = 1 + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java index 9174899dfe6c..a56b59a4a481 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java @@ -1255,21 +1255,6 @@ public final class DisplayPowerController2Test { } @Test - public void testPowerStateStopsOnDpcStop() { - // Set up - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); - - // Stop dpc - mHolder.dpc.stop(); - advanceTime(1); - - // Ensure dps has stopped - verify(mHolder.displayPowerState, times(1)).stop(); - } - - @Test public void testRampRateForHdrContent_HdrClamperOff() { float hdrBrightness = 0.8f; float clampedBrightness = 0.6f; diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 412b65f5835a..05721174b71f 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -1191,21 +1191,6 @@ public final class DisplayPowerControllerTest { } @Test - public void testPowerStateStopsOnDpcStop() { - // Set up - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); - - // Stop dpc - mHolder.dpc.stop(); - advanceTime(1); - - // Ensure dps has stopped - verify(mHolder.displayPowerState, times(1)).stop(); - } - - @Test public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() { Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.java deleted file mode 100644 index 167a412d3860..000000000000 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.java +++ /dev/null @@ -1,64 +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.server.display; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; - -import static org.mockito.Mockito.times; - -import android.os.Handler; -import android.os.test.TestLooper; -import android.view.Display; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - - -@SmallTest -public class DisplayPowerStateTest { - private static final int DISPLAY_ID = 123; - - private DisplayPowerState mDisplayPowerState; - private TestLooper mTestLooper; - @Mock - private DisplayBlanker mDisplayBlankerMock; - @Mock - private ColorFade mColorFadeMock; - - @Rule - public final MockitoRule mMockitoRule = MockitoJUnit.rule(); - - @Before - public void setUp() { - mTestLooper = new TestLooper(); - mDisplayPowerState = new DisplayPowerState( - mDisplayBlankerMock, mColorFadeMock, DISPLAY_ID, Display.STATE_ON, - new Handler(mTestLooper.getLooper())); - } - - @Test - public void testColorFadeStopsOnDpsStop() { - mDisplayPowerState.stop(); - verify(mColorFadeMock, times(1)).stop(); - } -} diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index fbad369ab19e..b8c18e070397 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -91,6 +91,7 @@ import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.display.DisplayDeviceConfig; import com.android.server.display.TestUtils; +import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.mode.DisplayModeDirector.BrightnessObserver; import com.android.server.display.mode.DisplayModeDirector.DesiredDisplayModeSpecs; import com.android.server.sensors.SensorManagerInternal; @@ -110,7 +111,9 @@ import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -121,10 +124,114 @@ import junitparams.Parameters; @SmallTest @RunWith(JUnitParamsRunner.class) public class DisplayModeDirectorTest { - // The tolerance within which we consider something approximately equals. + public static Collection<Object[]> getAppRequestedSizeTestCases() { + var appRequestedSizeTestCases = Arrays.asList(new Object[][] { + {DEFAULT_MODE_75.getModeId(), Float.POSITIVE_INFINITY, + DEFAULT_MODE_75.getRefreshRate(), Map.of()}, + {APP_MODE_HIGH_90.getModeId(), Float.POSITIVE_INFINITY, + APP_MODE_HIGH_90.getRefreshRate(), + Map.of( + Vote.PRIORITY_APP_REQUEST_SIZE, + Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(), + APP_MODE_HIGH_90.getPhysicalHeight()), + Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()))}, + {LIMIT_MODE_70.getModeId(), Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, + Map.of( + Vote.PRIORITY_APP_REQUEST_SIZE, + Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(), + APP_MODE_HIGH_90.getPhysicalHeight()), + Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()), + Vote.PRIORITY_LOW_POWER_MODE, + Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(), + LIMIT_MODE_70.getPhysicalHeight()))}, + {LIMIT_MODE_70.getModeId(), LIMIT_MODE_70.getRefreshRate(), + LIMIT_MODE_70.getRefreshRate(), + Map.of( + Vote.PRIORITY_APP_REQUEST_SIZE, + Vote.forSize(APP_MODE_65.getPhysicalWidth(), + APP_MODE_65.getPhysicalHeight()), + Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()), + Vote.PRIORITY_LOW_POWER_MODE, + Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(), + LIMIT_MODE_70.getPhysicalHeight()))}, + {LIMIT_MODE_70.getModeId(), LIMIT_MODE_70.getRefreshRate(), + LIMIT_MODE_70.getRefreshRate(), + Map.of( + Vote.PRIORITY_APP_REQUEST_SIZE, + Vote.forSize(APP_MODE_65.getPhysicalWidth(), + APP_MODE_65.getPhysicalHeight()), + Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()), + Vote.PRIORITY_LOW_POWER_MODE, + Vote.forSizeAndPhysicalRefreshRatesRange( + 0, 0, + LIMIT_MODE_70.getPhysicalWidth(), + LIMIT_MODE_70.getPhysicalHeight(), + 0, Float.POSITIVE_INFINITY)), false}, + {APP_MODE_65.getModeId(), APP_MODE_65.getRefreshRate(), + APP_MODE_65.getRefreshRate(), + Map.of( + Vote.PRIORITY_APP_REQUEST_SIZE, + Vote.forSize(APP_MODE_65.getPhysicalWidth(), + APP_MODE_65.getPhysicalHeight()), + Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()), + Vote.PRIORITY_LOW_POWER_MODE, + Vote.forSizeAndPhysicalRefreshRatesRange( + 0, 0, + LIMIT_MODE_70.getPhysicalWidth(), + LIMIT_MODE_70.getPhysicalHeight(), + 0, Float.POSITIVE_INFINITY)), true}}); + + final var res = new ArrayList<Object[]>(appRequestedSizeTestCases.size() * 2); + + // Add additional argument for displayResolutionRangeVotingEnabled=false if not present. + for (var testCaseArrayArgs : appRequestedSizeTestCases) { + if (testCaseArrayArgs.length == 4) { + var testCaseListArgs = new ArrayList<>(Arrays.asList(testCaseArrayArgs)); + testCaseListArgs.add(/* displayResolutionRangeVotingEnabled */ false); + res.add(testCaseListArgs.toArray()); + } else { + res.add(testCaseArrayArgs); + } + } + + // Add additional argument for displayResolutionRangeVotingEnabled=true if not present. + for (var testCaseArrayArgs : appRequestedSizeTestCases) { + if (testCaseArrayArgs.length == 4) { + var testCaseListArgs = new ArrayList<>(Arrays.asList(testCaseArrayArgs)); + testCaseListArgs.add(/* displayResolutionRangeVotingEnabled */ true); + res.add(testCaseListArgs.toArray()); + } + } + + return res; + } + private static final String TAG = "DisplayModeDirectorTest"; private static final boolean DEBUG = false; private static final float FLOAT_TOLERANCE = 0.01f; + + private static final Display.Mode APP_MODE_65 = new Display.Mode( + /*modeId=*/65, /*width=*/1900, /*height=*/1900, 65); + private static final Display.Mode LIMIT_MODE_70 = new Display.Mode( + /*modeId=*/70, /*width=*/2000, /*height=*/2000, 70); + private static final Display.Mode DEFAULT_MODE_75 = new Display.Mode( + /*modeId=*/75, /*width=*/2500, /*height=*/2500, 75); + private static final Display.Mode APP_MODE_HIGH_90 = new Display.Mode( + /*modeId=*/90, /*width=*/3000, /*height=*/3000, 90); + private static final Display.Mode[] TEST_MODES = new Display.Mode[] { + new Display.Mode( + /*modeId=*/60, /*width=*/1900, /*height=*/1900, 60), + APP_MODE_65, + LIMIT_MODE_70, + DEFAULT_MODE_75, + APP_MODE_HIGH_90 + }; + private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY; private static final int MODE_ID = 1; private static final float TRANSITION_POINT = 0.763f; @@ -142,6 +249,8 @@ public class DisplayModeDirectorTest { public SensorManagerInternal mSensorManagerInternalMock; @Mock public DisplayManagerInternal mDisplayManagerInternalMock; + @Mock + private DisplayManagerFlags mDisplayManagerFlags; @Before public void setUp() throws Exception { @@ -177,7 +286,7 @@ public class DisplayModeDirectorTest { private DisplayModeDirector createDirectorFromModeArray(Display.Mode[] modes, Display.Mode defaultMode) { DisplayModeDirector director = - new DisplayModeDirector(mContext, mHandler, mInjector); + new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags); director.setLoggingEnabled(true); SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>(); supportedModesByDisplay.put(DISPLAY_ID, modes); @@ -219,9 +328,8 @@ public class DisplayModeDirectorTest { // should take precedence over lower priority votes. { int minFps = 60; - int maxFps = 90; - director = createDirectorFromFpsRange(60, 90); - assertTrue(2 * numPriorities < maxFps - minFps + 1); + int maxFps = minFps + 2 * numPriorities; + director = createDirectorFromFpsRange(minFps, maxFps); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); @@ -472,6 +580,7 @@ public class DisplayModeDirectorTest { assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90); } + /** Resolution range voting disabled */ @Test public void testAppRequestRefreshRateRange() { // Confirm that the app request range doesn't include flicker or min refresh rate settings, @@ -530,6 +639,33 @@ public class DisplayModeDirectorTest { assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f); } + /** Tests for app requested size */ + @Parameters(method = "getAppRequestedSizeTestCases") + @Test + public void testAppRequestedSize(final int expectedBaseModeId, + final float expectedPhysicalRefreshRate, + final float expectedAppRequestedRefreshRate, + final Map<Integer, Vote> votesWithPriorities, + final boolean displayResolutionRangeVotingEnabled) { + when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()) + .thenReturn(displayResolutionRangeVotingEnabled); + DisplayModeDirector director = createDirectorFromModeArray(TEST_MODES, DEFAULT_MODE_75); + + SparseArray<Vote> votes = new SparseArray<>(); + votesWithPriorities.forEach(votes::put); + + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(DISPLAY_ID, votes); + director.injectVotesByDisplay(votesByDisplay); + + var desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(expectedBaseModeId); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0); + assertThat(desiredSpecs.primary.physical.max).isAtLeast(expectedPhysicalRefreshRate); + assertThat(desiredSpecs.appRequest.physical.min).isAtMost(0); + assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(expectedAppRequestedRefreshRate); + } + void verifySpecsWithRefreshRateSettings(DisplayModeDirector director, float minFps, float peakFps, float defaultFps, RefreshRateRanges primary, RefreshRateRanges appRequest) { @@ -843,7 +979,7 @@ public class DisplayModeDirectorTest { @Test public void testStaleAppRequestSize() { DisplayModeDirector director = - new DisplayModeDirector(mContext, mHandler, mInjector); + new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags); Display.Mode[] modes = new Display.Mode[] { new Display.Mode(1, 1280, 720, 60), }; diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java new file mode 100644 index 000000000000..ff91d34470d4 --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java @@ -0,0 +1,446 @@ +/* + * 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.display.mode; + + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.Mode.INVALID_MODE_ID; + + +import static com.android.server.display.mode.DisplayModeDirector.SYNCHRONIZED_REFRESH_RATE_TOLERANCE; +import static com.android.server.display.mode.Vote.PRIORITY_LIMIT_MODE; +import static com.android.server.display.mode.Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE; +import static com.android.server.display.mode.Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE; +import static com.android.server.display.mode.VotesStorage.GLOBAL_ID; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.ContextWrapper; +import android.content.res.Resources; +import android.hardware.display.DisplayManager; +import android.os.Handler; +import android.os.Looper; +import android.provider.DeviceConfigInterface; +import android.view.Display; +import android.view.DisplayInfo; + +import androidx.annotation.Nullable; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.filters.SmallTest; + +import com.android.internal.R; +import com.android.server.display.feature.DisplayManagerFlags; +import com.android.server.sensors.SensorManagerInternal; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import junitparams.JUnitParamsRunner; + + +@SmallTest +@RunWith(JUnitParamsRunner.class) +public class DisplayObserverTest { + private static final int EXTERNAL_DISPLAY = 1; + private static final int MAX_WIDTH = 1920; + private static final int MAX_HEIGHT = 1080; + private static final int MAX_REFRESH_RATE = 60; + + private final Display.Mode[] mInternalDisplayModes = new Display.Mode[] { + new Display.Mode(/*modeId=*/ 0, MAX_WIDTH / 2, MAX_HEIGHT / 2, + (float) MAX_REFRESH_RATE / 2), + new Display.Mode(/*modeId=*/ 1, MAX_WIDTH / 2, MAX_HEIGHT / 2, + MAX_REFRESH_RATE), + new Display.Mode(/*modeId=*/ 2, MAX_WIDTH / 2, MAX_HEIGHT / 2, + MAX_REFRESH_RATE * 2), + new Display.Mode(/*modeId=*/ 3, MAX_WIDTH, MAX_HEIGHT, MAX_REFRESH_RATE * 2), + new Display.Mode(/*modeId=*/ 4, MAX_WIDTH, MAX_HEIGHT, MAX_REFRESH_RATE), + new Display.Mode(/*modeId=*/ 5, MAX_WIDTH * 2, MAX_HEIGHT * 2, + MAX_REFRESH_RATE), + new Display.Mode(/*modeId=*/ 6, MAX_WIDTH / 2, MAX_HEIGHT / 2, + MAX_REFRESH_RATE * 3), + }; + + private final Display.Mode[] mExternalDisplayModes = new Display.Mode[] { + new Display.Mode(/*modeId=*/ 0, MAX_WIDTH / 2, MAX_HEIGHT / 2, + (float) MAX_REFRESH_RATE / 2), + new Display.Mode(/*modeId=*/ 1, MAX_WIDTH / 2, MAX_HEIGHT / 2, + MAX_REFRESH_RATE), + new Display.Mode(/*modeId=*/ 2, MAX_WIDTH / 2, MAX_HEIGHT / 2, + MAX_REFRESH_RATE * 2), + new Display.Mode(/*modeId=*/ 3, MAX_WIDTH, MAX_HEIGHT, MAX_REFRESH_RATE * 2), + new Display.Mode(/*modeId=*/ 4, MAX_WIDTH, MAX_HEIGHT, MAX_REFRESH_RATE), + new Display.Mode(/*modeId=*/ 5, MAX_WIDTH * 2, MAX_HEIGHT * 2, + MAX_REFRESH_RATE), + }; + + private DisplayModeDirector mDmd; + private Context mContext; + private DisplayModeDirector.Injector mInjector; + private Handler mHandler; + private DisplayManager.DisplayListener mObserver; + private Resources mResources; + @Mock + private DisplayManagerFlags mDisplayManagerFlags; + private int mExternalDisplayUserPreferredModeId = INVALID_MODE_ID; + private int mInternalDisplayUserPreferredModeId = INVALID_MODE_ID; + private Display mDefaultDisplay; + private Display mExternalDisplay; + + /** Setup tests. */ + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mHandler = new Handler(Looper.getMainLooper()); + mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); + mResources = mock(Resources.class); + when(mContext.getResources()).thenReturn(mResources); + when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate)) + .thenReturn(0); + when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth)) + .thenReturn(0); + when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight)) + .thenReturn(0); + when(mResources.getBoolean(R.bool.config_refreshRateSynchronizationEnabled)) + .thenReturn(false); + + // Necessary configs to initialize DisplayModeDirector + when(mResources.getIntArray(R.array.config_brightnessThresholdsOfPeakRefreshRate)) + .thenReturn(new int[]{5}); + when(mResources.getIntArray(R.array.config_ambientThresholdsOfPeakRefreshRate)) + .thenReturn(new int[]{10}); + when(mResources.getIntArray( + R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate)) + .thenReturn(new int[]{250}); + when(mResources.getIntArray( + R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate)) + .thenReturn(new int[]{7000}); + } + + /** No vote for user preferred mode */ + @Test + public void testExternalDisplay_notVotedUserPreferredMode() { + var preferredMode = mExternalDisplayModes[5]; + mExternalDisplayUserPreferredModeId = preferredMode.getModeId(); + + init(); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + + // Testing that the vote is not added when display is added because feature is disabled + mObserver.onDisplayAdded(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + + // Testing that the vote is not present after display is removed + mObserver.onDisplayRemoved(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + + // Testing that the vote is not added when display is changed because feature is disabled + mObserver.onDisplayChanged(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + } + + /** Vote for user preferred mode */ + @Test + public void testExternalDisplay_voteUserPreferredMode() { + when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true); + var preferredMode = mExternalDisplayModes[5]; + mExternalDisplayUserPreferredModeId = preferredMode.getModeId(); + var expectedVote = Vote.forSize( + preferredMode.getPhysicalWidth(), + preferredMode.getPhysicalHeight()); + init(); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + mObserver.onDisplayAdded(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(expectedVote); + + mExternalDisplayUserPreferredModeId = INVALID_MODE_ID; + mObserver.onDisplayChanged(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + + preferredMode = mExternalDisplayModes[4]; + mExternalDisplayUserPreferredModeId = preferredMode.getModeId(); + expectedVote = Vote.forSize( + preferredMode.getPhysicalWidth(), + preferredMode.getPhysicalHeight()); + mObserver.onDisplayChanged(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(expectedVote); + + // Testing that the vote is removed. + mObserver.onDisplayRemoved(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + } + + /** External display: Do not apply limit to user preferred mode */ + @Test + public void testExternalDisplay_doNotApplyLimitToUserPreferredMode() { + when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true); + when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate)) + .thenReturn(MAX_REFRESH_RATE); + when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth)) + .thenReturn(MAX_WIDTH); + when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight)) + .thenReturn(MAX_HEIGHT); + + var preferredMode = mExternalDisplayModes[5]; + mExternalDisplayUserPreferredModeId = preferredMode.getModeId(); + var expectedResolutionVote = Vote.forSize(preferredMode.getPhysicalWidth(), + preferredMode.getPhysicalHeight()); + init(); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + mObserver.onDisplayAdded(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(expectedResolutionVote); + + // Testing that the vote is removed. + mObserver.onDisplayRemoved(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + } + + /** Default display: Do not apply limit to user preferred mode */ + @Test + public void testDefaultDisplayAdded_notAppliedLimitToUserPreferredMode() { + when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true); + when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate)) + .thenReturn(MAX_REFRESH_RATE); + when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth)) + .thenReturn(MAX_WIDTH); + when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight)) + .thenReturn(MAX_HEIGHT); + var preferredMode = mInternalDisplayModes[5]; + mInternalDisplayUserPreferredModeId = preferredMode.getModeId(); + var expectedResolutionVote = Vote.forSize(preferredMode.getPhysicalWidth(), + preferredMode.getPhysicalHeight()); + init(); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + mObserver.onDisplayAdded(DEFAULT_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(expectedResolutionVote); + mObserver.onDisplayRemoved(DEFAULT_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) + .isEqualTo(null); + } + + /** Default display added, no mode limit set */ + @Test + public void testDefaultDisplayAdded() { + when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true); + when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate)) + .thenReturn(MAX_REFRESH_RATE); + when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth)) + .thenReturn(MAX_WIDTH); + when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight)) + .thenReturn(MAX_HEIGHT); + init(); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + mObserver.onDisplayAdded(DEFAULT_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + } + + /** External display added, apply resolution refresh rate limit */ + @Test + public void testExternalDisplayAdded_applyResolutionRefreshRateLimit() { + when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true); + when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate)) + .thenReturn(MAX_REFRESH_RATE); + when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth)) + .thenReturn(MAX_WIDTH); + when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight)) + .thenReturn(MAX_HEIGHT); + init(); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + mObserver.onDisplayAdded(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo( + Vote.forSizeAndPhysicalRefreshRatesRange(0, 0, + MAX_WIDTH, MAX_HEIGHT, + /*minPhysicalRefreshRate=*/ 0, MAX_REFRESH_RATE)); + mObserver.onDisplayRemoved(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + } + + /** External display added, disabled resolution refresh rate limit. */ + @Test + public void testExternalDisplayAdded_disabledResolutionRefreshRateLimit() { + when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true); + init(); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + mObserver.onDisplayAdded(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + mObserver.onDisplayChanged(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + mObserver.onDisplayRemoved(EXTERNAL_DISPLAY); + assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + } + + /** External display added, applied refresh rates synchronization */ + @Test + public void testExternalDisplayAdded_appliedRefreshRatesSynchronization() { + when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(true); + when(mResources.getBoolean(R.bool.config_refreshRateSynchronizationEnabled)) + .thenReturn(true); + init(); + assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null); + mObserver.onDisplayAdded(EXTERNAL_DISPLAY); + assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo( + Vote.forPhysicalRefreshRates( + MAX_REFRESH_RATE - SYNCHRONIZED_REFRESH_RATE_TOLERANCE, + MAX_REFRESH_RATE + SYNCHRONIZED_REFRESH_RATE_TOLERANCE)); + + // Remove external display and check that sync vote is no longer present. + mObserver.onDisplayRemoved(EXTERNAL_DISPLAY); + + assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null); + } + + /** External display added, disabled feature refresh rates synchronization */ + @Test + public void testExternalDisplayAdded_disabledFeatureRefreshRatesSynchronization() { + when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(false); + when(mResources.getBoolean(R.bool.config_refreshRateSynchronizationEnabled)) + .thenReturn(true); + init(); + assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null); + mObserver.onDisplayAdded(EXTERNAL_DISPLAY); + assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null); + } + + /** External display not applied refresh rates synchronization, because + * config_refreshRateSynchronizationEnabled is false. */ + @Test + public void testExternalDisplay_notAppliedRefreshRatesSynchronization() { + when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true); + when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(true); + init(); + assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null); + mObserver.onDisplayAdded(EXTERNAL_DISPLAY); + assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null); + } + + private void init() { + mInjector = mock(DisplayModeDirector.Injector.class); + doAnswer(invocation -> { + assertThat(mObserver).isNull(); + mObserver = invocation.getArgument(0); + return null; + }).when(mInjector).registerDisplayListener(any(), any()); + + doAnswer(c -> { + DisplayInfo info = c.getArgument(1); + info.type = Display.TYPE_INTERNAL; + info.displayId = DEFAULT_DISPLAY; + info.defaultModeId = 0; + info.supportedModes = mInternalDisplayModes; + info.userPreferredModeId = mInternalDisplayUserPreferredModeId; + return true; + }).when(mInjector).getDisplayInfo(eq(DEFAULT_DISPLAY), /*displayInfo=*/ any()); + + doAnswer(c -> { + DisplayInfo info = c.getArgument(1); + info.type = Display.TYPE_EXTERNAL; + info.displayId = EXTERNAL_DISPLAY; + info.defaultModeId = 0; + info.supportedModes = mExternalDisplayModes; + info.userPreferredModeId = mExternalDisplayUserPreferredModeId; + return true; + }).when(mInjector).getDisplayInfo(eq(EXTERNAL_DISPLAY), /*displayInfo=*/ any()); + + doAnswer(c -> mock(SensorManagerInternal.class)).when(mInjector).getSensorManagerInternal(); + doAnswer(c -> mock(DeviceConfigInterface.class)).when(mInjector).getDeviceConfig(); + + mDefaultDisplay = mock(Display.class); + when(mDefaultDisplay.getDisplayId()).thenReturn(DEFAULT_DISPLAY); + doAnswer(c -> mInjector.getDisplayInfo(DEFAULT_DISPLAY, c.getArgument(0))) + .when(mDefaultDisplay).getDisplayInfo(/*displayInfo=*/ any()); + + mExternalDisplay = mock(Display.class); + when(mExternalDisplay.getDisplayId()).thenReturn(EXTERNAL_DISPLAY); + doAnswer(c -> mInjector.getDisplayInfo(EXTERNAL_DISPLAY, c.getArgument(0))) + .when(mExternalDisplay).getDisplayInfo(/*displayInfo=*/ any()); + + when(mInjector.getDisplays()).thenReturn(new Display[] {mDefaultDisplay, mExternalDisplay}); + + mDmd = new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags); + mDmd.start(null); + assertThat(mObserver).isNotNull(); + } + + @Nullable + private Vote getVote(final int displayId, final int priority) { + return mDmd.getVote(displayId, priority); + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java index 287fdd5c344b..50e239218fa0 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java @@ -19,6 +19,7 @@ package com.android.server.display.mode; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.util.SparseArray; @@ -72,6 +73,18 @@ public class VotesStorageTest { verify(mVotesListener).onChanged(); } + /** Verifies that adding the same vote twice results in a single call to onChanged */ + @Test + public void notifiesVoteListenerCalledOnceIfVoteUpdatedTwice() { + // WHEN updateVote is called + mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE); + mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE); + mVotesStorage.updateVote(DISPLAY_ID, PRIORITY_OTHER, VOTE_OTHER); + mVotesStorage.updateVote(DISPLAY_ID, PRIORITY_OTHER, VOTE); + // THEN listener is notified, but only when vote changes. + verify(mVotesListener, times(3)).onChanged(); + } + @Test public void addsAnotherVoteToStorageWithDifferentPriority() { // GIVEN vote storage with one vote diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 552b59ce442d..a250ac75635b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -33,7 +33,6 @@ import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.WORKING_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sSystemClock; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -75,11 +74,11 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; -import android.platform.test.annotations.LargeTest; import android.provider.DeviceConfig; import android.util.ArraySet; import android.util.SparseBooleanArray; +import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.ArrayUtils; @@ -2597,7 +2596,9 @@ public class QuotaControllerTest { @Test public void testIsWithinEJQuotaLocked_TempAllowlisting_Restricted() { setDischarging(); - JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TempAllowlisting_Restricted", 1); + JobStatus js = + createExpeditedJobStatus( + "testIsWithinEJQuotaLocked_TempAllowlisting_Restricted", 1); setStandbyBucket(RESTRICTED_INDEX, js); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); @@ -6088,7 +6089,8 @@ public class QuotaControllerTest { Handler handler = mQuotaController.getHandler(); spyOn(handler); - JobStatus job = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting_Restricted", 1); + JobStatus job = + createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting_Restricted", 1); setStandbyBucket(RESTRICTED_INDEX, job); synchronized (mQuotaController.mLock) { mQuotaController.maybeStartTrackingJobLocked(job, null); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java index 52044bfaef24..de8b3080907c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java @@ -117,7 +117,9 @@ public class ApexManagerTest { Build.VERSION_CODES.CUR_DEVELOPMENT, Build.VERSION.INCREMENTAL); mMockSystem.system().validateFinalState(); - mInstallPackageHelper = new InstallPackageHelper(mPmService, mock(AppDataHelper.class)); + mInstallPackageHelper = new InstallPackageHelper(mPmService, mock(AppDataHelper.class), + mock(RemovePackageHelper.class), mock(DeletePackageHelper.class), + mock(BroadcastHelper.class)); } @NonNull diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt index d6a4d40c763c..931b38dc2951 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt @@ -34,6 +34,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.mock @RunWith(JUnit4::class) class DeletePackageHelperTest { @@ -79,7 +80,8 @@ class DeletePackageHelperTest { whenever(mUserManagerInternal.getUserInfo(1)).thenReturn(UserInfo(1, "test", 0)) whenever(mUserManagerInternal.getProfileParentId(1)).thenReturn(1) - val dph = DeletePackageHelper(mPms) + val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java), + mock(BroadcastHelper::class.java)) val result = dph.deletePackageX("a.data.package", 1L, 1, 0, false) assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED) @@ -97,7 +99,8 @@ class DeletePackageHelperTest { whenever(mUserManagerInternal.getUserInfo(parentId)).thenReturn( UserInfo(userId, "testparent", 0)) - val dph = DeletePackageHelper(mPms) + val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java), + mock(BroadcastHelper::class.java)) val result = dph.deletePackageX("a.data.package", 1L, userId, 0, false) assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED) @@ -112,7 +115,8 @@ class DeletePackageHelperTest { whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM)) .thenReturn(PERMISSION_DENIED) - val dph = DeletePackageHelper(mPms) + val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java), + mock(BroadcastHelper::class.java)) val result = dph.deletePackageX("a.data.package", 1L, 1, PackageManager.DELETE_SYSTEM_APP, false) @@ -133,7 +137,8 @@ class DeletePackageHelperTest { whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM)) .thenReturn(PERMISSION_DENIED) - val dph = DeletePackageHelper(mPms) + val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java), + mock(BroadcastHelper::class.java)) val result = dph.deletePackageX("a.data.package", 1L, userId, PackageManager.DELETE_SYSTEM_APP, false) @@ -150,7 +155,8 @@ class DeletePackageHelperTest { whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM)) .thenReturn(PERMISSION_DENIED) - val dph = DeletePackageHelper(mPms) + val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java), + mock(BroadcastHelper::class.java)) val result = dph.deletePackageX("a.data.package", 1L, 1, PackageManager.DELETE_SYSTEM_APP, false) @@ -164,7 +170,8 @@ class DeletePackageHelperTest { whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM)) .thenReturn(PERMISSION_GRANTED) - val dph = DeletePackageHelper(mPms) + val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java), + mock(BroadcastHelper::class.java)) val result = dph.deletePackageX("a.data.package", 1L, 1, PackageManager.DELETE_SYSTEM_APP, false) diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt index 9f1cec3b595a..cf81f0a77702 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt @@ -39,7 +39,7 @@ class DistractingPackageHelperTest : PackageHelperTestBase() { override fun setup() { super.setup() distractingPackageHelper = DistractingPackageHelper( - pms, rule.mocks().injector, broadcastHelper, suspendPackageHelper) + pms, broadcastHelper, suspendPackageHelper) } @Test @@ -50,12 +50,11 @@ class DistractingPackageHelperTest : PackageHelperTestBase() { testHandler.flush() verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) - verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), - nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), - nullable(), nullable(), nullable(), nullable()) + verify(broadcastHelper).sendDistractingPackagesChanged(any(Computer::class.java), + pkgListCaptor.capture(), any(), any(), flagsCaptor.capture()) - val modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST) - val distractionFlags = bundleCaptor.value.getInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS) + val modifiedPackages = pkgListCaptor.value + val distractionFlags = flagsCaptor.value assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2) assertThat(distractionFlags).isEqualTo(PackageManager.RESTRICTION_HIDE_NOTIFICATIONS) assertThat(unactionedPackages).isEmpty() @@ -75,10 +74,8 @@ class DistractingPackageHelperTest : PackageHelperTestBase() { PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, deviceOwnerUid) testHandler.flush() verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) - verify(broadcastHelper, never()).sendPackageBroadcast( - eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), bundleCaptor.capture(), - anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(), - nullable()) + verify(broadcastHelper, never()).sendDistractingPackagesChanged( + any(), any(), any(), any(), any()) assertThat(unactionedPackages).isEmpty() } @@ -154,11 +151,11 @@ class DistractingPackageHelperTest : PackageHelperTestBase() { testHandler.flush() verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) - verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), - nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), - nullable(), nullable(), nullable(), nullable()) - val modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST) - val distractionFlags = bundleCaptor.value.getInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS) + verify(broadcastHelper).sendDistractingPackagesChanged( + any(Computer::class.java), pkgListCaptor.capture(), any(), eq(TEST_USER_ID), + flagsCaptor.capture()) + val modifiedPackages = pkgListCaptor.value + val distractionFlags = flagsCaptor.value assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2) assertThat(distractionFlags).isEqualTo(PackageManager.RESTRICTION_NONE) } @@ -170,9 +167,8 @@ class DistractingPackageHelperTest : PackageHelperTestBase() { testHandler.flush() verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) - verify(broadcastHelper, never()).sendPackageBroadcast(eq( - Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(), - nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable()) + verify(broadcastHelper, never()).sendDistractingPackagesChanged( + any(), any(), any(), any(), any()) } @Test @@ -189,22 +185,21 @@ class DistractingPackageHelperTest : PackageHelperTestBase() { arrayOfNulls(0), TEST_USER_ID) testHandler.flush() verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) - verify(broadcastHelper, never()).sendPackageBroadcast(eq( - Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(), - nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable()) + verify(broadcastHelper, never()).sendDistractingPackagesChanged( + any(), any(), any(), any(), any()) } @Test fun sendDistractingPackagesChanged() { - distractingPackageHelper.sendDistractingPackagesChanged(packagesToChange, uidsToChange, - TEST_USER_ID, PackageManager.RESTRICTION_HIDE_NOTIFICATIONS) + broadcastHelper.sendDistractingPackagesChanged(pms.snapshotComputer(), + packagesToChange, uidsToChange, TEST_USER_ID, + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS) testHandler.flush() - verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), - nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), - nullable(), nullable(), nullable(), nullable()) + verify(broadcastHelper).sendDistractingPackagesChanged(any(Computer::class.java), + pkgListCaptor.capture(), uidsCaptor.capture(), eq(TEST_USER_ID), any()) - var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST) - var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST) + var changedPackages = pkgListCaptor.value + var changedUids = uidsCaptor.value assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2) assertThat(changedUids).asList().containsExactly( packageSetting1.appId, packageSetting2.appId) 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 e578ea35518d..2f6859c14742 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -505,8 +505,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { pkg: ParsingPackage, applicationInfo: ApplicationInfo?, className: String? - ): - ActivityInfo { + ): ActivityInfo { val activityInfo = ActivityInfo() activityInfo.applicationInfo = applicationInfo activityInfo.packageName = pkg.packageName @@ -518,8 +517,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { pkg: ParsingPackage, applicationInfo: ApplicationInfo?, className: String? - ): - ServiceInfo { + ): ServiceInfo { val serviceInfo = ServiceInfo() serviceInfo.applicationInfo = applicationInfo serviceInfo.packageName = pkg.packageName @@ -699,8 +697,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { /** Override get*Folder methods to point to temporary local directories */ @Throws(IOException::class) - private fun redirectScanPartitions(partitions: List<ScanPartition>): - List<ScanPartition> { + private fun redirectScanPartitions(partitions: List<ScanPartition>): List<ScanPartition> { val spiedPartitions: MutableList<ScanPartition> = ArrayList(partitions.size) for (partition: ScanPartition in partitions) { @@ -732,6 +729,7 @@ class MockSystemRule : TestRule { } finally { mockSystem?.cleanup() mockSystem = null + Mockito.framework().clearInlineMocks() } } } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt index 5fd270ecb2b4..eb001645863f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt @@ -17,7 +17,6 @@ package com.android.server.pm import android.os.Build -import android.os.Bundle import android.os.UserHandle import android.os.UserManager import com.android.server.pm.pkg.PackageStateInternal @@ -68,7 +67,11 @@ open class PackageHelperTestBase { lateinit var protectedPackages: ProtectedPackages @Captor - lateinit var bundleCaptor: ArgumentCaptor<Bundle> + lateinit var pkgListCaptor: ArgumentCaptor<Array<String>> + @Captor + lateinit var flagsCaptor: ArgumentCaptor<Int> + @Captor + lateinit var uidsCaptor: ArgumentCaptor<IntArray> @Rule @JvmField diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java index c5db5db41787..6c44fd0da1c4 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java @@ -17,12 +17,12 @@ package com.android.server.pm; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.after; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.content.Intent; import android.content.pm.PackageInstaller; @@ -63,9 +63,7 @@ public class PackageMonitorCallbackHelperTest { @Before public void setup() { - when(mMockSystem.mocks().getInjector().getHandler()).thenReturn(mHandler); - mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper( - mMockSystem.mocks().getInjector()); + mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper(); } @@ -80,7 +78,7 @@ public class PackageMonitorCallbackHelperTest { mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */, - null /* instantUserIds */, null /* broadcastAllowList */); + null /* instantUserIds */, null /* broadcastAllowList */, mHandler); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any()); } @@ -93,7 +91,7 @@ public class PackageMonitorCallbackHelperTest { Binder.getCallingUid()); mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0}, null /* instantUserIds */, - null /* broadcastAllowList */); + null /* broadcastAllowList */, mHandler); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any()); @@ -101,7 +99,7 @@ public class PackageMonitorCallbackHelperTest { mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback); mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */, - null /* instantUserIds */, null /* broadcastAllowList */); + null /* instantUserIds */, null /* broadcastAllowList */, mHandler); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any()); } @@ -114,7 +112,7 @@ public class PackageMonitorCallbackHelperTest { Binder.getCallingUid()); mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */, - null /* instantUserIds */, null /* broadcastAllowList */); + null /* instantUserIds */, null /* broadcastAllowList */, mHandler); ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult( @@ -138,7 +136,7 @@ public class PackageMonitorCallbackHelperTest { // Notify for user 10 mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */, - null /* instantUserIds */, null /* broadcastAllowList */); + null /* instantUserIds */, null /* broadcastAllowList */, mHandler); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any()); } @@ -155,7 +153,7 @@ public class PackageMonitorCallbackHelperTest { mPackageMonitorCallbackHelper.notifyPackageChanged(FAKE_PACKAGE_NAME, false /* dontKillApp */, components, FAKE_PACKAGE_UID, null /* reason */, new int[]{0} /* userIds */, null /* instantUserIds */, - null /* broadcastAllowList */); + null /* broadcastAllowList */, mHandler); ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult( @@ -183,7 +181,8 @@ public class PackageMonitorCallbackHelperTest { Binder.getCallingUid()); mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(FAKE_PACKAGE_NAME, FAKE_PACKAGE_UID, new int[]{0} /* userIds */, new int[0], false /* isArchived */, - PackageInstaller.DATA_LOADER_TYPE_STREAMING, null /* broadcastAllowList */); + PackageInstaller.DATA_LOADER_TYPE_STREAMING, null /* broadcastAllowList */, + mHandler); ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult( @@ -207,7 +206,7 @@ public class PackageMonitorCallbackHelperTest { Binder.getCallingUid()); mPackageMonitorCallbackHelper.notifyResourcesChanged(true /* mediaStatus */, true /* replacing */, new String[]{FAKE_PACKAGE_NAME}, - new int[]{FAKE_PACKAGE_UID} /* uids */); + new int[]{FAKE_PACKAGE_UID} /* uids */, mHandler); ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult( @@ -240,7 +239,7 @@ public class PackageMonitorCallbackHelperTest { mPackageMonitorCallbackHelper.onUserRemoved(10); mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */, - null /* instantUserIds */, null /* broadcastAllowList */); + null /* instantUserIds */, null /* broadcastAllowList */, mHandler); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any()); } @@ -256,7 +255,7 @@ public class PackageMonitorCallbackHelperTest { Binder.getCallingUid()); mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */, - null /* instantUserIds */, broadcastAllowList); + null /* instantUserIds */, broadcastAllowList, mHandler); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any()); } @@ -272,7 +271,7 @@ public class PackageMonitorCallbackHelperTest { Binder.getCallingUid()); mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */, - null /* instantUserIds */, broadcastAllowList); + null /* instantUserIds */, broadcastAllowList, mHandler); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any()); } @@ -288,7 +287,7 @@ public class PackageMonitorCallbackHelperTest { Process.SYSTEM_UID); mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */, - null /* instantUserIds */, broadcastAllowList); + null /* instantUserIds */, broadcastAllowList, mHandler); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any()); } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt index 679757629e32..4240373b7c1d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt @@ -22,12 +22,11 @@ import android.os.Binder import android.os.PersistableBundle import com.android.server.testutils.any import com.android.server.testutils.eq -import com.android.server.testutils.nullable import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito import org.mockito.Mockito.times import org.mockito.Mockito.verify @@ -44,17 +43,10 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { testHandler.flush() verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) - verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_SUSPENDED), - nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), - nullable(), nullable(), nullable(), nullable()) - verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_SUSPENDED), nullable(), - nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(), nullable(), - nullable(), nullable()) - verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_SUSPENDED), nullable(), - nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(), nullable(), - nullable(), nullable()) - - var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST) + verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java), + eq(Intent.ACTION_PACKAGES_SUSPENDED), pkgListCaptor.capture(), any(), any(), any()) + + var modifiedPackages = pkgListCaptor.value assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2) assertThat(failedNames).isEmpty() } @@ -146,6 +138,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */) testHandler.flush() + Mockito.clearInvocations(broadcastHelper) assertThat(failedNames).isEmpty() failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, false /* suspended */, null /* appExtras */, @@ -154,17 +147,13 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { testHandler.flush() verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) - verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED), - nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), - nullable(), nullable(), nullable(), nullable()) - verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED), - nullable(), nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(), - nullable(), nullable(), nullable()) - verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED), - nullable(), nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(), - nullable(), nullable(), nullable()) - - var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST) + verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java), + eq(Intent.ACTION_PACKAGES_UNSUSPENDED), pkgListCaptor.capture(), any(), any(), + any()) + verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(any(Computer::class.java), + any(), any(), any()) + + var modifiedPackages = pkgListCaptor.value assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2) assertThat(failedNames).isEmpty() } @@ -206,7 +195,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { testHandler.flush() assertThat(failedNames).isEmpty() - val result = suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), + val result = SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)!! assertThat(result.getString(TEST_PACKAGE_1)).isEqualTo(TEST_PACKAGE_1) @@ -222,14 +211,15 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */) testHandler.flush() + Mockito.clearInvocations(broadcastHelper) assertThat(failedNames).isEmpty() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) - assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), + assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNotNull() - assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), + assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNotNull() suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), @@ -238,23 +228,18 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { testHandler.flush() verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) - verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED), - nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), - nullable(), nullable(), nullable(), nullable()) - verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED), - nullable(), nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(), - nullable(), nullable(), nullable()) - verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED), - nullable(), nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(), - nullable(), nullable(), nullable()) + verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java), + eq(Intent.ACTION_PACKAGES_UNSUSPENDED), any(), any(), any(), any()) + verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(any(Computer::class.java), + any(), any(), any()) assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull() - assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), + assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull() - assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), + assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull() } @@ -319,39 +304,4 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { assertThat(result.title).isEqualTo(TEST_PACKAGE_1) } - - @Test - @Throws(Exception::class) - fun sendPackagesSuspendedForUser() { - suspendPackageHelper.sendPackagesSuspendedForUser( - Intent.ACTION_PACKAGES_SUSPENDED, packagesToChange, uidsToChange, false, TEST_USER_ID) - testHandler.flush() - verify(broadcastHelper).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(), - anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(), - nullable()) - - var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST) - var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST) - assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2) - assertThat(changedUids).asList().containsExactly( - packageSetting1.appId, packageSetting2.appId) - } - - @Test - @Throws(Exception::class) - fun sendPackagesSuspendModifiedForUser() { - suspendPackageHelper.sendPackagesSuspendedForUser( - Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, packagesToChange, uidsToChange, false, TEST_USER_ID) - testHandler.flush() - verify(broadcastHelper).sendPackageBroadcast( - eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(), - anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(), - nullable()) - - var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST) - var modifiedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST) - assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2) - assertThat(modifiedUids).asList().containsExactly( - packageSetting1.appId, packageSetting2.appId) - } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java index 82b75408ad18..2f0257ab6b25 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java @@ -39,11 +39,16 @@ import android.content.pm.ApplicationInfo; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.hardware.display.DisplayManager; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.testing.DexmakerShareClassLoaderRule; import android.view.Display; +import android.view.WindowManager; import com.android.server.accessibility.magnification.MagnificationProcessor; import com.android.server.accessibility.test.MessageCapturingHandler; @@ -76,6 +81,9 @@ public class AccessibilityServiceConnectionTest { public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + AccessibilityServiceConnection mConnection; @Mock AccessibilityUserState mMockUserState; @@ -113,6 +121,8 @@ public class AccessibilityServiceConnectionTest { when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient); when(mMockA11yTrace.isA11yTracingEnabled()).thenReturn(false); + when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)) + .thenReturn(new DisplayManager(mMockContext)); mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext, COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(), @@ -168,6 +178,18 @@ public class AccessibilityServiceConnectionTest { assertFalse(mConnection.getServiceInfo().crashed); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK) + public void onServiceConnected_addsWindowTokens() { + setServiceBinding(COMPONENT_NAME); + mConnection.bindLocked(); + mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); + + verify(mMockWindowManagerInternal).addWindowToken( + any(), eq(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY), + anyInt(), eq(null)); + } + private void setServiceBinding(ComponentName componentName) { when(mMockUserState.getBindingServicesLocked()) .thenReturn(new HashSet<>(Arrays.asList(componentName))); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java index b4558b211fc6..63281b77ade7 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java @@ -35,6 +35,7 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -46,6 +47,9 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Color; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.test.mock.MockContentResolver; import android.testing.DexmakerShareClassLoaderRule; @@ -88,6 +92,9 @@ public class AccessibilityUserStateTest { @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Mock private AccessibilityServiceInfo mMockServiceInfo; @Mock private AccessibilityServiceConnection mMockConnection; @@ -188,7 +195,7 @@ public class AccessibilityUserStateTest { mUserState.addServiceLocked(mMockConnection); - verify(mMockConnection, never()).onAdded(); + verify(mMockListener, never()).onServiceInfoChangedLocked(any()); } @Test @@ -197,13 +204,24 @@ public class AccessibilityUserStateTest { mUserState.addServiceLocked(mMockConnection); - verify(mMockConnection).onAdded(); assertTrue(mUserState.getBoundServicesLocked().contains(mMockConnection)); assertEquals(mMockConnection, mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME)); verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState)); } @Test + // addServiceLocked only calls addWindowTokensForAllDisplays when + // FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK is off, so skip the test if it is on. + @RequiresFlagsDisabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK) + public void addService_flagDisabled_addsWindowTokens() { + when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME); + + mUserState.addServiceLocked(mMockConnection); + + verify(mMockConnection).addWindowTokensForAllDisplays(); + } + + @Test public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() { // When soft kb show mode is hidden in settings and is auto in state. putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java index 4ce9ba031b25..3ee5f61bb8cd 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java @@ -21,8 +21,11 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -36,6 +39,10 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.hardware.display.DisplayManager; import android.os.IBinder; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import com.android.server.accessibility.test.MessageCapturingHandler; @@ -43,6 +50,7 @@ import com.android.server.wm.WindowManagerInternal; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; @@ -58,6 +66,9 @@ public class UiAutomationManagerTest { MessageCapturingHandler mMessageCapturingHandler; + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Mock Context mMockContext; @Mock AccessibilityServiceInfo mMockServiceInfo; @Mock ResolveInfo mMockResolveInfo; @@ -197,6 +208,24 @@ public class UiAutomationManagerTest { assertEquals(0, mUiAutomationManager.getRequestedEventMaskLocked()); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK) + public void registerUiAutomationService_callsAddWindowTokenUsingHandler() { + register(0); + // registerUiTestAutomationServiceLocked() should not directly call addWindowToken. + verify(mMockWindowManagerInternal, never()).addWindowToken( + any(), anyInt(), anyInt(), any()); + + // Advance UiAutomationManager#UiAutomationService's handler. + mMessageCapturingHandler.sendAllMessages(); + + // After advancing the handler we expect addWindowToken to have been called + // by the UiAutomationService instance. + verify(mMockWindowManagerInternal).addWindowToken( + any(), eq(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY), + anyInt(), eq(null)); + } + private void register(int flags) { mUiAutomationManager.registerUiTestAutomationServiceLocked(mMockOwner, mMockAccessibilityServiceClient, mMockContext, mMockServiceInfo, SERVICE_ID, diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java index 4a06611f397e..1cd61e90126e 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java @@ -253,7 +253,8 @@ public class TouchExplorerTest { goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER); moveEachPointers(mLastEvent, p(10, 10)); send(mLastEvent); - + // Wait 10 ms to make sure that hover enter and exit are not scheduled for the same moment. + mHandler.fastForward(10); send(upEvent()); // Wait for sending hover exit event to transit to clear state. mHandler.fastForward(USER_INTENT_TIMEOUT); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index 349a597a37fa..430f600a6288 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -687,7 +687,7 @@ public class FullScreenMagnificationGestureHandlerTest { swipeAndHold(initCoords, edgeCoords); assertTrue(mMgh.mCurrentState == mMgh.mSinglePanningState); - assertTrue(mMgh.mSinglePanningState.mOverscrollState == mMgh.OVERSCROLL_LEFT_EDGE); + assertTrue(mMgh.mOverscrollHandler.mOverscrollState == mMgh.OVERSCROLL_LEFT_EDGE); assertTrue(isZoomed()); } @@ -711,7 +711,7 @@ public class FullScreenMagnificationGestureHandlerTest { swipeAndHold(initCoords, edgeCoords); assertTrue(mMgh.mCurrentState == mMgh.mSinglePanningState); - assertTrue(mMgh.mSinglePanningState.mOverscrollState == mMgh.OVERSCROLL_RIGHT_EDGE); + assertTrue(mMgh.mOverscrollHandler.mOverscrollState == mMgh.OVERSCROLL_RIGHT_EDGE); assertTrue(isZoomed()); } @@ -734,7 +734,7 @@ public class FullScreenMagnificationGestureHandlerTest { swipeAndHold(initCoords, edgeCoords); - assertTrue(mMgh.mSinglePanningState.mOverscrollState == mMgh.OVERSCROLL_VERTICAL_EDGE); + assertTrue(mMgh.mOverscrollHandler.mOverscrollState == mMgh.OVERSCROLL_VERTICAL_EDGE); assertTrue(isZoomed()); } @@ -756,7 +756,7 @@ public class FullScreenMagnificationGestureHandlerTest { swipeAndHold(initCoords, edgeCoords); - assertTrue(mMgh.mSinglePanningState.mOverscrollState == mMgh.OVERSCROLL_NONE); + assertTrue(mMgh.mOverscrollHandler.mOverscrollState == mMgh.OVERSCROLL_NONE); assertTrue(isZoomed()); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java index 8faddf8ff541..fcfe48ef4c03 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java @@ -17,7 +17,6 @@ package com.android.server.accessibility.magnification; 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.mock; @@ -27,13 +26,13 @@ import static org.mockito.Mockito.when; import android.graphics.Rect; import android.os.Handler; -import android.platform.test.annotations.LargeTest; import android.testing.TestableContext; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowMetrics; import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Before; diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java index c40ad2840501..1c48b8aa79f9 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java @@ -88,7 +88,8 @@ public class VirtualAudioControllerTest { /* secureWindowCallback= */ null, /* intentListenerCallback= */ null, /* displayCategories= */ new ArraySet<>(), - /* showTasksInHostDeviceRecents= */ true); + /* showTasksInHostDeviceRecents= */ true, + /* customHomeComponent= */ null); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 0d172fdb2a80..708ee352a8c9 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -21,8 +21,15 @@ import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV; import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; +import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM; +import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; +import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; +import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_3; import static com.android.server.hdmi.HdmiControlService.DEVICE_CLEANUP_TIMEOUT; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; +import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_HOTPLUG; +import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_SCREEN_ON; +import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_SOUNDBAR_MODE; import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON; import static com.google.common.truth.Truth.assertThat; @@ -51,6 +58,7 @@ import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener; import android.hardware.hdmi.IHdmiControlStatusChangeListener; import android.hardware.hdmi.IHdmiVendorCommandListener; +import android.hardware.tv.cec.V1_0.SendMessageResult; import android.os.Binder; import android.os.Looper; import android.os.RemoteException; @@ -71,6 +79,7 @@ import org.mockito.Mockito; import java.util.ArrayList; import java.util.Arrays; import java.util.Optional; +import java.util.concurrent.TimeUnit; /** * Tests for {@link HdmiControlService} class. @@ -137,7 +146,7 @@ public class HdmiControlServiceTest { mLocalDevices.add(mAudioSystemDeviceSpy); mLocalDevices.add(mPlaybackDeviceSpy); - mHdmiPortInfo = new HdmiPortInfo[4]; + mHdmiPortInfo = new HdmiPortInfo[5]; mHdmiPortInfo[0] = new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, 0x2100) .setCecSupported(true) @@ -166,6 +175,13 @@ public class HdmiControlServiceTest { .setArcSupported(false) .setEarcSupported(false) .build(); + mHdmiPortInfo[4] = + new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_OUTPUT, 0x3000) + .setCecSupported(true) + .setMhlSupported(false) + .setArcSupported(false) + .setEarcSupported(false) + .build(); mNativeWrapper.setPortInfo(mHdmiPortInfo); mHdmiControlServiceSpy.initService(); mWakeLockSpy = spy(new FakePowerManagerWrapper.FakeWakeLockWrapper()); @@ -1396,6 +1412,207 @@ public class HdmiControlServiceTest { } @Test + public void triggerMultipleAddressAllocations_uniqueLocalDevicePerDeviceType() { + long allocationDelay = TimeUnit.SECONDS.toMillis(60); + mHdmiCecController.setLogicalAddressAllocationDelay(allocationDelay); + mTestLooper.dispatchAll(); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + // Wake up process that will trigger the address allocation to start. + mHdmiControlServiceSpy.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON); + verify(mHdmiControlServiceSpy, times(1)) + .allocateLogicalAddress(any(), eq(INITIATED_BY_SCREEN_ON)); + Mockito.clearInvocations(mHdmiControlServiceSpy); + mTestLooper.dispatchAll(); + + mTestLooper.moveTimeForward(allocationDelay / 2); + mTestLooper.dispatchAll(); + // Hotplug In will trigger the address allocation to start. + mHdmiControlServiceSpy.onHotplug(4, true); + verify(mHdmiControlServiceSpy, times(1)) + .allocateLogicalAddress(any(), eq(INITIATED_BY_HOTPLUG)); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + mTestLooper.moveTimeForward(allocationDelay / 2); + mTestLooper.dispatchAll(); + // The first allocation finished. The second allocation is still in progress. + HdmiCecLocalDevicePlayback firstAllocatedPlayback = mHdmiControlServiceSpy.playback(); + verify(mHdmiControlServiceSpy, times(1)) + .notifyAddressAllocated(any(), eq(INITIATED_BY_SCREEN_ON)); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + mTestLooper.moveTimeForward(allocationDelay / 2); + mTestLooper.dispatchAll(); + // The second allocation finished. + HdmiCecLocalDevicePlayback secondAllocatedPlayback = mHdmiControlServiceSpy.playback(); + verify(mHdmiControlServiceSpy, times(1)) + .notifyAddressAllocated(any(), eq(INITIATED_BY_HOTPLUG)); + // Local devices have the same identity. + assertTrue(firstAllocatedPlayback == secondAllocatedPlayback); + } + + @Test + public void triggerMultipleAddressAllocations_keepLastAllocatedAddress() { + // First logical address for playback is free. + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.NACK); + mTestLooper.dispatchAll(); + + long allocationDelay = TimeUnit.SECONDS.toMillis(60); + mHdmiCecController.setLogicalAddressAllocationDelay(allocationDelay); + mTestLooper.dispatchAll(); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + // Wake up process that will trigger the address allocation to start. + mHdmiControlServiceSpy.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON); + verify(mHdmiControlServiceSpy, times(1)) + .allocateLogicalAddress(any(), eq(INITIATED_BY_SCREEN_ON)); + Mockito.clearInvocations(mHdmiControlServiceSpy); + mTestLooper.dispatchAll(); + + mTestLooper.moveTimeForward(allocationDelay / 2); + mTestLooper.dispatchAll(); + + // First logical address for playback is busy. + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); + mTestLooper.dispatchAll(); + + mHdmiControlServiceSpy.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON); + verify(mHdmiControlServiceSpy, times(1)) + .allocateLogicalAddress(any(), eq(INITIATED_BY_SCREEN_ON)); + Mockito.clearInvocations(mHdmiControlServiceSpy); + mTestLooper.dispatchAll(); + + mTestLooper.moveTimeForward(allocationDelay / 2); + mTestLooper.dispatchAll(); + // The first allocation finished. The second allocation is still in progress. + verify(mHdmiControlServiceSpy, times(1)) + .notifyAddressAllocated(any(), eq(INITIATED_BY_SCREEN_ON)); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + mTestLooper.moveTimeForward(allocationDelay / 2); + mTestLooper.dispatchAll(); + // The second allocation finished. Second logical address for playback is used. + HdmiCecLocalDevicePlayback allocatedPlayback = mHdmiControlServiceSpy.playback(); + verify(mHdmiControlServiceSpy, times(1)) + .notifyAddressAllocated(any(), eq(INITIATED_BY_SCREEN_ON)); + assertThat(allocatedPlayback.getDeviceInfo().getLogicalAddress()) + .isEqualTo(ADDR_PLAYBACK_2); + } + + @Test + public void triggerMultipleAddressAllocations_toggleSoundbarMode_addThenRemoveAudioSystem() { + mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON); + long allocationDelay = TimeUnit.SECONDS.toMillis(60); + mHdmiCecController.setLogicalAddressAllocationDelay(allocationDelay); + mTestLooper.dispatchAll(); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + // Enabling Dynamic soundbar mode will trigger address allocation. + mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE, + HdmiControlManager.SOUNDBAR_MODE_ENABLED); + mTestLooper.dispatchAll(); + verify(mHdmiControlServiceSpy, times(1)) + .allocateLogicalAddress(any(), eq(INITIATED_BY_SOUNDBAR_MODE)); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + mTestLooper.moveTimeForward(allocationDelay / 2); + mTestLooper.dispatchAll(); + // Disabling Dynamic soundbar mode will trigger another address allocation. + mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE, + HdmiControlManager.SOUNDBAR_MODE_DISABLED); + mTestLooper.dispatchAll(); + verify(mHdmiControlServiceSpy, times(1)) + .allocateLogicalAddress(any(), eq(INITIATED_BY_SOUNDBAR_MODE)); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + mTestLooper.moveTimeForward(allocationDelay / 2); + mTestLooper.dispatchAll(); + // The first allocation finished. The second allocation is still in progress. + // The audio system is present in the network. + verify(mHdmiControlServiceSpy, times(1)) + .notifyAddressAllocated(any(), eq(INITIATED_BY_SOUNDBAR_MODE)); + assertThat(mHdmiControlServiceSpy.audioSystem()).isNotNull(); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + mTestLooper.moveTimeForward(allocationDelay / 2); + mTestLooper.dispatchAll(); + // The second allocation finished. The audio system is not present in the network. + verify(mHdmiControlServiceSpy, times(1)) + .notifyAddressAllocated(any(), eq(INITIATED_BY_SOUNDBAR_MODE)); + assertThat(mHdmiControlServiceSpy.audioSystem()).isNull(); + } + + @Test + public void triggerMultipleAddressAllocations_toggleSoundbarMode_removeThenAddAudioSystem() { + mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON); + // Enable the setting and check if the audio system local device is found in the network. + mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE, + HdmiControlManager.SOUNDBAR_MODE_ENABLED); + mTestLooper.dispatchAll(); + assertThat(mHdmiControlServiceSpy.audioSystem()).isNotNull(); + + long allocationDelay = TimeUnit.SECONDS.toMillis(60); + mHdmiCecController.setLogicalAddressAllocationDelay(allocationDelay); + mTestLooper.dispatchAll(); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + // Disabling Dynamic soundbar mode will trigger address allocation. + mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE, + HdmiControlManager.SOUNDBAR_MODE_DISABLED); + mTestLooper.dispatchAll(); + verify(mHdmiControlServiceSpy, times(1)) + .allocateLogicalAddress(any(), eq(INITIATED_BY_SOUNDBAR_MODE)); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + mTestLooper.moveTimeForward(allocationDelay / 2); + mTestLooper.dispatchAll(); + // Enabling Dynamic soundbar mode will trigger another address allocation. + mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE, + HdmiControlManager.SOUNDBAR_MODE_ENABLED); + mTestLooper.dispatchAll(); + verify(mHdmiControlServiceSpy, times(1)) + .allocateLogicalAddress(any(), eq(INITIATED_BY_SOUNDBAR_MODE)); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + mTestLooper.moveTimeForward(allocationDelay / 2); + mTestLooper.dispatchAll(); + // The first allocation finished. The second allocation is still in progress. + // The audio system is not present in the network. + verify(mHdmiControlServiceSpy, times(1)) + .notifyAddressAllocated(any(), eq(INITIATED_BY_SOUNDBAR_MODE)); + assertThat(mHdmiControlServiceSpy.audioSystem()).isNull(); + Mockito.clearInvocations(mHdmiControlServiceSpy); + + mTestLooper.moveTimeForward(allocationDelay / 2); + mTestLooper.dispatchAll(); + // The second allocation finished. The audio system is present in the network. + verify(mHdmiControlServiceSpy, times(1)) + .notifyAddressAllocated(any(), eq(INITIATED_BY_SOUNDBAR_MODE)); + assertThat(mHdmiControlServiceSpy.audioSystem()).isNotNull(); + } + + @Test + public void failedAddressAllocation_noLocalDevice() { + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_AUDIO_SYSTEM, SendMessageResult.SUCCESS); + mTestLooper.dispatchAll(); + + mHdmiControlServiceSpy.clearCecLocalDevices(); + mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + + assertThat(mHdmiControlServiceSpy.playback()).isNull(); + assertThat(mHdmiControlServiceSpy.audioSystem()).isNull(); + } + + @Test public void earcIdle_blocksArcConnection() { mHdmiControlServiceSpy.clearEarcLocalDevice(); HdmiEarcLocalDeviceTx localDeviceTx = new HdmiEarcLocalDeviceTx(mHdmiControlServiceSpy); diff --git a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java index be1375310704..213e05eb788e 100644 --- a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java +++ b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java @@ -18,7 +18,6 @@ package com.android.server.job; import static android.app.job.JobInfo.NETWORK_TYPE_ANY; import static android.app.job.JobInfo.NETWORK_TYPE_NONE; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -28,13 +27,14 @@ import static org.junit.Assert.fail; import android.app.job.JobInfo; import android.content.ComponentName; -import android.platform.test.annotations.LargeTest; import android.util.ArraySet; import android.util.Log; import android.util.SparseArrayMap; import android.util.SparseBooleanArray; import android.util.SparseLongArray; +import androidx.test.filters.LargeTest; + import com.android.server.job.controllers.JobStatus; import org.junit.Test; diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index dee77806e4f3..37a6d22f038b 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -781,7 +781,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode()); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mService.onCleanupUser(PRIMARY_USER_ID); + mService.onUserStopped(PRIMARY_USER_ID); assertNull(mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID)); assertTrue(mLocalService.unlockUserWithToken(handle, token, PRIMARY_USER_ID)); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java b/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java index 2c9ba34e850f..e8b7ad7e7389 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java @@ -169,7 +169,7 @@ public class WeakEscrowTokenTests extends BaseLockSettingsServiceTests{ assertTrue(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID)); assertTrue(mService.isWeakEscrowTokenValid(handle, token, PRIMARY_USER_ID)); - mService.onCleanupUser(PRIMARY_USER_ID); + mService.onUserStopped(PRIMARY_USER_ID); assertNull(mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID)); assertTrue(mLocalService.unlockUserWithToken(handle, token, PRIMARY_USER_ID)); diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java index 83fa29a5dd66..6ae26585fab2 100644 --- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java +++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java @@ -382,7 +382,7 @@ public class UsageStatsDatabaseTest { void runWriteReadTest(int interval) throws IOException { mUsageStatsDatabase.putUsageStats(interval, mIntervalStats); List<IntervalStats> stats = mUsageStatsDatabase.queryUsageStats(interval, 0, mEndTime, - mIntervalStatsVerifier); + mIntervalStatsVerifier, false); assertEquals(1, stats.size()); compareIntervalStats(mIntervalStats, stats.get(0), MAX_TESTED_VERSION); @@ -421,7 +421,7 @@ public class UsageStatsDatabaseTest { newDB.readMappingsLocked(); newDB.init(mEndTime); List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime, - mIntervalStatsVerifier); + mIntervalStatsVerifier, false); assertEquals(1, stats.size()); @@ -465,7 +465,7 @@ public class UsageStatsDatabaseTest { assertTrue(restoredApps.containsAll(prevDBApps), "List of restored apps does not match list backed-up apps list."); List<IntervalStats> stats = newDB.queryUsageStats( - UsageStatsManager.INTERVAL_DAILY, 0, mEndTime, mIntervalStatsVerifier); + UsageStatsManager.INTERVAL_DAILY, 0, mEndTime, mIntervalStatsVerifier, false); if (version > UsageStatsDatabase.BACKUP_VERSION || version < 1) { assertFalse(stats != null && !stats.isEmpty(), @@ -593,7 +593,7 @@ public class UsageStatsDatabaseTest { newDB.readMappingsLocked(); newDB.init(mEndTime); List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime, - mIntervalStatsVerifier); + mIntervalStatsVerifier, false); assertEquals(1, stats.size()); // The written and read IntervalStats should match @@ -620,7 +620,7 @@ public class UsageStatsDatabaseTest { db.onPackageRemoved(removedPackage, System.currentTimeMillis()); List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime, - mIntervalStatsVerifier); + mIntervalStatsVerifier, false); assertEquals(1, stats.size(), "Only one interval stats object should exist for the given time range."); final IntervalStats stat = stats.get(0); @@ -648,7 +648,7 @@ public class UsageStatsDatabaseTest { private void verifyPackageDataIsRemoved(UsageStatsDatabase db, int interval, String removedPackage) { List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime, - mIntervalStatsVerifier); + mIntervalStatsVerifier, false); assertEquals(1, stats.size(), "Only one interval stats object should exist for the given time range."); final IntervalStats stat = stats.get(0); @@ -668,7 +668,7 @@ public class UsageStatsDatabaseTest { private void verifyPackageDataIsNotRemoved(UsageStatsDatabase db, int interval, Set<String> installedPackages) { List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime, - mIntervalStatsVerifier); + mIntervalStatsVerifier, false); assertEquals(1, stats.size(), "Only one interval stats object should exist for the given time range."); final IntervalStats stat = stats.get(0); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 020afdbce987..8d7b5cb984b4 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -141,6 +141,7 @@ import com.android.server.UiServiceTestCase; import com.android.server.notification.PermissionHelper.PackagePermission; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.protobuf.InvalidProtocolBufferException; import org.json.JSONArray; @@ -563,7 +564,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false)); List<NotificationChannelGroup> actualGroups = mHelper.getNotificationChannelGroups( - PKG_N_MR1, UID_N_MR1, false, true, false).getList(); + PKG_N_MR1, UID_N_MR1, false, true, false, true, null).getList(); boolean foundNcg = false; for (NotificationChannelGroup actual : actualGroups) { if (ncg.getId().equals(actual.getId())) { @@ -647,7 +648,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false)); List<NotificationChannelGroup> actualGroups = mHelper.getNotificationChannelGroups( - PKG_N_MR1, UID_N_MR1, false, true, false).getList(); + PKG_N_MR1, UID_N_MR1, false, true, false, true, null).getList(); boolean foundNcg = false; for (NotificationChannelGroup actual : actualGroups) { if (ncg.getId().equals(actual.getId())) { @@ -2620,6 +2621,16 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void testOnlyHasDefaultChannel() throws Exception { + assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1)); + assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O)); + + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false, + UID_N_MR1, false); + assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1)); + } + + @Test public void testCreateDeletedChannel() throws Exception { long[] vibration = new long[]{100, 67, 145, 156}; NotificationChannel channel = @@ -2644,16 +2655,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testOnlyHasDefaultChannel() throws Exception { - assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1)); - assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O)); - - mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false, - UID_N_MR1, false); - assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1)); - } - - @Test public void testCreateChannel_defaultChannelId() throws Exception { try { mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, new NotificationChannel( @@ -2884,7 +2885,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { UID_N_MR1}); assertEquals(0, mHelper.getNotificationChannelGroups( - PKG_N_MR1, UID_N_MR1, true, true, false).getList().size()); + PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList().size()); } @Test @@ -3022,7 +3023,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { UID_N_MR1, false); List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups( - PKG_N_MR1, UID_N_MR1, true, true, false).getList(); + PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList(); assertEquals(3, actual.size()); for (NotificationChannelGroup group : actual) { if (group.getId() == null) { @@ -3056,14 +3057,15 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel1.setGroup(ncg.getId()); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false, UID_N_MR1, false); - mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true, false).getList(); + mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true, false, true, null) + .getList(); channel1.setImportance(IMPORTANCE_LOW); mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, UID_N_MR1, false); List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups( - PKG_N_MR1, UID_N_MR1, true, true, false).getList(); + PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList(); assertEquals(2, actual.size()); for (NotificationChannelGroup group : actual) { @@ -3089,7 +3091,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { UID_N_MR1, false); List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups( - PKG_N_MR1, UID_N_MR1, false, false, true).getList(); + PKG_N_MR1, UID_N_MR1, false, false, true, true, null).getList(); assertEquals(2, actual.size()); for (NotificationChannelGroup group : actual) { @@ -5786,6 +5788,62 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertFalse(isUserSet); } + @Test + public void testGetNotificationChannelGroups_withChannelFilter_includeBlocked() { + NotificationChannel channel = + new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false, + UID_N_MR1, false); + // modifying same object, don't need to call updateNotificationChannel + channel.setImportance(IMPORTANCE_NONE); + + NotificationChannel channel2 = + new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false, + UID_N_MR1, false); + + NotificationChannel channel3 = + new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false, + UID_N_MR1, false); + + Set<String> filter = ImmutableSet.of("id3"); + + NotificationChannelGroup actual = mHelper.getNotificationChannelGroups( + PKG_N_MR1, UID_N_MR1, false, true, false, true, filter).getList().get(0); + assertEquals(2, actual.getChannels().size()); + assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id3")).count()); + assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id2")).count()); + } + + @Test + public void testGetNotificationChannelGroups_withChannelFilter_doNotIncludeBlocked() { + NotificationChannel channel = + new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false, + UID_N_MR1, false); + // modifying same object, don't need to call updateNotificationChannel + channel.setImportance(IMPORTANCE_NONE); + + NotificationChannel channel2 = + new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false, + UID_N_MR1, false); + + NotificationChannel channel3 = + new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false, + UID_N_MR1, false); + + Set<String> filter = ImmutableSet.of("id3"); + + NotificationChannelGroup actual = mHelper.getNotificationChannelGroups( + PKG_N_MR1, UID_N_MR1, false, true, false, false, filter).getList().get(0); + assertEquals(1, actual.getChannels().size()); + assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id3")).count()); + assertEquals(0, actual.getChannels().stream().filter(c -> c.getId().equals("id2")).count()); + } + private static NotificationChannel cloneChannel(NotificationChannel original) { Parcel parcel = Parcel.obtain(); try { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 0494dfdfb1ec..f65cb93258bc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -147,14 +147,12 @@ import android.view.DisplayInfo; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner.Stub; import android.view.IWindowManager; -import android.view.IWindowSession; import android.view.InsetsSource; import android.view.InsetsState; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.Surface; import android.view.WindowManager; -import android.view.WindowManagerGlobal; import android.window.TaskSnapshot; import androidx.test.filters.MediumTest; @@ -2073,7 +2071,7 @@ public class ActivityRecordTests extends WindowTestsBase { WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); params.width = params.height = WindowManager.LayoutParams.MATCH_PARENT; final TestWindowState w = new TestWindowState( - mAtm.mWindowManager, mock(Session.class), new TestIWindow(), params, activity); + mAtm.mWindowManager, getTestSession(), new TestIWindow(), params, activity); activity.addWindow(w); // Assume the activity is launching in different rotation, and there was an available @@ -2083,23 +2081,8 @@ public class ActivityRecordTests extends WindowTestsBase { .build(); setRotatedScreenOrientationSilently(activity); activity.setVisible(false); - - final IWindowSession session = WindowManagerGlobal.getWindowSession(); - spyOn(session); - try { - // Return error to skip unnecessary operation. - doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay( - any() /* window */, any() /* attrs */, - anyInt() /* viewVisibility */, anyInt() /* displayId */, - anyInt() /* requestedVisibleTypes */, any() /* outInputChannel */, - any() /* outInsetsState */, any() /* outActiveControls */, - any() /* outAttachedFrame */, any() /* outSizeCompatScale */); - mAtm.mWindowManager.mStartingSurfaceController - .createTaskSnapshotSurface(activity, snapshot); - } catch (RemoteException ignored) { - } finally { - reset(session); - } + mAtm.mWindowManager.mStartingSurfaceController + .createTaskSnapshotSurface(activity, snapshot); // Because the rotation of snapshot and the corresponding top activity are different, fixed // rotation should be applied when creating snapshot surface if the display rotation may be diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index f30ecbe96c8b..8e7ba7030e82 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -337,6 +337,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { WindowState appWindow = task.getTopVisibleAppMainWindow(); WindowOnBackInvokedDispatcher dispatcher = new WindowOnBackInvokedDispatcher(context); + spyOn(appWindow.mSession); doAnswer(invocation -> { appWindow.setOnBackInvokedCallbackInfo(invocation.getArgument(1)); return null; diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index 1ad04a254f66..cd0389dce416 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -700,7 +700,7 @@ public class DisplayAreaTest extends WindowTestsBase { } private WindowState createWindowState(WindowToken token) { - return new WindowState(mWm, mock(Session.class), new TestIWindow(), token, + return new WindowState(mWm, getTestSession(), new TestIWindow(), token, null /* parentWindow */, 0 /* appOp */, new WindowManager.LayoutParams(), View.VISIBLE, 0 /* ownerId */, 0 /* showUserId */, false /* ownerCanAddInternalSystemWindow */); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index ae4ebc1223b2..c2b7fec4ff66 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -573,7 +573,10 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); // Add a window to the second display, and it should be focused - final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2"); + final ActivityRecord app2 = new ActivityBuilder(mAtm) + .setTask(new TaskBuilder(mSupervisor).setDisplay(dc).build()) + .setUseProcess(window1.getProcess()).setOnTop(true).build(); + final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, app2, "window2"); window2.mActivityRecord.mTargetSdk = targetSdk; updateFocusedWindow(); assertTrue(window2.isFocused()); @@ -1088,7 +1091,7 @@ public class DisplayContentTests extends WindowTestsBase { assertFalse(dc.getRotationReversionController().isAnyOverrideActive()); dc.getDisplayRotation().setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, - ROTATION_90); + ROTATION_90, /* caller= */ "DisplayContentTests"); updateAllDisplayContentAndRotation(dc); assertEquals(ROTATION_90, dc.getDisplayRotation() .rotationForOrientation(SCREEN_ORIENTATION_UNSPECIFIED, ROTATION_90)); @@ -1107,7 +1110,7 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(ROTATION_90, dc.getDisplayRotation() .rotationForOrientation(SCREEN_ORIENTATION_UNSPECIFIED, ROTATION_0)); dc.getDisplayRotation().setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, - ROTATION_0); + ROTATION_0, /* caller= */ "DisplayContentTests"); } @Test @@ -1134,7 +1137,8 @@ public class DisplayContentTests extends WindowTestsBase { dc.getDisplayRotation().setFixedToUserRotation( IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); dc.getDisplayRotation().setUserRotation( - WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_180); + WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_180, + /* caller= */ "DisplayContentTests"); final int newOrientation = getRotatedOrientation(dc); final Task task = new TaskBuilder(mSupervisor) @@ -1174,7 +1178,8 @@ public class DisplayContentTests extends WindowTestsBase { dc.getDisplayRotation().setFixedToUserRotation( IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); dc.getDisplayRotation().setUserRotation( - WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_0); + WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_0, + /* caller= */ "DisplayContentTests"); dc.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FULLSCREEN); final int newOrientation = getRotatedOrientation(dc); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index 915b387ddb75..e14568d281c0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -1265,7 +1265,7 @@ public class DisplayRotationTests { } private void freezeRotation(int rotation) { - mTarget.freezeRotation(rotation); + mTarget.freezeRotation(rotation, /* caller= */ "DisplayRotationTests"); if (mTarget.isDefaultDisplay) { mAccelerometerRotationObserver.onChange(false); @@ -1274,7 +1274,7 @@ public class DisplayRotationTests { } private void thawRotation() { - mTarget.thawRotation(); + mTarget.thawRotation(/* caller= */ "DisplayRotationTests"); if (mTarget.isDefaultDisplay) { mAccelerometerRotationObserver.onChange(false); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java index 30a89412d946..cf620fe605c6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java @@ -275,5 +275,10 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase { public boolean isEnteringPipAllowed(int uid) { return true; } + + @Override + public ComponentName getCustomHomeComponent() { + return null; + } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 4526d18d63d8..50fe0425fe9c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -55,7 +55,6 @@ import android.os.Parcelable; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.view.DragEvent; -import android.view.IWindowSessionCallback; import android.view.InputChannel; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -98,6 +97,7 @@ public class DragDropControllerTests extends WindowTestsBase { private static final String TEST_PACKAGE = "com.test.package"; private TestDragDropController mTarget; + private WindowProcessController mProcess; private WindowState mWindow; private IBinder mToken; @@ -137,10 +137,9 @@ public class DragDropControllerTests extends WindowTestsBase { * Creates a window state which can be used as a drop target. */ private WindowState createDropTargetWindow(String name, int ownerId) { - final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); - final Task rootTask = createTask(mDisplayContent); - final Task task = createTaskInRootTask(rootTask, ownerId); - task.addChild(activity, 0); + final Task task = new TaskBuilder(mSupervisor).setUserId(ownerId).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task) + .setUseProcess(mProcess).build(); // Use a new TestIWindow so we don't collect events for other windows final WindowState window = createWindow( @@ -167,6 +166,8 @@ public class DragDropControllerTests extends WindowTestsBase { @Before public void setUp() throws Exception { mTarget = new TestDragDropController(mWm, mWm.mH.getLooper()); + mProcess = mSystemServicesTestRule.addProcess(TEST_PACKAGE, "testProc", + TEST_PID, TEST_UID); mWindow = createDropTargetWindow("Drag test window", 0); doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0); when(mWm.mInputManager.transferTouchFocus(any(InputChannel.class), @@ -221,8 +222,6 @@ public class DragDropControllerTests extends WindowTestsBase { @Test public void testPrivateInterceptGlobalDragDropFlagChecksPermission() { - spyOn(mWm.mContext); - DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(); attrs.privateFlags |= PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; @@ -323,10 +322,7 @@ public class DragDropControllerTests extends WindowTestsBase { @Test public void testValidateAppActivityArguments() { - final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { - @Override - public void onAnimatorScaleChanged(float scale) {} - }); + final Session session = getTestSession(); try { session.validateAndResolveDragMimeTypeExtras( createClipDataForActivity(mock(PendingIntent.class), null), TEST_UID, TEST_PID, @@ -364,10 +360,7 @@ public class DragDropControllerTests extends WindowTestsBase { public void testValidateAppShortcutArguments() { doReturn(PERMISSION_GRANTED).when(mWm.mContext) .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS)); - final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { - @Override - public void onAnimatorScaleChanged(float scale) {} - }); + final Session session = createTestSession(mAtm); try { session.validateAndResolveDragMimeTypeExtras( createClipDataForShortcut(null, "test_shortcut_id", mock(UserHandle.class)), @@ -398,10 +391,7 @@ public class DragDropControllerTests extends WindowTestsBase { public void testValidateProfileAppShortcutArguments_notCallingUid() { doReturn(PERMISSION_GRANTED).when(mWm.mContext) .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS)); - final Session session = Mockito.spy(new Session(mWm, new IWindowSessionCallback.Stub() { - @Override - public void onAnimatorScaleChanged(float scale) {} - })); + final Session session = createTestSession(mAtm); final ShortcutServiceInternal shortcutService = mock(ShortcutServiceInternal.class); final Intent[] shortcutIntents = new Intent[1]; shortcutIntents[0] = new Intent(); @@ -443,10 +433,7 @@ public class DragDropControllerTests extends WindowTestsBase { public void testValidateAppTaskArguments() { doReturn(PERMISSION_GRANTED).when(mWm.mContext) .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS)); - final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { - @Override - public void onAnimatorScaleChanged(float scale) {} - }); + final Session session = createTestSession(mAtm); try { final ClipData clipData = new ClipData( new ClipDescription("drag", new String[] { MIMETYPE_APPLICATION_TASK }), @@ -462,10 +449,7 @@ public class DragDropControllerTests extends WindowTestsBase { @Test public void testValidateFlags() { - final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { - @Override - public void onAnimatorScaleChanged(float scale) {} - }); + final Session session = getTestSession(); try { session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION); fail("Expected failure without permission"); @@ -478,10 +462,7 @@ public class DragDropControllerTests extends WindowTestsBase { public void testValidateFlagsWithPermission() { doReturn(PERMISSION_GRANTED).when(mWm.mContext) .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS)); - final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { - @Override - public void onAnimatorScaleChanged(float scale) {} - }); + final Session session = createTestSession(mAtm); try { session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION); // Expected pass @@ -571,7 +552,8 @@ public class DragDropControllerTests extends WindowTestsBase { assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(), new InputChannel(), true /* isDragDrop */)); - mToken = mTarget.performDrag(0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0, data); + mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient, + flag, surface, 0, 0, 0, 0, 0, data); assertNotNull(mToken); r.run(); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 8f68c0fa0b73..1ceb1a8c7097 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -4739,24 +4739,20 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(new Rect(1050, 0, 1750, 1400), mActivity.getBounds()); } - private static WindowState addWindowToActivity(ActivityRecord activity) { + private WindowState addWindowToActivity(ActivityRecord activity) { final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; params.setFitInsetsSides(0); params.setFitInsetsTypes(0); final TestWindowState w = new TestWindowState( - activity.mWmService, mock(Session.class), new TestIWindow(), params, activity); + activity.mWmService, getTestSession(), new TestIWindow(), params, activity); makeWindowVisible(w); w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN; activity.addWindow(w); return w; } - private static TestWindowState addStatusBar(DisplayContent displayContent) { - final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); - doReturn(true).when(displayPolicy).hasStatusBar(); - displayPolicy.onConfigurationChanged(); - + private TestWindowState addStatusBar(DisplayContent displayContent) { final TestWindowToken token = createTestWindowToken( TYPE_STATUS_BAR, displayContent); final WindowManager.LayoutParams attrs = @@ -4772,11 +4768,12 @@ public class SizeCompatTests extends WindowTestsBase { new InsetsFrameProvider(owner, 0, WindowInsets.Type.mandatorySystemGestures()) }; final TestWindowState statusBar = new TestWindowState( - displayContent.mWmService, mock(Session.class), new TestIWindow(), attrs, token); + displayContent.mWmService, getTestSession(), new TestIWindow(), attrs, token); token.addWindow(statusBar); statusBar.setRequestedSize(displayContent.mBaseDisplayWidth, SystemBarUtils.getStatusBarHeight(displayContent.getDisplayUiContext())); + final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); displayPolicy.addWindowLw(statusBar, attrs); displayPolicy.layoutWindowLw(statusBar, null, displayContent.mDisplayFrames); return statusBar; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index fb27d6368a7e..0c580697bc4a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -610,7 +610,7 @@ public class TaskTests extends WindowTestsBase { // display. final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); - dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); + dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0, /* caller= */ "TaskTests"); final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); @@ -642,7 +642,7 @@ public class TaskTests extends WindowTestsBase { // Setting app to fixed landscape and changing display top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); // Fix the display orientation to portrait which is 90 degrees for the test display. - dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90); + dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90, /* caller= */ "TaskTests"); // Fixed orientation request should be resolved on activity level. Task fills display // bounds. @@ -681,7 +681,7 @@ public class TaskTests extends WindowTestsBase { // display. final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); - dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); + dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0, /* caller= */ "TaskTests"); final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 76576f76355d..e86fc366a631 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -82,7 +82,6 @@ import android.platform.test.annotations.Presubmit; import android.util.MergedConfiguration; import android.view.ContentRecordingSession; import android.view.IWindow; -import android.view.IWindowSessionCallback; import android.view.InputChannel; import android.view.InsetsSourceControl; import android.view.InsetsState; @@ -496,11 +495,7 @@ public class WindowManagerServiceTests extends WindowTestsBase { spyOn(mWm.mWindowContextListenerController); final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD, mDefaultDisplay); - final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { - @Override - public void onAnimatorScaleChanged(float v) throws RemoteException { - } - }); + final Session session = getTestSession(); final WindowManager.LayoutParams params = new WindowManager.LayoutParams( TYPE_APPLICATION_ATTACHED_DIALOG); params.token = windowToken.token; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 0ddd3135506e..014d57dd9970 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -95,7 +95,6 @@ import android.util.ArraySet; import android.util.MergedConfiguration; import android.view.Gravity; import android.view.IWindow; -import android.view.IWindowSessionCallback; import android.view.InputWindowHandle; import android.view.InsetsSource; import android.view.InsetsSourceControl; @@ -1332,12 +1331,7 @@ public class WindowStateTests extends WindowTestsBase { final WindowToken windowToken = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent); final IWindow client = new TestIWindow(); - final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { - @Override - public void onAnimatorScaleChanged(float v) throws RemoteException { - - } - }); + final Session session = getTestSession(); final ClientWindowFrames outFrames = new ClientWindowFrames(); final MergedConfiguration outConfig = new MergedConfiguration(); final SurfaceControl outSurfaceControl = new SurfaceControl(); 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 99688dabda6e..9146889e37d9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -94,6 +94,7 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.view.IDisplayWindowInsetsController; import android.view.IWindow; +import android.view.IWindowSessionCallback; import android.view.InsetsFrameProvider; import android.view.InsetsSourceControl; import android.view.InsetsState; @@ -153,7 +154,7 @@ class WindowTestsBase extends SystemServiceTestsBase { ActivityTaskSupervisor mSupervisor; WindowManagerService mWm; private final IWindow mIWindow = new TestIWindow(); - private Session mMockSession; + private Session mTestSession; private boolean mUseFakeSettingsProvider; DisplayInfo mDisplayInfo = new DisplayInfo(); @@ -231,7 +232,6 @@ class WindowTestsBase extends SystemServiceTestsBase { suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget()); mTransaction = mSystemServicesTestRule.mTransaction; - mMockSession = mock(Session.class); mContext.getSystemService(DisplayManager.class) .getDisplay(Display.DEFAULT_DISPLAY).getDisplayInfo(mDisplayInfo); @@ -508,6 +508,54 @@ class WindowTestsBase extends SystemServiceTestsBase { return statusBar; } + Session getTestSession() { + if (mTestSession != null) { + return mTestSession; + } + mTestSession = createTestSession(mAtm); + return mTestSession; + } + + private Session getTestSession(WindowToken token) { + final ActivityRecord r = token.asActivityRecord(); + if (r == null || r.app == null) { + return getTestSession(); + } + // If the activity has a process, let the window session belonging to activity use the + // process of the activity. + int pid = r.app.getPid(); + if (pid == 0) { + // See SystemServicesTestRule#addProcess, pid 0 isn't added to the map. So generate + // a non-zero pid to initialize it. + final int numPid = mAtm.mProcessMap.getPidMap().size(); + pid = numPid > 0 ? mAtm.mProcessMap.getPidMap().keyAt(numPid - 1) + 1 : 1; + r.app.setPid(pid); + mAtm.mProcessMap.put(pid, r.app); + } else { + final WindowState win = mRootWindowContainer.getWindow(w -> w.getProcess() == r.app); + if (win != null) { + // Reuse the same Session if there is a window uses the same process. + return win.mSession; + } + } + return createTestSession(mAtm, pid, r.getUid()); + } + + static Session createTestSession(ActivityTaskManagerService atms) { + return createTestSession(atms, WindowManagerService.MY_PID, WindowManagerService.MY_UID); + } + + static Session createTestSession(ActivityTaskManagerService atms, int pid, int uid) { + if (atms.mProcessMap.getProcess(pid) == null) { + SystemServicesTestRule.addProcess(atms, "testPkg", "testProc", pid, uid); + } + return new Session(atms.mWindowManager, new IWindowSessionCallback.Stub() { + @Override + public void onAnimatorScaleChanged(float scale) { + } + }, pid, uid); + } + WindowState createAppWindow(Task task, int type, String name) { final ActivityRecord activity = createNonAttachedActivityRecord(task.getDisplayContent()); task.addChild(activity, 0); @@ -587,7 +635,7 @@ class WindowTestsBase extends SystemServiceTestsBase { WindowState createWindow(WindowState parent, int type, WindowToken token, String name, int ownerId, boolean ownerCanAddInternalSystemWindow, IWindow iwindow) { return createWindow(parent, type, token, name, ownerId, UserHandle.getUserId(ownerId), - ownerCanAddInternalSystemWindow, mWm, mMockSession, iwindow, + ownerCanAddInternalSystemWindow, mWm, getTestSession(token), iwindow, mSystemServicesTestRule.getPowerManagerWrapper()); } @@ -891,7 +939,7 @@ class WindowTestsBase extends SystemServiceTestsBase { TestWindowState createWindowState(WindowManager.LayoutParams attrs, WindowToken token) { SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock); - return new TestWindowState(mWm, mMockSession, mIWindow, attrs, token); + return new TestWindowState(mWm, getTestSession(), mIWindow, attrs, token); } /** Creates a {@link DisplayContent} as parts of simulate display info for test. */ @@ -1705,8 +1753,7 @@ class WindowTestsBase extends SystemServiceTestsBase { final WindowState window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, activity, "Starting window", 0 /* ownerId */, 0 /* userId*/, - false /* internalWindows */, mWMService, mock(Session.class), - iWindow, + false /* internalWindows */, mWMService, createTestSession(mAtm), iWindow, mPowerManagerWrapper); activity.mStartingWindow = window; mAppWindowMap.put(info.appToken, window); diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index b028b474e96b..a8a90170ae4d 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -243,7 +243,7 @@ public class UsageStatsDatabase { try { for (int i = start; i < fileCount - 1; i++) { final IntervalStats stats = new IntervalStats(); - readLocked(files.valueAt(i), stats); + readLocked(files.valueAt(i), stats, false); if (!checkinAction.checkin(stats)) { return false; } @@ -542,7 +542,8 @@ public class UsageStatsDatabase { } try { IntervalStats stats = new IntervalStats(); - readLocked(new AtomicFile(files[j]), stats, version, mPackagesTokenData); + readLocked(new AtomicFile(files[j]), stats, version, mPackagesTokenData, + false); // Upgrade to version 5+. // Future version upgrades should add additional logic here to upgrade. if (mCurrentVersion >= 5) { @@ -602,7 +603,8 @@ public class UsageStatsDatabase { try { final IntervalStats stats = new IntervalStats(); final AtomicFile atomicFile = new AtomicFile(files[j]); - if (!readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData)) { + if (!readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData, + false)) { continue; // no data was omitted when read so no need to rewrite } // Any data related to packages that have been removed would have failed @@ -648,7 +650,7 @@ public class UsageStatsDatabase { try { final IntervalStats stats = new IntervalStats(); final AtomicFile atomicFile = new AtomicFile(files[j]); - readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData); + readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData, false); if (!pruneStats(installedPackages, stats)) { continue; // no stats were pruned so no need to rewrite } @@ -755,7 +757,7 @@ public class UsageStatsDatabase { try { final AtomicFile f = mSortedStatFiles[intervalType].valueAt(fileCount - 1); IntervalStats stats = new IntervalStats(); - readLocked(f, stats); + readLocked(f, stats, false); return stats; } catch (Exception e) { Slog.e(TAG, "Failed to read usage stats file", e); @@ -821,7 +823,7 @@ public class UsageStatsDatabase { */ @Nullable public <T> List<T> queryUsageStats(int intervalType, long beginTime, long endTime, - StatCombiner<T> combiner) { + StatCombiner<T> combiner, boolean skipEvents) { // mIntervalDirs is final. Accessing its size without holding the lock should be fine. if (intervalType < 0 || intervalType >= mIntervalDirs.length) { throw new IllegalArgumentException("Bad interval type " + intervalType); @@ -875,7 +877,7 @@ public class UsageStatsDatabase { } try { - readLocked(f, stats); + readLocked(f, stats, skipEvents); if (beginTime < stats.endTime && !combiner.combine(stats, false, results)) { break; @@ -990,7 +992,7 @@ public class UsageStatsDatabase { try { final AtomicFile af = new AtomicFile(f); final IntervalStats stats = new IntervalStats(); - readLocked(af, stats); + readLocked(af, stats, false); final int pkgCount = stats.packageStats.size(); for (int i = 0; i < pkgCount; i++) { UsageStats pkgStats = stats.packageStats.valueAt(i); @@ -1014,7 +1016,7 @@ public class UsageStatsDatabase { private static long parseBeginTime(File file) throws IOException { String name = file.getName(); - // Parse out the digits from the the front of the file name + // Parse out the digits from the front of the file name for (int i = 0; i < name.length(); i++) { final char c = name.charAt(i); if (c < '0' || c > '9') { @@ -1092,13 +1094,13 @@ public class UsageStatsDatabase { * method. It is up to the caller to ensure that this is the desired behavior - if not, the * caller should ensure that the data in the reused object is being cleared. */ - private void readLocked(AtomicFile file, IntervalStats statsOut) + private void readLocked(AtomicFile file, IntervalStats statsOut, boolean skipEvents) throws IOException, RuntimeException { if (mCurrentVersion <= 3) { Slog.wtf(TAG, "Reading UsageStats as XML; current database version: " + mCurrentVersion); } - readLocked(file, statsOut, mCurrentVersion, mPackagesTokenData); + readLocked(file, statsOut, mCurrentVersion, mPackagesTokenData, skipEvents); } /** @@ -1109,13 +1111,14 @@ public class UsageStatsDatabase { * caller should ensure that the data in the reused object is being cleared. */ private static boolean readLocked(AtomicFile file, IntervalStats statsOut, int version, - PackagesTokenData packagesTokenData) throws IOException, RuntimeException { + PackagesTokenData packagesTokenData, boolean skipEvents) + throws IOException, RuntimeException { boolean dataOmitted = false; try { FileInputStream in = file.openRead(); try { statsOut.beginTime = parseBeginTime(file); - dataOmitted = readLocked(in, statsOut, version, packagesTokenData); + dataOmitted = readLocked(in, statsOut, version, packagesTokenData, skipEvents); statsOut.lastTimeSaved = file.getLastModifiedTime(); } finally { try { @@ -1139,7 +1142,7 @@ public class UsageStatsDatabase { * caller should ensure that the data in the reused object is being cleared. */ private static boolean readLocked(InputStream in, IntervalStats statsOut, int version, - PackagesTokenData packagesTokenData) throws RuntimeException { + PackagesTokenData packagesTokenData, boolean skipEvents) throws RuntimeException { boolean dataOmitted = false; switch (version) { case 1: @@ -1161,7 +1164,7 @@ public class UsageStatsDatabase { break; case 5: try { - UsageStatsProtoV2.read(in, statsOut); + UsageStatsProtoV2.read(in, statsOut, skipEvents); } catch (Exception e) { Slog.e(TAG, "Unable to read interval stats from proto.", e); } @@ -1436,7 +1439,7 @@ public class UsageStatsDatabase { throws IOException { IntervalStats stats = new IntervalStats(); try { - readLocked(statsFile, stats); + readLocked(statsFile, stats, false); } catch (IOException e) { Slog.e(TAG, "Failed to read usage stats file", e); out.writeInt(0); @@ -1481,7 +1484,7 @@ public class UsageStatsDatabase { IntervalStats stats = new IntervalStats(); try { stats.beginTime = in.readLong(); - readLocked(in, stats, version, mPackagesTokenData); + readLocked(in, stats, version, mPackagesTokenData, false); } catch (Exception e) { Slog.d(TAG, "DeSerializing IntervalStats Failed", e); stats = null; @@ -1579,7 +1582,7 @@ public class UsageStatsDatabase { synchronized (mLock) { final IntervalStats stats = new IntervalStats(); try { - readLocked(mSortedStatFiles[interval].get(fileName, null), stats); + readLocked(mSortedStatFiles[interval].get(fileName, null), stats, false); return stats; } catch (Exception e) { return null; diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java index 076eaf84de6a..81387471f4d6 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java +++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java @@ -438,7 +438,8 @@ final class UsageStatsProtoV2 { * @param in the input stream from which to read events. * @param stats the interval stats object which will be populated. */ - public static void read(InputStream in, IntervalStats stats) throws IOException { + public static void read(InputStream in, IntervalStats stats, boolean skipEvents) + throws IOException { final ProtoInputStream proto = new ProtoInputStream(in); while (true) { switch (proto.nextField()) { @@ -492,6 +493,9 @@ final class UsageStatsProtoV2 { } break; case (int) IntervalStatsObfuscatedProto.EVENT_LOG: + if (skipEvents) { + break; + } try { final long eventsToken = proto.start( IntervalStatsObfuscatedProto.EVENT_LOG); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index f93c70388129..58b5ae556f19 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1896,6 +1896,18 @@ public class UsageStatsService extends SystemService implements mUserState.valueAt(i).dump(idpw, pkgs, compact); idpw.println(); } + } else { + final LinkedList<Event> pendingEvents = mReportedEvents.get(userId); + if (pendingEvents != null && !pendingEvents.isEmpty()) { + final int eventCount = pendingEvents.size(); + idpw.println("Pending events: count=" + eventCount); + idpw.increaseIndent(); + for (int idx = 0; idx < eventCount; idx++) { + UserUsageStatsService.printEvent(idpw, pendingEvents.get(idx), true); + } + idpw.decreaseIndent(); + idpw.println(); + } } idpw.decreaseIndent(); } diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index b5d8096deac0..ddb27969dd0a 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -448,7 +448,7 @@ class UserUsageStatsService { */ @Nullable private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime, - StatCombiner<T> combiner) { + StatCombiner<T> combiner, boolean skipEvents) { if (intervalType == INTERVAL_BEST) { intervalType = mDatabase.findBestFitBucket(beginTime, endTime); if (intervalType < 0) { @@ -488,7 +488,7 @@ class UserUsageStatsService { // Get the stats from disk. List<T> results = mDatabase.queryUsageStats(intervalType, beginTime, - truncatedEndTime, combiner); + truncatedEndTime, combiner, skipEvents); if (DEBUG) { Slog.d(TAG, "Got " + (results != null ? results.size() : 0) + " results from disk"); Slog.d(TAG, "Current stats beginTime=" + currentStats.beginTime + @@ -518,21 +518,21 @@ class UserUsageStatsService { if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) { return null; } - return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner); + return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner, true); } List<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime) { if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) { return null; } - return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner); + return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner, true); } List<EventStats> queryEventStats(int bucketType, long beginTime, long endTime) { if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) { return null; } - return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner); + return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner, true); } UsageEvents queryEvents(final long beginTime, final long endTime, int flags) { @@ -587,7 +587,7 @@ class UserUsageStatsService { } return true; } - }); + }, false); if (results == null || results.isEmpty()) { return null; @@ -637,7 +637,7 @@ class UserUsageStatsService { } } return true; - }); + }, false); if (results == null || results.isEmpty()) { return null; @@ -684,7 +684,7 @@ class UserUsageStatsService { accumulatedResult.add(event); } return true; - }); + }, false); if (results == null || results.isEmpty()) { return null; @@ -770,7 +770,7 @@ class UserUsageStatsService { } } return true; - }); + }, false); if (results == null || results.isEmpty()) { // There won't be any new events added earlier than endTime, so we can use endTime to @@ -1059,7 +1059,7 @@ class UserUsageStatsService { return Long.toString(elapsedTime); } - void printEvent(IndentingPrintWriter pw, Event event, boolean prettyDates) { + static void printEvent(IndentingPrintWriter pw, Event event, boolean prettyDates) { pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates)); pw.printPair("type", eventToString(event.mEventType)); pw.printPair("package", event.mPackage); @@ -1124,7 +1124,7 @@ class UserUsageStatsService { } return true; } - }); + }, false); pw.print("Last 24 hour events ("); if (prettyDates) { diff --git a/tests/BinderLeakTest/Android.bp b/tests/BinderLeakTest/Android.bp new file mode 100644 index 000000000000..78b0ede76d4e --- /dev/null +++ b/tests/BinderLeakTest/Android.bp @@ -0,0 +1,40 @@ +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"], +} + +filegroup { + name: "binder_leak_test_aidl", + srcs: ["**/*.aidl"], + path: "aidl", +} + +java_defaults { + name: "BinderTest.defaults", + srcs: [ + "**/*.java", + ":binder_leak_test_aidl", + ], + static_libs: [ + "androidx.test.ext.junit", + "androidx.test.rules", + "androidx.test.runner", + ], +} + +// Built with target_sdk_version: current +android_test { + name: "BinderLeakTest", + defaults: ["BinderTest.defaults"], +} + +// Built with target_sdk_version: 33 +android_test { + name: "BinderLeakTest_legacy", + defaults: ["BinderTest.defaults"], + manifest: "AndroidManifest_legacy.xml", +} diff --git a/tests/BinderLeakTest/AndroidManifest.xml b/tests/BinderLeakTest/AndroidManifest.xml new file mode 100644 index 000000000000..756def7ac29d --- /dev/null +++ b/tests/BinderLeakTest/AndroidManifest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.binder"> + <application> + <service + android:name=".MyService" + android:enabled="true" + android:exported="true" + android:process=":service"> + </service> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.test.binder" + android:label="Binder leak test"> + </instrumentation> +</manifest> diff --git a/tests/BinderLeakTest/AndroidManifest_legacy.xml b/tests/BinderLeakTest/AndroidManifest_legacy.xml new file mode 100644 index 000000000000..03d1dfd1fd83 --- /dev/null +++ b/tests/BinderLeakTest/AndroidManifest_legacy.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.binder"> + <uses-sdk android:minSdkVersion="33" + android:targetSdkVersion="33" + android:maxSdkVersion="33" /> + <application> + <service + android:name=".MyService" + android:enabled="true" + android:exported="true" + android:process=":service"> + </service> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.test.binder" + android:label="Binder leak test"> + </instrumentation> +</manifest> diff --git a/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl b/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl new file mode 100644 index 000000000000..a721959d19b4 --- /dev/null +++ b/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl @@ -0,0 +1,5 @@ +package com.android.test.binder; + +interface IFoo { + +} diff --git a/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl b/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl new file mode 100644 index 000000000000..b487f51f812c --- /dev/null +++ b/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl @@ -0,0 +1,10 @@ +package com.android.test.binder; +import com.android.test.binder.IFoo; + +interface IFooProvider { + IFoo createFoo(); + + boolean isFooGarbageCollected(); + + oneway void killProcess(); +} diff --git a/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java b/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java new file mode 100644 index 000000000000..f07317f7d5f3 --- /dev/null +++ b/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java @@ -0,0 +1,153 @@ +/* + * 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.test.binder; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import android.content.Intent; +import android.os.Build; +import android.os.IBinder; +import android.os.RemoteException; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ServiceTestRule; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.TimeoutException; + +@RunWith(AndroidJUnit4.class) +public class BinderTest { + @Rule + public final ServiceTestRule serviceRule = new ServiceTestRule(); + + @Test + public void testDeathRecipientLeaksOrNot() + throws RemoteException, TimeoutException, InterruptedException { + Intent intent = new Intent(ApplicationProvider.getApplicationContext(), MyService.class); + IFooProvider provider = IFooProvider.Stub.asInterface(serviceRule.bindService(intent)); + FooHolder holder = new FooHolder(provider.createFoo()); + + // ref will get enqueued right after holder is finalized for gc. + ReferenceQueue<FooHolder> refQueue = new ReferenceQueue<>(); + PhantomReference<FooHolder> ref = new PhantomReference<>(holder, refQueue); + + DeathRecorder deathRecorder = new DeathRecorder(); + holder.registerDeathRecorder(deathRecorder); + + if (getSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { + ///////////////////////////////////////////// + // New behavior + // + // Reference chain at this moment: + // holder --(java strong ref)--> FooHolder + // FooHolder.mProxy --(java strong ref)--> IFoo.Proxy + // IFoo.Proxy.mRemote --(java strong ref)--> BinderProxy + // BinderProxy --(binder ref)--> Foo.Stub + // In other words, the variable "holder" is the root of the reference chain. + + // By setting the variable to null, we make FooHolder, IFoo.Proxy, BinderProxy, and even + // Foo.Stub unreachable. + holder = null; + + // Ensure that the objects are garbage collected + forceGc(); + assertEquals(ref, refQueue.poll()); + assertTrue(provider.isFooGarbageCollected()); + + // The binder has died, but we don't get notified since the death recipient is GC'ed. + provider.killProcess(); + Thread.sleep(1000); // give some time for the service process to die and reaped + assertFalse(deathRecorder.deathRecorded); + } else { + ///////////////////////////////////////////// + // Legacy behavior + // + // Reference chain at this moment: + // JavaDeathRecipient --(JNI strong ref)--> FooHolder + // holder --(java strong ref)--> FooHolder + // FooHolder.mProxy --(java strong ref)--> IFoo.Proxy + // IFoo.Proxy.mRemote --(java strong ref)--> BinderProxy + // BinderProxy --(binder ref)--> Foo.Stub + // So, BOTH JavaDeathRecipient and holder are roots of the reference chain. + + // Even if we set holder to null, it doesn't make other objects unreachable; they are + // still reachable via the JNI strong ref. + holder = null; + + // Check that objects are not garbage collected + forceGc(); + assertNotEquals(ref, refQueue.poll()); + assertFalse(provider.isFooGarbageCollected()); + + // The legacy behavior is getting notified even when there's no reference + provider.killProcess(); + Thread.sleep(1000); // give some time for the service process to die and reaped + assertTrue(deathRecorder.deathRecorded); + } + } + + static class FooHolder implements IBinder.DeathRecipient { + private IFoo mProxy; + private DeathRecorder mDeathRecorder; + + FooHolder(IFoo proxy) throws RemoteException { + proxy.asBinder().linkToDeath(this, 0); + + // A strong reference from DeathRecipient(this) to the binder proxy is created here + mProxy = proxy; + } + + public void registerDeathRecorder(DeathRecorder dr) { + mDeathRecorder = dr; + } + + @Override + public void binderDied() { + if (mDeathRecorder != null) { + mDeathRecorder.deathRecorded = true; + } + } + } + + static class DeathRecorder { + public boolean deathRecorded = false; + } + + // Try calling System.gc() until an orphaned object is confirmed to be finalized + private static void forceGc() { + Object obj = new Object(); + ReferenceQueue<Object> refQueue = new ReferenceQueue<>(); + PhantomReference<Object> ref = new PhantomReference<>(obj, refQueue); + obj = null; // make it an orphan + while (refQueue.poll() != ref) { + System.gc(); + } + } + + private static int getSdkVersion() { + return ApplicationProvider.getApplicationContext().getApplicationInfo().targetSdkVersion; + } +} diff --git a/tests/BinderLeakTest/java/com/android/test/binder/MyService.java b/tests/BinderLeakTest/java/com/android/test/binder/MyService.java new file mode 100644 index 000000000000..c701253446f4 --- /dev/null +++ b/tests/BinderLeakTest/java/com/android/test/binder/MyService.java @@ -0,0 +1,63 @@ +/* + * 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.test.binder; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; + +public class MyService extends Service { + @Override + public IBinder onBind(Intent intent) { + return new IFooProvider.Stub() { + ReferenceQueue<IFoo> mRefQueue = new ReferenceQueue<>(); + PhantomReference<IFoo> mRef; + + @Override + public IFoo createFoo() throws RemoteException { + IFoo binder = new IFoo.Stub() {}; + mRef = new PhantomReference<>(binder, mRefQueue); + return binder; + } + + @Override + public boolean isFooGarbageCollected() throws RemoteException { + forceGc(); + return mRefQueue.poll() == mRef; + } + + @Override + public void killProcess() throws RemoteException { + android.os.Process.killProcess(android.os.Process.myPid()); + } + }; + } + + private static void forceGc() { + Object obj = new Object(); + ReferenceQueue<Object> refQueue = new ReferenceQueue<>(); + PhantomReference<Object> ref = new PhantomReference<>(obj, refQueue); + obj = null; // make it an orphan + while (refQueue.poll() != ref) { + System.gc(); + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt index ee22d0732eac..badd7c8c712c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt @@ -50,7 +50,6 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest(bugId = 298544839) class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp1 = SimpleAppHelper(instrumentation) private val testApp2 = NonResizeableAppHelper(instrumentation) @@ -92,6 +91,7 @@ class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTes * Checks that the transition starts with [testApp1]'s windows filling/covering exactly the * entirety of the display. */ + @FlakyTest(bugId = 298544839) @Test open fun startsWithApp1WindowsCoverFullScreen() { flicker.assertWmStart { @@ -120,6 +120,7 @@ class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTes * Checks that [testApp2] windows fill the entire screen (i.e. is "fullscreen") at the end of * the transition once we have fully quick switched from [testApp1] back to the [testApp2]. */ + @FlakyTest(bugId = 298544839) @Test open fun endsWithApp2WindowsCoveringFullScreen() { flicker.assertWmEnd { this.visibleRegion(testApp2).coversExactly(startDisplayBounds) } @@ -129,6 +130,7 @@ class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTes * Checks that [testApp2] layers fill the entire screen (i.e. is "fullscreen") at the end of the * transition once we have fully quick switched from [testApp1] back to the [testApp2]. */ + @FlakyTest(bugId = 298544839) @Test open fun endsWithApp2LayersCoveringFullScreen() { flicker.assertLayersEnd { @@ -141,6 +143,7 @@ class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTes * Checks that [testApp2] is the top window at the end of the transition once we have fully * quick switched from [testApp1] back to the [testApp2]. */ + @FlakyTest(bugId = 298544839) @Test open fun endsWithApp2BeingOnTop() { flicker.assertWmEnd { this.isAppWindowOnTop(testApp2) } @@ -165,6 +168,7 @@ class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTes * Checks that [testApp2]'s layer starts off invisible and becomes visible at some point before * the end of the transition and then stays visible until the end of the transition. */ + @FlakyTest(bugId = 298544839) @Test open fun app2LayerBecomesAndStaysVisible() { flicker.assertLayers { this.isInvisible(testApp2).then().isVisible(testApp2) } @@ -174,6 +178,7 @@ class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTes * Checks that [testApp1]'s window starts off visible and becomes invisible at some point before * the end of the transition and then stays invisible until the end of the transition. */ + @FlakyTest(bugId = 298544839) @Test open fun app1WindowBecomesAndStaysInvisible() { flicker.assertWm { this.isAppWindowVisible(testApp1).then().isAppWindowInvisible(testApp1) } @@ -193,6 +198,7 @@ class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTes * Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially * visible. */ + @FlakyTest(bugId = 298544839) @Test open fun app2WindowIsVisibleOnceApp1WindowIsInvisible() { flicker.assertWm { @@ -211,6 +217,7 @@ class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTes * Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially * visible. */ + @FlakyTest(bugId = 298544839) @Test open fun app2LayerIsVisibleOnceApp1LayerIsInvisible() { flicker.assertLayers { @@ -225,6 +232,7 @@ class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTes } /** {@inheritDoc} */ + @FlakyTest(bugId = 298544839) @Test override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() @@ -233,31 +241,45 @@ class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTes @Test override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() + @FlakyTest(bugId = 298544839) @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() - @Test override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() + @FlakyTest(bugId = 298544839) + @Test + override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() - @Test override fun entireScreenCovered() = super.entireScreenCovered() + @FlakyTest(bugId = 298544839) + @Test + override fun entireScreenCovered() = super.entireScreenCovered() + @FlakyTest(bugId = 298544839) @Test override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd() + @FlakyTest(bugId = 298544839) @Test override fun navBarWindowIsVisibleAtStartAndEnd() = super.navBarWindowIsVisibleAtStartAndEnd() + @FlakyTest(bugId = 298544839) @Test override fun statusBarLayerIsVisibleAtStartAndEnd() = super.statusBarLayerIsVisibleAtStartAndEnd() + @FlakyTest(bugId = 298544839) @Test override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() - @Test override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() + @FlakyTest(bugId = 298544839) + @Test + override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() - @Test override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() + @FlakyTest(bugId = 298544839) + @Test + override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() + @FlakyTest(bugId = 298544839) @Test override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = super.visibleWindowsShownMoreThanOneConsecutiveEntry() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt index 030b29269e48..b3c9c96c614c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt @@ -31,7 +31,8 @@ import org.junit.runner.RunWith @RunWith(FlickerServiceJUnit4ClassRunner::class) class CloseAppBackButton3ButtonLandscape : CloseAppBackButton(NavBar.MODE_3BUTTON, Rotation.ROTATION_90) { - @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) + // TODO: Missing CUJ (b/300078127) + @ExpectedScenarios(["ENTIRE_TRACE"]) @Test override fun closeAppBackButtonTest() = super.closeAppBackButtonTest() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt index 770da143b184..29c11a4257f6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt @@ -31,7 +31,8 @@ import org.junit.runner.RunWith @RunWith(FlickerServiceJUnit4ClassRunner::class) class CloseAppBackButton3ButtonPortrait : CloseAppBackButton(NavBar.MODE_3BUTTON, Rotation.ROTATION_0) { - @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) + // TODO: Missing CUJ (b/300078127) + @ExpectedScenarios(["ENTIRE_TRACE"]) @Test override fun closeAppBackButtonTest() = super.closeAppBackButtonTest() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt index 4b2206d78276..1bb4a253ff41 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt @@ -31,7 +31,8 @@ import org.junit.runner.RunWith @RunWith(FlickerServiceJUnit4ClassRunner::class) class CloseAppBackButtonGesturalNavLandscape : CloseAppBackButton(NavBar.MODE_GESTURAL, Rotation.ROTATION_90) { - @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) + // TODO: Missing CUJ (b/300078127) + @ExpectedScenarios(["ENTIRE_TRACE"]) @Test override fun closeAppBackButtonTest() = super.closeAppBackButtonTest() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt index 54b471e5ab28..17cb6e194ec3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt @@ -31,7 +31,8 @@ import org.junit.runner.RunWith @RunWith(FlickerServiceJUnit4ClassRunner::class) class CloseAppBackButtonGesturalNavPortrait : CloseAppBackButton(NavBar.MODE_GESTURAL, Rotation.ROTATION_0) { - @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) + // TODO: Missing CUJ (b/300078127) + @ExpectedScenarios(["ENTIRE_TRACE"]) @Test override fun closeAppBackButtonTest() = super.closeAppBackButtonTest() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt index 93130c4680ff..a1708fe3a4b6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt @@ -19,6 +19,7 @@ package com.android.server.wm.flicker.service.quickswitch.scenarios import android.app.Instrumentation import android.tools.common.NavBar import android.tools.common.Rotation +import android.tools.device.flicker.rules.ChangeDisplayOrientationRule import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.tapl.LauncherInstrumentation @@ -46,7 +47,9 @@ abstract class QuickSwitchBetweenTwoAppsBack(val rotation: Rotation = Rotation.R tapl.setExpectedRotation(rotation.value) tapl.setIgnoreTaskbarVisibility(true) testApp1.launchViaIntent(wmHelper) + ChangeDisplayOrientationRule.setRotation(rotation) testApp2.launchViaIntent(wmHelper) + ChangeDisplayOrientationRule.setRotation(rotation) } @Test diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index f867c989232d..9198ae184b18 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -102,6 +102,20 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> + <activity android:name=".PortraitImmersiveActivity" + android:taskAffinity="com.android.server.wm.flicker.testapp.PortraitImmersiveActivity" + android:immersive="true" + android:resizeableActivity="true" + android:screenOrientation="portrait" + android:theme="@android:style/Theme.NoTitleBar" + android:configChanges="screenSize" + android:label="PortraitImmersiveActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> <activity android:name=".LaunchTransparentActivity" android:resizeableActivity="false" android:screenOrientation="portrait" diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java index 7c5e9a3f86b5..8b334c0a8588 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java @@ -73,6 +73,12 @@ public class ActivityOptions { FLICKER_APP_PACKAGE + ".NonResizeablePortraitActivity"); } + public static class PortraitImmersiveActivity { + public static final String LABEL = "PortraitImmersiveActivity"; + public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE, + FLICKER_APP_PACKAGE + ".PortraitImmersiveActivity"); + } + public static class TransparentActivity { public static final String LABEL = "TransparentActivity"; public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE, diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitImmersiveActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitImmersiveActivity.java new file mode 100644 index 000000000000..0a7f81cba747 --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitImmersiveActivity.java @@ -0,0 +1,20 @@ +/* + * 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.wm.flicker.testapp; + +public class PortraitImmersiveActivity extends GameActivity { +} diff --git a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der Binary files differindex 235bd4797b78..fd888ec600cd 100644 --- a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der +++ b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der diff --git a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem index 413e3c07d2ab..66f7bfd21289 100644 --- a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem +++ b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem @@ -1,35 +1,31 @@ -----BEGIN CERTIFICATE----- -MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT -MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0 -aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw -WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE -AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m -OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu -T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c -JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR -Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz -PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm -aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM -TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g -LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO -BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv -dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB -AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL -NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W -b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG -A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz -cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 -MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV -BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt -YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN -ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE -BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is -I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G -CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i -2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ -2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ +MIIFYjCCBEqgAwIBAgIQd70NbNs2+RrqIQ/E8FjTDTANBgkqhkiG9w0BAQsFADBX +MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UE +CxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTIwMDYx +OTAwMDA0MloXDTI4MDEyODAwMDA0MlowRzELMAkGA1UEBhMCVVMxIjAgBgNVBAoT +GUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBMTEMxFDASBgNVBAMTC0dUUyBSb290IFIx +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAthECix7joXebO9y/lD63 +ladAPKH9gvl9MgaCcfb2jH/76Nu8ai6Xl6OMS/kr9rH5zoQdsfnFl97vufKj6bwS +iV6nqlKr+CMny6SxnGPb15l+8Ape62im9MZaRw1NEDPjTrETo8gYbEvs/AmQ351k +KSUjB6G00j0uYODP0gmHu81I8E3CwnqIiru6z1kZ1q+PsAewnjHxgsHA3y6mbWwZ +DrXYfiYaRQM9sHmklCitD38m5agI/pboPGiUU+6DOogrFZYJsuB6jC511pzrp1Zk +j5ZPaK49l8KEj8C8QMALXL32h7M1bKwYUH+E4EzNktMg6TO8UpmvMrUpsyUqtEj5 +cuHKZPfmghCN6J3Cioj6OGaK/GP5Afl4/Xtcd/p2h/rs37EOeZVXtL0m79YB0esW +CruOC7XFxYpVq9Os6pFLKcwZpDIlTirxZUTQAs6qzkm06p98g7BAe+dDq6dso499 +iYH6TKX/1Y7DzkvgtdizjkXPdsDtQCv9Uw+wp9U7DbGKogPeMa3Md+pvez7W35Ei +Eua++tgy/BBjFFFy3l3WFpO9KWgz7zpm7AeKJt8T11dleCfeXkkUAKIAf5qoIbap +sZWwpbkNFhHax2xIPEDgfg1azVY80ZcFuctL7TlLnMQ/0lUTbiSw1nH69MG6zO0b +9f6BQdgAmD06yK56mDcYBZUCAwEAAaOCATgwggE0MA4GA1UdDwEB/wQEAwIBhjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTkrysmcRorSCeFL1JmLO/wiRNxPjAf +BgNVHSMEGDAWgBRge2YaRQ2XyolQL30EzTSo//z9SzBgBggrBgEFBQcBAQRUMFIw +JQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnBraS5nb29nL2dzcjEwKQYIKwYBBQUH +MAKGHWh0dHA6Ly9wa2kuZ29vZy9nc3IxL2dzcjEuY3J0MDIGA1UdHwQrMCkwJ6Al +oCOGIWh0dHA6Ly9jcmwucGtpLmdvb2cvZ3NyMS9nc3IxLmNybDA7BgNVHSAENDAy +MAgGBmeBDAECATAIBgZngQwBAgIwDQYLKwYBBAHWeQIFAwIwDQYLKwYBBAHWeQIF +AwMwDQYJKoZIhvcNAQELBQADggEBADSkHrEoo9C0dhemMXoh6dFSPsjbdBZBiLg9 +NR3t5P+T4Vxfq7vqfM/b5A3Ri1fyJm9bvhdGaJQ3b2t6yMAYN/olUazsaL+yyEn9 +WprKASOshIArAoyZl+tJaox118fessmXn1hIVw41oeQa1v1vg4Fv74zPl6/AhSrw +9U5pCZEt4Wi4wStz6dTZ/CLANx8LZh1J7QJVj2fhMtfTJr9w4z30Z209fOU0iOMy ++qduBmpvvYuR7hZL6Dupszfnw0Skfths18dG9ZKb59UhvmaSGZRVbNQpsg3BZlvi +d0lIKO2d1xozclOzgjXPYovJJIultzkMu34qQb9Sz/yilrbCgj8= -----END CERTIFICATE----- diff --git a/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml b/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml index 5d23d36e1dbf..99106ad37783 100644 --- a/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml +++ b/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml @@ -5,7 +5,7 @@ </domain> <domain> developer.android.com </domain> <pin-set> - <pin digest="SHA-256"> 7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y= </pin> + <pin digest="SHA-256"> zCTnfLwLKbS9S2sbp+uFz4KZOocFvXxkV06Ce9O5M2w= </pin> </pin-set> </domain-config> </network-security-config> diff --git a/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml b/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml index d45fd77a5f0f..232f88ff6cc9 100644 --- a/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml +++ b/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml @@ -9,7 +9,7 @@ <domain-config> <domain>developer.android.com</domain> <pin-set> - <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin> + <pin digest="SHA-256">zCTnfLwLKbS9S2sbp+uFz4KZOocFvXxkV06Ce9O5M2w=</pin> </pin-set> </domain-config> </domain-config> diff --git a/tests/NetworkSecurityConfigTest/res/xml/pins1.xml b/tests/NetworkSecurityConfigTest/res/xml/pins1.xml index 1773d28094a3..7cc81b0101f1 100644 --- a/tests/NetworkSecurityConfigTest/res/xml/pins1.xml +++ b/tests/NetworkSecurityConfigTest/res/xml/pins1.xml @@ -3,7 +3,7 @@ <domain-config> <domain>android.com</domain> <pin-set> - <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin> + <pin digest="SHA-256">zCTnfLwLKbS9S2sbp+uFz4KZOocFvXxkV06Ce9O5M2w=</pin> </pin-set> </domain-config> </network-security-config> diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java index 047be162e642..0494f174f191 100644 --- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java +++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java @@ -22,23 +22,17 @@ import android.os.Build; import android.test.ActivityUnitTestCase; import android.util.ArraySet; import android.util.Pair; + +import com.android.org.conscrypt.TrustedCertificateStore; + import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.net.Socket; -import java.net.URL; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.Set; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.TrustManager; -import com.android.org.conscrypt.TrustedCertificateStore; +import javax.net.ssl.SSLContext; public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { @@ -46,9 +40,9 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { super(Activity.class); } - // SHA-256 of the G2 intermediate CA for android.com (as of 10/2015). - private static final byte[] G2_SPKI_SHA256 - = hexToBytes("ec722969cb64200ab6638f68ac538e40abab5b19a6485661042a1061c4612776"); + // SHA-256 of the GTS intermediate CA (CN = GTS CA 1C3) for android.com (as of 09/2023). + private static final byte[] GTS_INTERMEDIATE_SPKI_SHA256 = + hexToBytes("cc24e77cbc0b29b4bd4b6b1ba7eb85cf82993a8705bd7c64574e827bd3b9336c"); private static final byte[] TEST_CA_BYTES = hexToBytes( @@ -161,7 +155,7 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { public void testGoodPin() throws Exception { ArraySet<Pin> pins = new ArraySet<Pin>(); - pins.add(new Pin("SHA-256", G2_SPKI_SHA256)); + pins.add(new Pin("SHA-256", GTS_INTERMEDIATE_SPKI_SHA256)); NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder() .setPinSet(new PinSet(pins, Long.MAX_VALUE)) .addCertificatesEntryRef( @@ -247,7 +241,7 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { public void testWithUrlConnection() throws Exception { ArraySet<Pin> pins = new ArraySet<Pin>(); - pins.add(new Pin("SHA-256", G2_SPKI_SHA256)); + pins.add(new Pin("SHA-256", GTS_INTERMEDIATE_SPKI_SHA256)); NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder() .setPinSet(new PinSet(pins, Long.MAX_VALUE)) .addCertificatesEntryRef( @@ -304,7 +298,7 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { } finally { // Delete the user added CA. We don't know the alias so just delete them all. for (String alias : store.aliases()) { - if (store.isUser(alias)) { + if (TrustedCertificateStore.isUser(alias)) { try { store.deleteCertificateEntry(alias); } catch (Exception ignored) { diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java index 9dec21be7f37..39b5cb4c4f0d 100644 --- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java +++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java @@ -16,19 +16,20 @@ package android.security.net.config; +import static org.junit.Assert.fail; + import android.content.pm.ApplicationInfo; import android.os.Build; -import java.net.Socket; + import java.net.URL; + import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.TrustManager; +import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManagerFactory; -import junit.framework.Assert; - -public final class TestUtils extends Assert { +public final class TestUtils { private TestUtils() { } @@ -36,8 +37,8 @@ public final class TestUtils extends Assert { public static void assertConnectionFails(SSLContext context, String host, int port) throws Exception { try { - Socket s = context.getSocketFactory().createSocket(host, port); - s.getInputStream(); + SSLSocket s = (SSLSocket) context.getSocketFactory().createSocket(host, port); + s.startHandshake(); fail("Expected connection to " + host + ":" + port + " to fail."); } catch (SSLHandshakeException expected) { } @@ -45,7 +46,8 @@ public final class TestUtils extends Assert { public static void assertConnectionSucceeds(SSLContext context, String host, int port) throws Exception { - Socket s = context.getSocketFactory().createSocket(host, port); + SSLSocket s = (SSLSocket) context.getSocketFactory().createSocket(host, port); + s.startHandshake(); s.getInputStream(); } diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java index 4b7a014f25dc..81e05c1d4e42 100644 --- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java +++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java @@ -16,26 +16,18 @@ package android.security.net.config; -import android.content.Context; import android.content.pm.ApplicationInfo; import android.test.AndroidTestCase; import android.test.MoreAsserts; -import android.util.ArraySet; -import android.util.Pair; + import java.io.IOException; import java.net.InetAddress; -import java.net.Socket; -import java.net.URL; import java.security.KeyStore; import java.security.Provider; -import java.security.Security; import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collections; import java.util.Set; -import javax.net.ssl.HttpsURLConnection; + import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; @@ -52,7 +44,7 @@ public class XmlConfigTests extends AndroidTestCase { NetworkSecurityConfig config = appConfig.getConfigForHostname(""); assertNotNull(config); // Check defaults. - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); assertFalse(config.getTrustAnchors().isEmpty()); PinSet pinSet = config.getPins(); @@ -72,7 +64,7 @@ public class XmlConfigTests extends AndroidTestCase { NetworkSecurityConfig config = appConfig.getConfigForHostname(""); assertNotNull(config); // Check defaults. - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); assertTrue(config.getTrustAnchors().isEmpty()); PinSet pinSet = config.getPins(); @@ -91,14 +83,14 @@ public class XmlConfigTests extends AndroidTestCase { NetworkSecurityConfig config = appConfig.getConfigForHostname(""); assertNotNull(config); // Check defaults. - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); assertTrue(config.getTrustAnchors().isEmpty()); PinSet pinSet = config.getPins(); assertTrue(pinSet.pins.isEmpty()); // Check android.com. config = appConfig.getConfigForHostname("android.com"); - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); assertFalse(config.getTrustAnchors().isEmpty()); pinSet = config.getPins(); @@ -188,7 +180,7 @@ public class XmlConfigTests extends AndroidTestCase { ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); assertFalse(config.getTrustAnchors().isEmpty()); PinSet pinSet = config.getPins(); @@ -250,9 +242,9 @@ public class XmlConfigTests extends AndroidTestCase { ApplicationConfig appConfig = new ApplicationConfig(source); // Check android.com. NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); - assertEquals(2, config.getTrustAnchors().size()); + assertEquals(1, config.getTrustAnchors().size()); // Try connections. SSLContext context = TestUtils.getSSLContext(source); TestUtils.assertConnectionSucceeds(context, "android.com", 443); @@ -267,9 +259,9 @@ public class XmlConfigTests extends AndroidTestCase { ApplicationConfig appConfig = new ApplicationConfig(source); // Check android.com. NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); - assertEquals(2, config.getTrustAnchors().size()); + assertEquals(1, config.getTrustAnchors().size()); // Try connections. SSLContext context = TestUtils.getSSLContext(source); TestUtils.assertConnectionSucceeds(context, "android.com", 443); diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java index f695cbd5daf9..5160df8e9b6d 100644 --- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java +++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java @@ -122,7 +122,7 @@ public class UsageStatsDatabasePerfTest { while (benchmarkState.keepRunning(elapsedTimeNs)) { final long startTime = SystemClock.elapsedRealtimeNanos(); List<UsageEvents.Event> temp = sUsageStatsDatabase.queryUsageStats( - UsageStatsManager.INTERVAL_DAILY, 0, 2, sUsageStatsCombiner); + UsageStatsManager.INTERVAL_DAILY, 0, 2, sUsageStatsCombiner, false); final long endTime = SystemClock.elapsedRealtimeNanos(); elapsedTimeNs = endTime - startTime; assertEquals(packageCount * eventsPerPackage, temp.size()); diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index 06cbeb5368a5..330bc84d7a76 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -124,7 +124,7 @@ public class WindowManagerPermissionTests extends TestCase { @SmallTest public void testSET_ORIENTATION() { try { - mWm.freezeRotation(-1); + mWm.freezeRotation(/* rotation= */ -1, /* caller= */ "WindowManagerPermissionTests"); fail("IWindowManager.freezeRotation did not throw SecurityException as" + " expected"); } catch (SecurityException e) { @@ -134,7 +134,7 @@ public class WindowManagerPermissionTests extends TestCase { } try { - mWm.thawRotation(); + mWm.thawRotation(/* called= */ "WindowManagerPermissionTests"); fail("IWindowManager.thawRotation did not throw SecurityException as" + " expected"); } catch (SecurityException e) { diff --git a/tests/utils/testutils/TEST_MAPPING b/tests/utils/testutils/TEST_MAPPING index d9eb44fc4222..6468d8cc99a6 100644 --- a/tests/utils/testutils/TEST_MAPPING +++ b/tests/utils/testutils/TEST_MAPPING @@ -4,7 +4,7 @@ "name": "frameworks-base-testutils-tests", "options": [ { - "exclude-annotation": "android.platform.test.annotations.LargeTest" + "exclude-annotation": "androidx.test.filters.LargeTest" }, { "exclude-annotation": "android.platform.test.annotations.FlakyTest" diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt index 1aa485963f97..0c1d88ad0ff9 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt @@ -13,11 +13,11 @@ public interface android.hosttest.annotation.HostSideTestClassLoadHook extends j } SourceFile: "HostSideTestClassLoadHook.java" RuntimeVisibleAnnotations: - 0: #x(#x=[e#x.#x]) + x: #x(#x=[e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE] ) - 1: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -33,11 +33,11 @@ public interface android.hosttest.annotation.HostSideTestKeep extends java.lang. } SourceFile: "HostSideTestKeep.java" RuntimeVisibleAnnotations: - 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] ) - 1: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -56,11 +56,11 @@ public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass } SourceFile: "HostSideTestNativeSubstitutionClass.java" RuntimeVisibleAnnotations: - 0: #x(#x=[e#x.#x]) + x: #x(#x=[e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE] ) - 1: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -76,11 +76,11 @@ public interface android.hosttest.annotation.HostSideTestRemove extends java.lan } SourceFile: "HostSideTestRemove.java" RuntimeVisibleAnnotations: - 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] ) - 1: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -96,11 +96,11 @@ public interface android.hosttest.annotation.HostSideTestStub extends java.lang. } SourceFile: "HostSideTestStub.java" RuntimeVisibleAnnotations: - 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] ) - 1: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -119,11 +119,11 @@ public interface android.hosttest.annotation.HostSideTestSubstitute extends java } SourceFile: "HostSideTestSubstitute.java" RuntimeVisibleAnnotations: - 0: #x(#x=[e#x.#x]) + x: #x(#x=[e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.METHOD] ) - 1: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -139,11 +139,11 @@ public interface android.hosttest.annotation.HostSideTestThrow extends java.lang } SourceFile: "HostSideTestThrow.java" RuntimeVisibleAnnotations: - 0: #x(#x=[e#x.#x,e#x.#x]) + x: #x(#x=[e#x.#x,e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] ) - 1: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -159,11 +159,11 @@ public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends } SourceFile: "HostSideTestWholeClassKeep.java" RuntimeVisibleAnnotations: - 0: #x(#x=[e#x.#x]) + x: #x(#x=[e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE] ) - 1: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -179,11 +179,11 @@ public interface android.hosttest.annotation.HostSideTestWholeClassStub extends } SourceFile: "HostSideTestWholeClassStub.java" RuntimeVisibleAnnotations: - 0: #x(#x=[e#x.#x]) + x: #x(#x=[e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE] ) - 1: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -199,7 +199,7 @@ public interface android.hosttest.annotation.tests.HostSideTestSuppress extends } SourceFile: "HostSideTestSuppress.java" RuntimeVisibleAnnotations: - 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x]) + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD] ) @@ -217,9 +217,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl flags: (0x0002) ACC_PRIVATE Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -230,11 +230,11 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=0, args_size=0 - 0: iconst_1 - 1: ireturn + x: iconst_1 + x: ireturn LineNumberTable: RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestKeep public static int getOneStub(); @@ -242,11 +242,11 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=0, args_size=0 - 0: iconst_1 - 1: ireturn + x: iconst_1 + x: ireturn LineNumberTable: RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub } SourceFile: "TinyFrameworkCallerCheck.java" @@ -267,9 +267,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -280,8 +280,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=0, args_size=0 - 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I - 3: ireturn + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I + x: ireturn LineNumberTable: public static int getOne_noCheck(); @@ -289,13 +289,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=0, args_size=0 - 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I - 3: ireturn + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I + x: ireturn LineNumberTable: } SourceFile: "TinyFrameworkCallerCheck.java" RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl @@ -314,14 +314,14 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota descriptor: I flags: (0x0001) ACC_PUBLIC RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub public int keep; descriptor: I flags: (0x0001) ACC_PUBLIC RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestKeep public int remove; @@ -333,21 +333,21 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: aload_0 - 5: iconst_1 - 6: putfield #x // Field stub:I - 9: aload_0 - 10: iconst_2 - 11: putfield #x // Field keep:I - 14: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_1 + x: putfield #x // Field stub:I + x: aload_0 + x: iconst_2 + x: putfield #x // Field keep:I + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub public int addOne(int); @@ -355,17 +355,17 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: iload_1 - 2: invokevirtual #x // Method addOneInner:(I)I - 5: ireturn + x: aload_0 + x: iload_1 + x: invokevirtual #x // Method addOneInner:(I)I + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; 0 6 1 value I RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub public int addOneInner(int); @@ -373,17 +373,17 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: iload_1 - 1: iconst_1 - 2: iadd - 3: ireturn + x: iload_1 + x: iconst_1 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; 0 4 1 value I RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestKeep public void toBeRemoved(java.lang.String); @@ -391,17 +391,17 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V - 7: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + x: athrow LineNumberTable: LocalVariableTable: Start Length Slot Name Signature 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; 0 8 1 foo Ljava/lang/String; RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestRemove public int addTwo(int); @@ -409,20 +409,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String not supported on host side - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String not supported on host side + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow LineNumberTable: LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; 0 10 1 value I RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub - 1: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestSubstitute( suffix="_host" ) @@ -432,10 +432,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: iload_1 - 1: iconst_2 - 2: iadd - 3: ireturn + x: iload_1 + x: iconst_2 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -446,9 +446,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota descriptor: (I)I flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub - 1: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestSubstitute( suffix="_host" ) @@ -458,10 +458,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 - 0: iload_0 - 1: iconst_3 - 2: iadd - 3: ireturn + x: iload_0 + x: iconst_3 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -472,14 +472,14 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: ldc #x // String This value shouldn\'t be seen on the host side. - 2: areturn + x: ldc #x // String This value shouldn\'t be seen on the host side. + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestThrow public java.lang.String visibleButUsesUnsupportedMethod(); @@ -487,22 +487,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; - 4: areturn + x: aload_0 + x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub } SourceFile: "TinyFrameworkClassAnnotations.java" RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub - 1: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) @@ -532,15 +532,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: aload_0 - 5: iconst_1 - 6: putfield #x // Field stub:I - 9: aload_0 - 10: iconst_2 - 11: putfield #x // Field keep:I - 14: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_1 + x: putfield #x // Field stub:I + x: aload_0 + x: iconst_2 + x: putfield #x // Field keep:I + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -551,10 +551,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: iload_1 - 2: invokevirtual #x // Method addOneInner:(I)I - 5: ireturn + x: aload_0 + x: iload_1 + x: invokevirtual #x // Method addOneInner:(I)I + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -566,10 +566,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: iload_1 - 1: iconst_1 - 2: iadd - 3: ireturn + x: iload_1 + x: iconst_1 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -581,10 +581,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V - 7: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + x: athrow LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -596,20 +596,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String not supported on host side - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String not supported on host side + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow LineNumberTable: LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; 0 10 1 value I RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub - 1: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestSubstitute( suffix="_host" ) @@ -619,10 +619,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: iload_1 - 1: iconst_2 - 2: iadd - 3: ireturn + x: iload_1 + x: iconst_2 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -633,9 +633,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW descriptor: (I)I flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub - 1: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestSubstitute( suffix="_host" ) @@ -645,10 +645,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 - 0: iload_0 - 1: iconst_3 - 2: iadd - 3: ireturn + x: iload_0 + x: iconst_3 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -659,8 +659,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: ldc #x // String This value shouldn\'t be seen on the host side. - 2: areturn + x: ldc #x // String This value shouldn\'t be seen on the host side. + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -671,9 +671,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; - 4: areturn + x: aload_0 + x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -681,7 +681,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW } SourceFile: "TinyFrameworkClassClassWideAnnotations.java" RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class Compiled from "TinyFrameworkClassLoadHook.java" @@ -702,9 +702,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo flags: (0x0002) ACC_PRIVATE Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -715,11 +715,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 - 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set; - 3: aload_0 - 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z - 9: pop - 10: return + x: getstatic #x // Field sLoadedClasses:Ljava/util/Set; + x: aload_0 + x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z + x: pop + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -734,16 +734,16 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo flags: (0x0008) ACC_STATIC Code: stack=2, locals=0, args_size=0 - 0: new #x // class java/util/HashSet - 3: dup - 4: invokespecial #x // Method java/util/HashSet."<init>":()V - 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set; - 10: return + x: new #x // class java/util/HashSet + x: dup + x: invokespecial #x // Method java/util/HashSet."<init>":()V + x: putstatic #x // Field sLoadedClasses:Ljava/util/Set; + x: return LineNumberTable: } SourceFile: "TinyFrameworkClassLoadHook.java" RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class Compiled from "TinyFrameworkClassWithInitializer.java" @@ -763,9 +763,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -776,18 +776,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn flags: (0x0008) ACC_STATIC Code: stack=1, locals=0, args_size=0 - 0: iconst_1 - 1: putstatic #x // Field sInitialized:Z - 4: return + x: iconst_1 + x: putstatic #x // Field sInitialized:Z + x: return LineNumberTable: } SourceFile: "TinyFrameworkClassWithInitializer.java" RuntimeInvisibleAnnotations: - 0: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) - 1: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class Compiled from "TinyFrameworkExceptionTester.java" @@ -803,9 +803,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -816,18 +816,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=1, args_size=0 - 0: new #x // class java/lang/IllegalStateException - 3: dup - 4: ldc #x // String Inner exception - 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V - 9: athrow - 10: astore_0 - 11: new #x // class java/lang/RuntimeException - 14: dup - 15: ldc #x // String Outer exception - 17: aload_0 - 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V - 21: athrow + x: new #x // class java/lang/IllegalStateException + x: dup + x: ldc #x // String Inner exception + x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V + x: athrow + x: astore_0 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Outer exception + x: aload_0 + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V + x: athrow Exception table: from to target type 0 10 10 Class java/lang/Exception @@ -841,7 +841,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe } SourceFile: "TinyFrameworkExceptionTester.java" RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class Compiled from "TinyFrameworkForTextPolicy.java" @@ -869,15 +869,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: aload_0 - 5: iconst_1 - 6: putfield #x // Field stub:I - 9: aload_0 - 10: iconst_2 - 11: putfield #x // Field keep:I - 14: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_1 + x: putfield #x // Field stub:I + x: aload_0 + x: iconst_2 + x: putfield #x // Field keep:I + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -888,10 +888,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: iload_1 - 2: invokevirtual #x // Method addOneInner:(I)I - 5: ireturn + x: aload_0 + x: iload_1 + x: invokevirtual #x // Method addOneInner:(I)I + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -903,10 +903,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: iload_1 - 1: iconst_1 - 2: iadd - 3: ireturn + x: iload_1 + x: iconst_1 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -918,10 +918,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V - 7: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + x: athrow LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -933,11 +933,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String not supported on host side - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String not supported on host side + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -949,10 +949,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: iload_1 - 1: iconst_2 - 2: iadd - 3: ireturn + x: iload_1 + x: iconst_2 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -968,10 +968,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 - 0: iload_0 - 1: iconst_3 - 2: iadd - 3: ireturn + x: iload_0 + x: iconst_3 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -982,8 +982,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: ldc #x // String This value shouldn\'t be seen on the host side. - 2: areturn + x: ldc #x // String This value shouldn\'t be seen on the host side. + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -994,9 +994,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; - 4: areturn + x: aload_0 + x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1017,9 +1017,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1034,9 +1034,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=1, args_size=1 - 0: iload_0 - 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I - 4: ireturn + x: iload_0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1051,10 +1051,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=4, args_size=2 - 0: lload_0 - 1: lload_2 - 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J - 5: lreturn + x: lload_0 + x: lload_2 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + x: lreturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1063,9 +1063,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative } SourceFile: "TinyFrameworkNative.java" RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub - 1: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestNativeSubstitutionClass( value="TinyFrameworkNative_host" ) @@ -1083,9 +1083,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1096,10 +1096,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 - 0: iload_0 - 1: iconst_2 - 2: iadd - 3: ireturn + x: iload_0 + x: iconst_2 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1110,10 +1110,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=4, args_size=2 - 0: lload_0 - 1: lload_2 - 2: ladd - 3: lreturn + x: lload_0 + x: lload_2 + x: ladd + x: lreturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1122,7 +1122,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host } SourceFile: "TinyFrameworkNative_host.java" RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassKeep ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class Compiled from "TinyFrameworkNestedClasses.java" @@ -1142,12 +1142,12 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex flags: (0x0000) Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: aload_1 - 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; - 5: aload_0 - 6: invokespecial #x // Method java/lang/Object."<init>":()V - 9: return + x: aload_0 + x: aload_1 + x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1159,9 +1159,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: iconst_1 - 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; - 4: areturn + x: iconst_1 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1172,9 +1172,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokevirtual #x // Method get:()Ljava/lang/Integer; - 4: areturn + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1200,9 +1200,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex flags: (0x0000) Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1213,9 +1213,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: iconst_2 - 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; - 4: areturn + x: iconst_2 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1226,9 +1226,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokevirtual #x // Method get:()Ljava/lang/Integer; - 4: areturn + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1258,12 +1258,12 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex flags: (0x0000) Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: aload_1 - 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; - 5: aload_0 - 6: invokespecial #x // Method java/lang/Object."<init>":()V - 9: return + x: aload_0 + x: aload_1 + x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1275,9 +1275,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: iconst_3 - 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; - 4: areturn + x: iconst_3 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1288,9 +1288,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokevirtual #x // Method get:()Ljava/lang/Integer; - 4: areturn + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1316,9 +1316,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex flags: (0x0000) Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1329,9 +1329,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: iconst_4 - 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; - 4: areturn + x: iconst_4 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1342,9 +1342,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokevirtual #x // Method get:()Ljava/lang/Integer; - 4: areturn + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1374,12 +1374,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: aload_0 - 5: iload_1 - 6: putfield #x // Field value:I - 9: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iload_1 + x: putfield #x // Field value:I + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1412,15 +1412,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: aload_1 - 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; - 5: aload_0 - 6: invokespecial #x // Method java/lang/Object."<init>":()V - 9: aload_0 - 10: iconst_5 - 11: putfield #x // Field value:I - 14: return + x: aload_0 + x: aload_1 + x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_5 + x: putfield #x // Field value:I + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1429,7 +1429,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass } SourceFile: "TinyFrameworkNestedClasses.java" RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses InnerClasses: @@ -1448,9 +1448,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat flags: (0x0000) Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1461,9 +1461,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: bipush 7 - 2: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; - 5: areturn + x: bipush 7 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1474,9 +1474,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokevirtual #x // Method get:()Ljava/lang/Integer; - 4: areturn + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1507,12 +1507,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: aload_0 - 5: bipush 6 - 7: putfield #x // Field value:I - 10: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: bipush 6 + x: putfield #x // Field value:I + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1523,16 +1523,16 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=0, args_size=0 - 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 - 3: dup - 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V - 7: areturn + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V + x: areturn LineNumberTable: Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; } SourceFile: "TinyFrameworkNestedClasses.java" RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses InnerClasses: @@ -1552,10 +1552,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: iload_1 - 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V - 5: return + x: aload_0 + x: iload_1 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1591,15 +1591,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: aload_0 - 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 - 8: dup - 9: aload_0 - 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V - 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier; - 16: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + x: dup + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + x: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1610,11 +1610,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 - 3: dup - 4: aload_0 - 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V - 8: areturn + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + x: dup + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1626,10 +1626,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=0, args_size=0 - 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 - 3: dup - 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V - 7: areturn + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V + x: areturn LineNumberTable: Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; @@ -1638,16 +1638,16 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0008) ACC_STATIC Code: stack=2, locals=0, args_size=0 - 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 - 3: dup - 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V - 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; - 10: return + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V + x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + x: return LineNumberTable: } SourceFile: "TinyFrameworkNestedClasses.java" RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt index 6e1528aedc1e..43ceec42660d 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt @@ -12,33 +12,33 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl flags: (0x0002) ACC_PRIVATE Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public static int getOneStub(); descriptor: ()I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=0, args_size=0 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub } InnerClasses: private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck SourceFile: "TinyFrameworkCallerCheck.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class @@ -55,44 +55,44 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public static int getOne_withCheck(); descriptor: ()I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=0, args_size=0 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public static int getOne_noCheck(); descriptor: ()I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=0, args_size=0 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow } InnerClasses: private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck SourceFile: "TinyFrameworkCallerCheck.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl @@ -109,7 +109,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota descriptor: I flags: (0x0001) ACC_PUBLIC RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); @@ -117,13 +117,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub public int addOne(int); @@ -131,13 +131,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub public int addTwo(int); @@ -145,47 +145,47 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public static int nativeAddThree(int); descriptor: (I)I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public java.lang.String visibleButUsesUnsupportedMethod(); descriptor: ()Ljava/lang/String; flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub } SourceFile: "TinyFrameworkClassAnnotations.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub - 1: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) @@ -215,97 +215,97 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public int addOne(int); descriptor: (I)I flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public int addOneInner(int); descriptor: (I)I flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public void toBeRemoved(java.lang.String); descriptor: (Ljava/lang/String;)V flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public int addTwo(int); descriptor: (I)I flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public static int nativeAddThree(int); descriptor: (I)I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public java.lang.String unsupportedMethod(); descriptor: ()Ljava/lang/String; flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public java.lang.String visibleButUsesUnsupportedMethod(); descriptor: ()Ljava/lang/String; flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow } SourceFile: "TinyFrameworkClassClassWideAnnotations.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class Compiled from "TinyFrameworkClassLoadHook.java" @@ -326,22 +326,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo flags: (0x0002) ACC_PRIVATE Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public static void onClassLoaded(java.lang.Class<?>); descriptor: (Ljava/lang/Class;)V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow Signature: #x // (Ljava/lang/Class<*>;)V static {}; @@ -349,20 +349,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo flags: (0x0008) ACC_STATIC Code: stack=3, locals=0, args_size=0 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow } SourceFile: "TinyFrameworkClassLoadHook.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class Compiled from "TinyFrameworkClassWithInitializer.java" @@ -382,35 +382,35 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow static {}; descriptor: ()V flags: (0x0008) ACC_STATIC Code: stack=3, locals=0, args_size=0 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow } SourceFile: "TinyFrameworkClassWithInitializer.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) - 1: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class Compiled from "TinyFrameworkExceptionTester.java" @@ -426,31 +426,31 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public static int testException(); descriptor: ()I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=0, args_size=0 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow } SourceFile: "TinyFrameworkExceptionTester.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class Compiled from "TinyFrameworkForTextPolicy.java" @@ -470,61 +470,61 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public int addOne(int); descriptor: (I)I flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public int addTwo(int); descriptor: (I)I flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public static int nativeAddThree(int); descriptor: (I)I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public java.lang.String visibleButUsesUnsupportedMethod(); descriptor: ()Ljava/lang/String; flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow } SourceFile: "TinyFrameworkForTextPolicy.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class Compiled from "TinyFrameworkNative.java" @@ -540,11 +540,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public static native int nativeAddTwo(int); descriptor: (I)I @@ -555,11 +555,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public static native long nativeLongPlus(long, long); descriptor: (JJ)J @@ -570,22 +570,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=4, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow } SourceFile: "TinyFrameworkNative.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub - 1: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestNativeSubstitutionClass( value="TinyFrameworkNative_host" ) @@ -607,19 +607,19 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow } InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class @@ -644,22 +644,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow } InnerClasses: public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class @@ -680,22 +680,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); descriptor: ()Ljava/util/function/Supplier; flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=0, args_size=0 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; } InnerClasses: @@ -703,12 +703,12 @@ InnerClasses: #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class @@ -725,20 +725,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow } InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class @@ -765,22 +765,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public java.util.function.Supplier<java.lang.Integer> getSupplier(); descriptor: ()Ljava/util/function/Supplier; flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); @@ -788,11 +788,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=0, args_size=0 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; static {}; @@ -800,11 +800,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0008) ACC_STATIC Code: stack=3, locals=0, args_size=0 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: ldc #x // String Stub! - 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 9: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow } InnerClasses: #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 @@ -818,12 +818,12 @@ InnerClasses: #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt index 5672e9c36706..faf0a46c8fab 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt @@ -13,13 +13,13 @@ public interface android.hosttest.annotation.HostSideTestClassLoadHook extends j } SourceFile: "HostSideTestClassLoadHook.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass - 1: #x(#x=[e#x.#x]) + x: #x(#x=[e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE] ) - 2: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -35,13 +35,13 @@ public interface android.hosttest.annotation.HostSideTestKeep extends java.lang. } SourceFile: "HostSideTestKeep.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass - 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] ) - 2: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -60,13 +60,13 @@ public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass } SourceFile: "HostSideTestNativeSubstitutionClass.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass - 1: #x(#x=[e#x.#x]) + x: #x(#x=[e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE] ) - 2: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -82,13 +82,13 @@ public interface android.hosttest.annotation.HostSideTestRemove extends java.lan } SourceFile: "HostSideTestRemove.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass - 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] ) - 2: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -104,13 +104,13 @@ public interface android.hosttest.annotation.HostSideTestStub extends java.lang. } SourceFile: "HostSideTestStub.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass - 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] ) - 2: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -129,13 +129,13 @@ public interface android.hosttest.annotation.HostSideTestSubstitute extends java } SourceFile: "HostSideTestSubstitute.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass - 1: #x(#x=[e#x.#x]) + x: #x(#x=[e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.METHOD] ) - 2: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -151,13 +151,13 @@ public interface android.hosttest.annotation.HostSideTestThrow extends java.lang } SourceFile: "HostSideTestThrow.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass - 1: #x(#x=[e#x.#x,e#x.#x]) + x: #x(#x=[e#x.#x,e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] ) - 2: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -173,13 +173,13 @@ public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends } SourceFile: "HostSideTestWholeClassKeep.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass - 1: #x(#x=[e#x.#x]) + x: #x(#x=[e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE] ) - 2: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -195,13 +195,13 @@ public interface android.hosttest.annotation.HostSideTestWholeClassStub extends } SourceFile: "HostSideTestWholeClassStub.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass - 1: #x(#x=[e#x.#x]) + x: #x(#x=[e#x.#x]) java.lang.annotation.Target( value=[Ljava/lang/annotation/ElementType;.TYPE] ) - 2: #x(#x=e#x.#x) + x: #x(#x=e#x.#x) java.lang.annotation.Retention( value=Ljava/lang/annotation/RetentionPolicy;.CLASS ) @@ -219,9 +219,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl flags: (0x0002) ACC_PRIVATE Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -232,17 +232,17 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=0, args_size=0 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - 2: ldc #x // String getOneKeep - 4: ldc #x // String ()I - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: iconst_1 - 16: ireturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + x: ldc #x // String getOneKeep + x: ldc #x // String ()I + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iconst_1 + x: ireturn LineNumberTable: RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestKeep public static int getOneStub(); @@ -250,20 +250,20 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=0, args_size=0 - 0: iconst_1 - 1: ireturn + x: iconst_1 + x: ireturn LineNumberTable: RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub } InnerClasses: private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck SourceFile: "TinyFrameworkCallerCheck.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class @@ -280,9 +280,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -293,8 +293,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=0, args_size=0 - 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I - 3: ireturn + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I + x: ireturn LineNumberTable: public static int getOne_noCheck(); @@ -302,20 +302,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=0, args_size=0 - 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I - 3: ireturn + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I + x: ireturn LineNumberTable: } InnerClasses: private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck SourceFile: "TinyFrameworkCallerCheck.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl @@ -332,14 +332,14 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota descriptor: I flags: (0x0001) ACC_PUBLIC RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub public int keep; descriptor: I flags: (0x0001) ACC_PUBLIC RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestKeep private static {}; @@ -347,31 +347,31 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x000a) ACC_PRIVATE, ACC_STATIC Code: stack=2, locals=0, args_size=0 - 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations - 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded - 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V - 7: return + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: return public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: aload_0 - 5: iconst_1 - 6: putfield #x // Field stub:I - 9: aload_0 - 10: iconst_2 - 11: putfield #x // Field keep:I - 14: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_1 + x: putfield #x // Field stub:I + x: aload_0 + x: iconst_2 + x: putfield #x // Field keep:I + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub public int addOne(int); @@ -379,17 +379,17 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: iload_1 - 2: invokevirtual #x // Method addOneInner:(I)I - 5: ireturn + x: aload_0 + x: iload_1 + x: invokevirtual #x // Method addOneInner:(I)I + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; 0 6 1 value I RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub public int addOneInner(int); @@ -397,23 +397,23 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=2, args_size=2 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations - 2: ldc #x // String addOneInner - 4: ldc #x // String (I)I - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: iload_1 - 16: iconst_1 - 17: iadd - 18: ireturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // String addOneInner + x: ldc #x // String (I)I + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iload_1 + x: iconst_1 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; 15 4 1 value I RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestKeep public int addTwo(int); @@ -421,10 +421,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: iload_1 - 1: iconst_2 - 2: iadd - 3: ireturn + x: iload_1 + x: iconst_2 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -436,10 +436,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 - 0: iload_0 - 1: iconst_3 - 2: iadd - 3: ireturn + x: iload_0 + x: iconst_3 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -450,20 +450,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations - 2: ldc #x // String unsupportedMethod - 4: ldc #x // String ()Ljava/lang/String; - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V - 18: new #x // class java/lang/RuntimeException - 21: dup - 22: ldc #x // String Unreachable - 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 27: athrow + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // String unsupportedMethod + x: ldc #x // String ()Ljava/lang/String; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Unreachable + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestThrow public java.lang.String visibleButUsesUnsupportedMethod(); @@ -471,27 +471,27 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; - 4: areturn + x: aload_0 + x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub } SourceFile: "TinyFrameworkClassAnnotations.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestStub - 1: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) @@ -521,15 +521,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: aload_0 - 5: iconst_1 - 6: putfield #x // Field stub:I - 9: aload_0 - 10: iconst_2 - 11: putfield #x // Field keep:I - 14: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_1 + x: putfield #x // Field stub:I + x: aload_0 + x: iconst_2 + x: putfield #x // Field keep:I + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -540,10 +540,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: iload_1 - 2: invokevirtual #x // Method addOneInner:(I)I - 5: ireturn + x: aload_0 + x: iload_1 + x: invokevirtual #x // Method addOneInner:(I)I + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -555,10 +555,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: iload_1 - 1: iconst_1 - 2: iadd - 3: ireturn + x: iload_1 + x: iconst_1 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -570,10 +570,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: new #x // class java/lang/RuntimeException - 3: dup - 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V - 7: athrow + x: new #x // class java/lang/RuntimeException + x: dup + x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + x: athrow LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -585,10 +585,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: iload_1 - 1: iconst_2 - 2: iadd - 3: ireturn + x: iload_1 + x: iconst_2 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -600,10 +600,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 - 0: iload_0 - 1: iconst_3 - 2: iadd - 3: ireturn + x: iload_0 + x: iconst_3 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -614,8 +614,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: ldc #x // String This value shouldn\'t be seen on the host side. - 2: areturn + x: ldc #x // String This value shouldn\'t be seen on the host side. + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -626,9 +626,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; - 4: areturn + x: aload_0 + x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -636,12 +636,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW } SourceFile: "TinyFrameworkClassClassWideAnnotations.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class Compiled from "TinyFrameworkClassLoadHook.java" @@ -662,9 +662,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo flags: (0x0002) ACC_PRIVATE Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -675,11 +675,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 - 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set; - 3: aload_0 - 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z - 9: pop - 10: return + x: getstatic #x // Field sLoadedClasses:Ljava/util/Set; + x: aload_0 + x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z + x: pop + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -694,21 +694,21 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo flags: (0x0008) ACC_STATIC Code: stack=2, locals=0, args_size=0 - 0: new #x // class java/util/HashSet - 3: dup - 4: invokespecial #x // Method java/util/HashSet."<init>":()V - 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set; - 10: return + x: new #x // class java/util/HashSet + x: dup + x: invokespecial #x // Method java/util/HashSet."<init>":()V + x: putstatic #x // Field sLoadedClasses:Ljava/util/Set; + x: return LineNumberTable: } SourceFile: "TinyFrameworkClassLoadHook.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class Compiled from "TinyFrameworkClassWithInitializer.java" @@ -728,9 +728,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -741,26 +741,26 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn flags: (0x0008) ACC_STATIC Code: stack=2, locals=0, args_size=0 - 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer - 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded - 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V - 7: iconst_1 - 8: putstatic #x // Field sInitialized:Z - 11: return + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer + x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: iconst_1 + x: putstatic #x // Field sInitialized:Z + x: return LineNumberTable: } SourceFile: "TinyFrameworkClassWithInitializer.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) - 1: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class Compiled from "TinyFrameworkExceptionTester.java" @@ -776,9 +776,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -789,18 +789,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=1, args_size=0 - 0: new #x // class java/lang/IllegalStateException - 3: dup - 4: ldc #x // String Inner exception - 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V - 9: athrow - 10: astore_0 - 11: new #x // class java/lang/RuntimeException - 14: dup - 15: ldc #x // String Outer exception - 17: aload_0 - 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V - 21: athrow + x: new #x // class java/lang/IllegalStateException + x: dup + x: ldc #x // String Inner exception + x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V + x: athrow + x: astore_0 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Outer exception + x: aload_0 + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V + x: athrow Exception table: from to target type 0 10 10 Class java/lang/Exception @@ -814,12 +814,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe } SourceFile: "TinyFrameworkExceptionTester.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class Compiled from "TinyFrameworkForTextPolicy.java" @@ -843,25 +843,25 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x000a) ACC_PRIVATE, ACC_STATIC Code: stack=2, locals=0, args_size=0 - 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy - 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded - 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V - 7: return + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: return public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: aload_0 - 5: iconst_1 - 6: putfield #x // Field stub:I - 9: aload_0 - 10: iconst_2 - 11: putfield #x // Field keep:I - 14: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_1 + x: putfield #x // Field stub:I + x: aload_0 + x: iconst_2 + x: putfield #x // Field keep:I + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -872,10 +872,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: iload_1 - 2: invokevirtual #x // Method addOneInner:(I)I - 5: ireturn + x: aload_0 + x: iload_1 + x: invokevirtual #x // Method addOneInner:(I)I + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -887,16 +887,16 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=2, args_size=2 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy - 2: ldc #x // String addOneInner - 4: ldc #x // String (I)I - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: iload_1 - 16: iconst_1 - 17: iadd - 18: ireturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + x: ldc #x // String addOneInner + x: ldc #x // String (I)I + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iload_1 + x: iconst_1 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -908,10 +908,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: iload_1 - 1: iconst_2 - 2: iadd - 3: ireturn + x: iload_1 + x: iconst_2 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -923,10 +923,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 - 0: iload_0 - 1: iconst_3 - 2: iadd - 3: ireturn + x: iload_0 + x: iconst_3 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -937,27 +937,27 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy - 2: ldc #x // String unsupportedMethod - 4: ldc #x // String ()Ljava/lang/String; - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V - 18: new #x // class java/lang/RuntimeException - 21: dup - 22: ldc #x // String Unreachable - 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - 27: athrow + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + x: ldc #x // String unsupportedMethod + x: ldc #x // String ()Ljava/lang/String; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Unreachable + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow public java.lang.String visibleButUsesUnsupportedMethod(); descriptor: ()Ljava/lang/String; flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; - 4: areturn + x: aload_0 + x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -965,9 +965,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli } SourceFile: "TinyFrameworkForTextPolicy.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class Compiled from "TinyFrameworkNative.java" @@ -983,9 +983,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -996,18 +996,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=1, args_size=1 - 0: iload_0 - 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I - 4: ireturn + x: iload_0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + x: ireturn public static int nativeAddTwo_should_be_like_this(int); descriptor: (I)I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=1, args_size=1 - 0: iload_0 - 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I - 4: ireturn + x: iload_0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1018,20 +1018,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=4, args_size=2 - 0: lload_0 - 1: lload_2 - 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J - 5: lreturn + x: lload_0 + x: lload_2 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + x: lreturn public static long nativeLongPlus_should_be_like_this(long, long); descriptor: (JJ)J flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=4, args_size=2 - 0: lload_0 - 1: lload_2 - 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J - 5: lreturn + x: lload_0 + x: lload_2 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + x: lreturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1040,14 +1040,14 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative } SourceFile: "TinyFrameworkNative.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub - 1: #x(#x=s#x) + x: #x(#x=s#x) android.hosttest.annotation.HostSideTestNativeSubstitutionClass( value="TinyFrameworkNative_host" ) @@ -1065,15 +1065,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host - 2: ldc #x // String <init> - 4: ldc #x // String ()V - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: aload_0 - 16: invokespecial #x // Method java/lang/Object."<init>":()V - 19: return + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + x: ldc #x // String <init> + x: ldc #x // String ()V + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1084,16 +1084,16 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host - 2: ldc #x // String nativeAddTwo - 4: ldc #x // String (I)I - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: iload_0 - 16: iconst_2 - 17: iadd - 18: ireturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + x: ldc #x // String nativeAddTwo + x: ldc #x // String (I)I + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iload_0 + x: iconst_2 + x: iadd + x: ireturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1104,16 +1104,16 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=4, args_size=2 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host - 2: ldc #x // String nativeLongPlus - 4: ldc #x // String (JJ)J - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: lload_0 - 16: lload_2 - 17: ladd - 18: lreturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + x: ldc #x // String nativeLongPlus + x: ldc #x // String (JJ)J + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: lload_0 + x: lload_2 + x: ladd + x: lreturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1122,10 +1122,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host } SourceFile: "TinyFrameworkNative_host.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassKeep ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class Compiled from "TinyFrameworkNestedClasses.java" @@ -1145,12 +1145,12 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex flags: (0x0000) Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: aload_1 - 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; - 5: aload_0 - 6: invokespecial #x // Method java/lang/Object."<init>":()V - 9: return + x: aload_0 + x: aload_1 + x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1162,15 +1162,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 - 2: ldc #x // String get - 4: ldc #x // String ()Ljava/lang/Integer; - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: iconst_1 - 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; - 19: areturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Integer; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iconst_1 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1181,15 +1181,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 - 2: ldc #x // String get - 4: ldc #x // String ()Ljava/lang/Object; - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: aload_0 - 16: invokevirtual #x // Method get:()Ljava/lang/Integer; - 19: areturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Object; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1201,7 +1201,7 @@ EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframe Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class @@ -1218,9 +1218,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex flags: (0x0000) Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1231,15 +1231,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 - 2: ldc #x // String get - 4: ldc #x // String ()Ljava/lang/Integer; - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: iconst_2 - 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; - 19: areturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Integer; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iconst_2 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1250,15 +1250,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 - 2: ldc #x // String get - 4: ldc #x // String ()Ljava/lang/Object; - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: aload_0 - 16: invokevirtual #x // Method get:()Ljava/lang/Integer; - 19: areturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Object; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1270,7 +1270,7 @@ EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframe Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class @@ -1291,12 +1291,12 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex flags: (0x0000) Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: aload_1 - 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; - 5: aload_0 - 6: invokespecial #x // Method java/lang/Object."<init>":()V - 9: return + x: aload_0 + x: aload_1 + x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1308,15 +1308,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 - 2: ldc #x // String get - 4: ldc #x // String ()Ljava/lang/Integer; - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: iconst_3 - 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; - 19: areturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Integer; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iconst_3 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1327,15 +1327,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 - 2: ldc #x // String get - 4: ldc #x // String ()Ljava/lang/Object; - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: aload_0 - 16: invokevirtual #x // Method get:()Ljava/lang/Integer; - 19: areturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Object; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1347,7 +1347,7 @@ EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframew Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class @@ -1364,9 +1364,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex flags: (0x0000) Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1377,15 +1377,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 - 2: ldc #x // String get - 4: ldc #x // String ()Ljava/lang/Integer; - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: iconst_4 - 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; - 19: areturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Integer; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iconst_4 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1396,15 +1396,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 - 2: ldc #x // String get - 4: ldc #x // String ()Ljava/lang/Object; - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: aload_0 - 16: invokevirtual #x // Method get:()Ljava/lang/Integer; - 19: areturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Object; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1416,7 +1416,7 @@ EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframew Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class @@ -1437,12 +1437,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: aload_0 - 5: iload_1 - 6: putfield #x // Field value:I - 9: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iload_1 + x: putfield #x // Field value:I + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1453,9 +1453,9 @@ InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class @@ -1480,15 +1480,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: aload_1 - 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; - 5: aload_0 - 6: invokespecial #x // Method java/lang/Object."<init>":()V - 9: aload_0 - 10: iconst_5 - 11: putfield #x // Field value:I - 14: return + x: aload_0 + x: aload_1 + x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_5 + x: putfield #x // Field value:I + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1499,12 +1499,12 @@ InnerClasses: public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class @@ -1521,9 +1521,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat flags: (0x0000) Code: stack=1, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1534,15 +1534,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 - 2: ldc #x // String get - 4: ldc #x // String ()Ljava/lang/Integer; - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: bipush 7 - 17: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; - 20: areturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Integer; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: bipush 7 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1553,15 +1553,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=4, locals=1, args_size=1 - 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 - 2: ldc #x // String get - 4: ldc #x // String ()Ljava/lang/Object; - 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - 15: aload_0 - 16: invokevirtual #x // Method get:()Ljava/lang/Integer; - 19: areturn + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Object; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1574,7 +1574,7 @@ EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframew Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class @@ -1595,12 +1595,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: aload_0 - 5: bipush 6 - 7: putfield #x // Field value:I - 10: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: bipush 6 + x: putfield #x // Field value:I + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1611,10 +1611,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=0, args_size=0 - 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 - 3: dup - 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V - 7: areturn + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V + x: areturn LineNumberTable: Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; } @@ -1623,12 +1623,12 @@ InnerClasses: #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class @@ -1645,10 +1645,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 - 0: aload_0 - 1: iload_1 - 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V - 5: return + x: aload_0 + x: iload_1 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1660,9 +1660,9 @@ InnerClasses: public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class @@ -1689,15 +1689,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #x // Method java/lang/Object."<init>":()V - 4: aload_0 - 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 - 8: dup - 9: aload_0 - 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V - 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier; - 16: return + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + x: dup + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + x: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1708,11 +1708,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 - 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 - 3: dup - 4: aload_0 - 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V - 8: areturn + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + x: dup + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + x: areturn LineNumberTable: LocalVariableTable: Start Length Slot Name Signature @@ -1724,10 +1724,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=0, args_size=0 - 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 - 3: dup - 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V - 7: areturn + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V + x: areturn LineNumberTable: Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; @@ -1736,11 +1736,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0008) ACC_STATIC Code: stack=2, locals=0, args_size=0 - 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 - 3: dup - 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V - 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; - 10: return + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V + x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + x: return LineNumberTable: } InnerClasses: @@ -1755,12 +1755,12 @@ InnerClasses: #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: - 0: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass - 1: #x() + x: #x() com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass RuntimeInvisibleAnnotations: - 0: #x() + x: #x() android.hosttest.annotation.HostSideTestWholeClassStub NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass diff --git a/tools/hoststubgen/scripts/dump-jar b/tools/hoststubgen/scripts/dump-jar index 93729fb22caa..992665ed58ee 100755 --- a/tools/hoststubgen/scripts/dump-jar +++ b/tools/hoststubgen/scripts/dump-jar @@ -93,6 +93,7 @@ filter_output() { if (( $simple )) ; then # For "simple output" mode, # - Normalize the constant numbers (replace with "#x") + # - Normalize byte code offsets and other similar numbers. (e.g. "0:" -> "x:") # - Remove the constant pool # - Remove the line number table # - Some other transient lines @@ -100,6 +101,7 @@ filter_output() { # `/PATTERN-1/,/PATTERN-1/{//!d}` is a trick to delete lines between two patterns, without # the start and the end lines. sed -e 's/#[0-9][0-9]*/#x/g' \ + -e 's/^\( *\)[0-9][0-9]*:/\1x:/' \ -e '/^Constant pool:/,/^[^ ]/{//!d}' \ -e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \ -e '/SHA-256 checksum/d' \ |