diff --git a/AconfigFlags.bp b/AconfigFlags.bp index fbe4905d9754fc15752c5d08fe53bab7ba8c9c78..c6ce799f0a247298aae22c7a5d1ec7f74f0948e3 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -24,6 +24,7 @@ aconfig_declarations_group { "android-sdk-flags-java", "android.adaptiveauth.flags-aconfig-java", "android.app.appfunctions.flags-aconfig-java", + "android.app.assist.flags-aconfig-java", "android.app.contextualsearch.flags-aconfig-java", "android.app.flags-aconfig-java", "android.app.jank.flags-aconfig-java", @@ -64,6 +65,7 @@ aconfig_declarations_group { "android.server.app.flags-aconfig-java", "android.service.autofill.flags-aconfig-java", "android.service.chooser.flags-aconfig-java", + "android.service.compat.flags-aconfig-java", "android.service.controls.flags-aconfig-java", "android.service.dreams.flags-aconfig-java", "android.service.notification.flags-aconfig-java", @@ -862,6 +864,21 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +aconfig_declarations { + name: "android.service.compat.flags-aconfig", + package: "com.android.server.compat", + container: "system", + srcs: [ + "services/core/java/com/android/server/compat/*.aconfig", + ], +} + +java_aconfig_library { + name: "android.service.compat.flags-aconfig-java", + aconfig_declarations: "android.service.compat.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Multi user aconfig_declarations { name: "android.multiuser.flags-aconfig", @@ -1230,6 +1247,20 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +// Assist +aconfig_declarations { + name: "android.app.assist.flags-aconfig", + package: "android.app.assist.flags", + container: "system", + srcs: ["core/java/android/app/assist/flags.aconfig"], +} + +java_aconfig_library { + name: "android.app.assist.flags-aconfig-java", + aconfig_declarations: "android.app.assist.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Smartspace aconfig_declarations { name: "android.app.smartspace.flags-aconfig", diff --git a/Android.bp b/Android.bp index 811755d0bdaf1d2c4395f7fe11e93610b5513cc1..252aeef079d290d67fc386549e2db85b75738875 100644 --- a/Android.bp +++ b/Android.bp @@ -150,6 +150,7 @@ filegroup { // etc. ":framework-javastream-protos", ":statslog-framework-java-gen", // FrameworkStatsLog.java + ":statslog-hwui-java-gen", // HwuiStatsLog.java ":audio_policy_configuration_V7_0", ], } @@ -170,12 +171,6 @@ java_library { //same purpose. "//external/robolectric:__subpackages__", "//frameworks/layoutlib:__subpackages__", - - // This is for the same purpose as robolectric -- to build "framework.jar" for host-side - // testing. - // TODO: Once Ravenwood is stable, move the host side jar targets to this directory, - // and remove this line. - "//frameworks/base/tools/hoststubgen:__subpackages__", ], } diff --git a/Ravenwood.bp b/Ravenwood.bp index ec58210e1e3bd3c72c27163236db5ea9b8a74719..2e038e00bb35a8a0000c764c90946f2ff39e2729 100644 --- a/Ravenwood.bp +++ b/Ravenwood.bp @@ -12,256 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -// We need this "trampoline" rule to force soong to give a host-side jar to -// framework-minus-apex.ravenwood-base. Otherwise, soong would mix up the arch (?) and we'd get -// a dex jar. -java_library { - name: "framework-minus-apex-for-hoststubgen", - installable: false, // host only jar. - static_libs: [ - "framework-minus-apex", - ], - sdk_version: "core_platform", - visibility: ["//visibility:private"], -} - -// Process framework-all with hoststubgen for Ravenwood. -// This step takes several tens of seconds, so we manually shard it to multiple modules. -// All the copies have to be kept in sync. -// TODO: Do the sharding better, either by making hostsubgen support sharding natively, or -// making a better build rule. - -genrule_defaults { - name: "framework-minus-apex.ravenwood-base_defaults", - defaults: ["ravenwood-internal-only-visibility-genrule"], - tools: ["hoststubgen"], - srcs: [ - ":framework-minus-apex-for-hoststubgen", - ":ravenwood-framework-policies", - ":ravenwood-standard-options", - ":ravenwood-annotation-allowed-classes", - ], - out: [ - "ravenwood.jar", - "hoststubgen_framework-minus-apex.log", - ], -} - -framework_minus_apex_cmd = "$(location hoststubgen) " + - "@$(location :ravenwood-standard-options) " + - "--debug-log $(location hoststubgen_framework-minus-apex.log) " + - "--out-jar $(location ravenwood.jar) " + - "--in-jar $(location :framework-minus-apex-for-hoststubgen) " + - "--policy-override-file $(location :ravenwood-framework-policies) " + - "--annotation-allowed-classes-file $(location :ravenwood-annotation-allowed-classes) " - -java_genrule { - name: "framework-minus-apex.ravenwood-base_X0", - defaults: ["framework-minus-apex.ravenwood-base_defaults"], - cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 0", -} - -java_genrule { - name: "framework-minus-apex.ravenwood-base_X1", - defaults: ["framework-minus-apex.ravenwood-base_defaults"], - cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 1", -} - -java_genrule { - name: "framework-minus-apex.ravenwood-base_X2", - defaults: ["framework-minus-apex.ravenwood-base_defaults"], - cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 2", -} - -java_genrule { - name: "framework-minus-apex.ravenwood-base_X3", - defaults: ["framework-minus-apex.ravenwood-base_defaults"], - cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 3", -} - -java_genrule { - name: "framework-minus-apex.ravenwood-base_X4", - defaults: ["framework-minus-apex.ravenwood-base_defaults"], - cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 4", -} - -java_genrule { - name: "framework-minus-apex.ravenwood-base_X5", - defaults: ["framework-minus-apex.ravenwood-base_defaults"], - cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 5", -} - -java_genrule { - name: "framework-minus-apex.ravenwood-base_X6", - defaults: ["framework-minus-apex.ravenwood-base_defaults"], - cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 6", -} - -java_genrule { - name: "framework-minus-apex.ravenwood-base_X7", - defaults: ["framework-minus-apex.ravenwood-base_defaults"], - cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 7", -} - -java_genrule { - name: "framework-minus-apex.ravenwood-base_X8", - defaults: ["framework-minus-apex.ravenwood-base_defaults"], - cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 8", -} - -java_genrule { - name: "framework-minus-apex.ravenwood-base_X9", - defaults: ["framework-minus-apex.ravenwood-base_defaults"], - cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 9", -} - -// Build framework-minus-apex.ravenwood-base without sharding. -// We extract the various dump files from this one, rather than the sharded ones, because -// some dumps use the output from other classes (e.g. base classes) which may not be in the -// same shard. Also some of the dump files ("apis") may be slow even when sharded, because -// the output contains the information from all the input classes, rather than the output classes. -// Not using sharding is fine for this module because it's only used for collecting the -// dump / stats files, which don't have to happen regularly. -java_genrule { - name: "framework-minus-apex.ravenwood-base_all", - defaults: ["framework-minus-apex.ravenwood-base_defaults"], - cmd: framework_minus_apex_cmd + - "--stats-file $(location hoststubgen_framework-minus-apex_stats.csv) " + - "--supported-api-list-file $(location hoststubgen_framework-minus-apex_apis.csv) " + - - "--gen-keep-all-file $(location hoststubgen_framework-minus-apex_keep_all.txt) " + - "--gen-input-dump-file $(location hoststubgen_framework-minus-apex_dump.txt) ", - - out: [ - "hoststubgen_framework-minus-apex_keep_all.txt", - "hoststubgen_framework-minus-apex_dump.txt", - "hoststubgen_framework-minus-apex_stats.csv", - "hoststubgen_framework-minus-apex_apis.csv", - ], -} - -// Marge all the sharded jars -java_genrule { - name: "framework-minus-apex.ravenwood", - defaults: ["ravenwood-internal-only-visibility-java"], - cmd: "$(location merge_zips) $(out) $(in)", - tools: ["merge_zips"], - srcs: [ - ":framework-minus-apex.ravenwood-base_X0{ravenwood.jar}", - ":framework-minus-apex.ravenwood-base_X1{ravenwood.jar}", - ":framework-minus-apex.ravenwood-base_X2{ravenwood.jar}", - ":framework-minus-apex.ravenwood-base_X3{ravenwood.jar}", - ":framework-minus-apex.ravenwood-base_X4{ravenwood.jar}", - ":framework-minus-apex.ravenwood-base_X5{ravenwood.jar}", - ":framework-minus-apex.ravenwood-base_X6{ravenwood.jar}", - ":framework-minus-apex.ravenwood-base_X7{ravenwood.jar}", - ":framework-minus-apex.ravenwood-base_X8{ravenwood.jar}", - ":framework-minus-apex.ravenwood-base_X9{ravenwood.jar}", - ], - out: [ - "framework-minus-apex.ravenwood.jar", - ], -} +// "framework-minus-apex" and "all-updatable-modules-system-stubs" are not +// visible publicly. We re-export them to Ravenwood in this file. java_library { - name: "services.core-for-hoststubgen", - installable: false, // host only jar. - static_libs: [ - "services.core", - ], - sdk_version: "core_platform", - visibility: ["//visibility:private"], -} - -java_genrule { - name: "services.core.ravenwood-base", - tools: ["hoststubgen"], - cmd: "$(location hoststubgen) " + - "@$(location :ravenwood-standard-options) " + - - "--debug-log $(location hoststubgen_services.core.log) " + - "--stats-file $(location hoststubgen_services.core_stats.csv) " + - "--supported-api-list-file $(location hoststubgen_services.core_apis.csv) " + - - "--out-jar $(location ravenwood.jar) " + - - "--gen-keep-all-file $(location hoststubgen_services.core_keep_all.txt) " + - "--gen-input-dump-file $(location hoststubgen_services.core_dump.txt) " + - - "--in-jar $(location :services.core-for-hoststubgen) " + - "--policy-override-file $(location :ravenwood-services-policies) " + - "--annotation-allowed-classes-file $(location :ravenwood-annotation-allowed-classes) ", - srcs: [ - ":services.core-for-hoststubgen", - ":ravenwood-services-policies", - ":ravenwood-standard-options", - ":ravenwood-annotation-allowed-classes", - ], - out: [ - "ravenwood.jar", - - // Following files are created just as FYI. - "hoststubgen_services.core_keep_all.txt", - "hoststubgen_services.core_dump.txt", - - "hoststubgen_services.core.log", - "hoststubgen_services.core_stats.csv", - "hoststubgen_services.core_apis.csv", - ], - defaults: ["ravenwood-internal-only-visibility-genrule"], -} - -java_genrule { - name: "services.core.ravenwood", - defaults: ["ravenwood-internal-only-visibility-genrule"], - cmd: "cp $(in) $(out)", - srcs: [ - ":services.core.ravenwood-base{ravenwood.jar}", - ], - out: [ - "services.core.ravenwood.jar", - ], -} - -// TODO(b/313930116) This jarjar is a bit slow. We should use hoststubgen for renaming, -// but services.core.ravenwood has complex dependencies, so it'll take more than -// just using hoststubgen "rename"s. -java_library { - name: "services.core.ravenwood-jarjar", - defaults: ["ravenwood-internal-only-visibility-java"], + name: "framework-minus-apex-for-host", installable: false, - static_libs: [ - "services.core.ravenwood", - ], - jarjar_rules: ":ravenwood-services-jarjar-rules", + static_libs: ["framework-minus-apex"], + visibility: ["//frameworks/base/ravenwood"], } -// Jars in "ravenwood-runtime" are set to the classpath, sorted alphabetically. -// Rename some of the dependencies to make sure they're included in the intended order. java_library { - name: "100-framework-minus-apex.ravenwood", - defaults: ["ravenwood-internal-only-visibility-java"], - static_libs: [ - "framework-minus-apex.ravenwood", - ], - sdk_version: "core_platform", - // See b/313930116. Jarjar is too slow on this jar. We use HostStubGen to do the rename. - // jarjar_rules: ":ravenwood-framework-jarjar-rules", -} - -java_genrule { - // Use 200 to make sure it comes before the mainline stub ("all-updatable..."). - name: "200-kxml2-android", - defaults: ["ravenwood-internal-only-visibility-genrule"], - cmd: "cp $(in) $(out)", - srcs: [":kxml2-android"], - out: ["200-kxml2-android.jar"], -} - -java_genrule { - name: "z00-all-updatable-modules-system-stubs", - defaults: ["ravenwood-internal-only-visibility-genrule"], - cmd: "cp $(in) $(out)", - srcs: [":all-updatable-modules-system-stubs"], - out: ["z00-all-updatable-modules-system-stubs.jar"], + name: "all-updatable-modules-system-stubs-for-host", + installable: false, + static_libs: ["all-updatable-modules-system-stubs"], + visibility: ["//frameworks/base/ravenwood"], } diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig index e5389b4f96fb7797f4c9f93a777736f5622589db..11c5b51e23ae564e6b154b064fcea813ce7610e1 100644 --- a/apex/jobscheduler/service/aconfig/job.aconfig +++ b/apex/jobscheduler/service/aconfig/job.aconfig @@ -75,3 +75,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "enforce_quota_policy_to_fgs_jobs" + namespace: "backstage_power" + description: "Applies the normal quota policy to FGS jobs" + bug: "341201311" +} diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index a1c72fb4c06c6d5655d6dd918b40ba5b12c0c41d..03a3a0d51891154a41c15e3d473f923d81ea28dc 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -99,10 +99,10 @@ import java.util.function.Predicate; * the number of jobs or sessions that can run within the window. Regardless of bucket, apps will * not be allowed to run more than 20 jobs within the past 10 minutes. * - * Jobs are throttled while an app is not in a foreground state. All jobs are allowed to run - * freely when an app enters the foreground state and are restricted when the app leaves the - * foreground state. However, jobs that are started while the app is in the TOP state do not count - * towards any quota and are not restricted regardless of the app's state change. + * Jobs are throttled while an app is not in a TOP or BOUND_TOP state. All jobs are allowed to run + * freely when an app enters the TOP or BOUND_TOP state and are restricted when the app leaves those + * states. However, jobs that are started while the app is in the TOP state do not count towards any + * quota and are not restricted regardless of the app's state change. * * Jobs will not be throttled when the device is charging. The device is considered to be charging * once the {@link BatteryManager#ACTION_CHARGING} intent has been broadcast. @@ -567,6 +567,11 @@ public final class QuotaController extends StateController { ActivityManager.getService().registerUidObserver(new QcUidObserver(), ActivityManager.UID_OBSERVER_PROCSTATE, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, null); + if (Flags.enforceQuotaPolicyToFgsJobs()) { + ActivityManager.getService().registerUidObserver(new QcUidObserver(), + ActivityManager.UID_OBSERVER_PROCSTATE, + ActivityManager.PROCESS_STATE_BOUND_TOP, null); + } ActivityManager.getService().registerUidObserver(new QcUidObserver(), ActivityManager.UID_OBSERVER_PROCSTATE, ActivityManager.PROCESS_STATE_TOP, null); @@ -2706,6 +2711,12 @@ public final class QuotaController extends StateController { } } + @VisibleForTesting + int getProcessStateQuotaFreeThreshold() { + return Flags.enforceQuotaPolicyToFgsJobs() ? ActivityManager.PROCESS_STATE_BOUND_TOP : + ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; + } + private class QcHandler extends Handler { QcHandler(Looper looper) { @@ -2832,15 +2843,15 @@ public final class QuotaController extends StateController { mTopAppCache.put(uid, true); mTopAppGraceCache.delete(uid); if (mForegroundUids.get(uid)) { - // Went from FGS to TOP. We don't need to reprocess timers or - // jobs. + // Went from a process state with quota free to TOP. We don't + // need to reprocess timers or jobs. break; } mForegroundUids.put(uid, true); isQuotaFree = true; } else { final boolean reprocess; - if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { + if (procState <= getProcessStateQuotaFreeThreshold()) { reprocess = !mForegroundUids.get(uid); mForegroundUids.put(uid, true); isQuotaFree = true; diff --git a/api/api.go b/api/api.go index e9f1feebd899d2124331dd55abe583c783a36527..1bbf3709480ad447cfe8befd69a0be7e628a3ccd 100644 --- a/api/api.go +++ b/api/api.go @@ -514,7 +514,7 @@ func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) { func combinedApisModuleFactory() android.Module { module := &CombinedApis{} module.AddProperties(&module.properties) - android.InitAndroidModule(module) + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) }) return module } diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp index 3534624a58a2357e62184520127f752b319abd38..f80ccf7a56fb11b83b5e59db3943ab8316714a3d 100644 --- a/cmds/bootanimation/Android.bp +++ b/cmds/bootanimation/Android.bp @@ -7,6 +7,18 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +aconfig_declarations { + name: "bootanimation_flags", + package: "com.android.graphics.bootanimation.flags", + container: "system", + srcs: ["bootanimation_flags.aconfig"], +} + +cc_aconfig_library { + name: "libbootanimationflags", + aconfig_declarations: "bootanimation_flags", +} + cc_defaults { name: "bootanimation_defaults", @@ -28,6 +40,10 @@ cc_defaults { "liblog", "libutils", ], + + static_libs: [ + "libbootanimationflags", + ], } // bootanimation executable diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index c2f6e3072507fcdddc950bed3302b88e2657923e..14e238768f4160e4063236a0ff78fb179fda5d05 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -15,6 +15,7 @@ */ #define LOG_NDEBUG 0 + #define LOG_TAG "BootAnimation" #define ATRACE_TAG ATRACE_TAG_GRAPHICS @@ -61,6 +62,8 @@ #include "BootAnimation.h" +#include + #define ANIM_PATH_MAX 255 #define STR(x) #x #define STRTO(x) STR(x) @@ -106,7 +109,6 @@ static const int TEXT_CENTER_VALUE = INT_MAX; static const int TEXT_MISSING_VALUE = INT_MIN; static const char EXIT_PROP_NAME[] = "service.bootanim.exit"; static const char PROGRESS_PROP_NAME[] = "service.bootanim.progress"; -static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays"; static const char CLOCK_ENABLED_PROP_NAME[] = "persist.sys.bootanim.clock.enabled"; static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1; static const int MAX_CHECK_EXIT_INTERVAL_US = 50000; @@ -449,19 +451,21 @@ public: auto token = SurfaceComposerClient::getPhysicalDisplayToken( event.header.displayId); - if (token != mBootAnimation->mDisplayToken) { + auto firstDisplay = mBootAnimation->mDisplays.front(); + if (token != firstDisplay.displayToken) { // ignore hotplug of a secondary display continue; } DisplayMode displayMode; const status_t error = SurfaceComposerClient::getActiveDisplayMode( - mBootAnimation->mDisplayToken, &displayMode); + firstDisplay.displayToken, &displayMode); if (error != NO_ERROR) { SLOGE("Can't get active display mode."); } mBootAnimation->resizeSurface(displayMode.resolution.getWidth(), - displayMode.resolution.getHeight()); + displayMode.resolution.getHeight(), + firstDisplay); } } } while (numEvents > 0); @@ -507,141 +511,106 @@ ui::Size BootAnimation::limitSurfaceSize(int width, int height) const { status_t BootAnimation::readyToRun() { ATRACE_CALL(); mAssets.addDefaultAssets(); + return initDisplaysAndSurfaces(); +} - const std::vector ids = SurfaceComposerClient::getPhysicalDisplayIds(); - if (ids.empty()) { - SLOGE("Failed to get ID for any displays\n"); +status_t BootAnimation::initDisplaysAndSurfaces() { + std::vector displayIds = SurfaceComposerClient::getPhysicalDisplayIds(); + if (displayIds.empty()) { + SLOGE("Failed to get ID for any displays"); return NAME_NOT_FOUND; } - // this system property specifies multi-display IDs to show the boot animation - // multiple ids can be set with comma (,) as separator, for example: - // setprop persist.boot.animation.displays 19260422155234049,19261083906282754 - Vector physicalDisplayIds; - char displayValue[PROPERTY_VALUE_MAX] = ""; - property_get(DISPLAYS_PROP_NAME, displayValue, ""); - bool isValid = displayValue[0] != '\0'; - if (isValid) { - char *p = displayValue; - while (*p) { - if (!isdigit(*p) && *p != ',') { - isValid = false; - break; - } - p ++; - } - if (!isValid) - SLOGE("Invalid syntax for the value of system prop: %s", DISPLAYS_PROP_NAME); - } - if (isValid) { - std::istringstream stream(displayValue); - for (PhysicalDisplayId id; stream >> id.value; ) { - physicalDisplayIds.add(id); - if (stream.peek() == ',') - stream.ignore(); - } - - // the first specified display id is used to retrieve mDisplayToken - for (const auto id : physicalDisplayIds) { - if (std::find(ids.begin(), ids.end(), id) != ids.end()) { - if (const auto token = SurfaceComposerClient::getPhysicalDisplayToken(id)) { - mDisplayToken = token; - break; - } - } - } + // If Multi-Display isn't explicitly enabled, ignore all displays after the first one + if (!com::android::graphics::bootanimation::flags::multidisplay()) { + displayIds.erase(displayIds.begin() + 1, displayIds.end()); } - // If the system property is not present or invalid, display 0 is used - if (mDisplayToken == nullptr) { - mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); - if (mDisplayToken == nullptr) { + for (const auto id : displayIds) { + if (const auto token = SurfaceComposerClient::getPhysicalDisplayToken(id)) { + mDisplays.push_back({.displayToken = token}); + } else { + SLOGE("Failed to get display token for a display"); + SLOGE("Failed to get display token for display %" PRIu64, id.value); return NAME_NOT_FOUND; } } - DisplayMode displayMode; - const status_t error = - SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &displayMode); - if (error != NO_ERROR) { - return error; - } + // Initialize EGL + mEgl = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(mEgl, nullptr, nullptr); + EGLConfig config = getEglConfig(mEgl); + EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + mEglContext = eglCreateContext(mEgl, config, nullptr, contextAttributes); mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0); mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0); - ui::Size resolution = displayMode.resolution; - resolution = limitSurfaceSize(resolution.width, resolution.height); - // create the native surface - sp control = session()->createSurface(String8("BootAnimation"), - resolution.getWidth(), resolution.getHeight(), PIXEL_FORMAT_RGB_565, - ISurfaceComposerClient::eOpaque); - SurfaceComposerClient::Transaction t; - if (isValid) { - // In the case of multi-display, boot animation shows on the specified displays - for (const auto id : physicalDisplayIds) { - if (std::find(ids.begin(), ids.end(), id) != ids.end()) { - if (const auto token = SurfaceComposerClient::getPhysicalDisplayToken(id)) { - t.setDisplayLayerStack(token, ui::DEFAULT_LAYER_STACK); - } - } + for (size_t displayIdx = 0; displayIdx < mDisplays.size(); displayIdx++) { + auto& display = mDisplays[displayIdx]; + DisplayMode displayMode; + const status_t error = + SurfaceComposerClient::getActiveDisplayMode(display.displayToken, &displayMode); + if (error != NO_ERROR) { + return error; } - t.setLayerStack(control, ui::DEFAULT_LAYER_STACK); - } - - t.setLayer(control, 0x40000000) - .apply(); - - sp s = control->getSurface(); - - // initialize opengl and egl - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - eglInitialize(display, nullptr, nullptr); - EGLConfig config = getEglConfig(display); - EGLSurface surface = eglCreateWindowSurface(display, config, s.get(), nullptr); - // Initialize egl context with client version number 2.0. - EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; - EGLContext context = eglCreateContext(display, config, nullptr, contextAttributes); - EGLint w, h; - eglQuerySurface(display, surface, EGL_WIDTH, &w); - eglQuerySurface(display, surface, EGL_HEIGHT, &h); - - if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { - return NO_INIT; - } - - mDisplay = display; - mContext = context; - mSurface = surface; - mInitWidth = mWidth = w; - mInitHeight = mHeight = h; - mFlingerSurfaceControl = control; - mFlingerSurface = s; - mTargetInset = -1; - - // Rotate the boot animation according to the value specified in the sysprop - // ro.bootanim.set_orientation_. Four values are supported: ORIENTATION_0, - // ORIENTATION_90, ORIENTATION_180 and ORIENTATION_270. - // If the value isn't specified or is ORIENTATION_0, nothing will be changed. - // This is needed to support having boot animation in orientations different from the natural - // device orientation. For example, on tablets that may want to keep natural orientation - // portrait for applications compatibility and to have the boot animation in landscape. - rotateAwayFromNaturalOrientationIfNeeded(); - - projectSceneToWindow(); + ui::Size resolution = displayMode.resolution; + // Clamp each surface to max size + resolution = limitSurfaceSize(resolution.width, resolution.height); + // Create the native surface + display.surfaceControl = + session()->createSurface(String8("BootAnimation"), resolution.width, + resolution.height, PIXEL_FORMAT_RGB_565, + ISurfaceComposerClient::eOpaque); + // Attach surface to layerstack, and associate layerstack with physical display + configureDisplayAndLayerStack(display, ui::LayerStack::fromValue(displayIdx)); + display.surface = display.surfaceControl->getSurface(); + display.eglSurface = eglCreateWindowSurface(mEgl, config, display.surface.get(), nullptr); + + EGLint w, h; + eglQuerySurface(mEgl, display.eglSurface, EGL_WIDTH, &w); + eglQuerySurface(mEgl, display.eglSurface, EGL_HEIGHT, &h); + if (eglMakeCurrent(mEgl, display.eglSurface, display.eglSurface, + mEglContext) == EGL_FALSE) { + return NO_INIT; + } + display.initWidth = display.width = w; + display.initHeight = display.height = h; + mTargetInset = -1; + + // Rotate the boot animation according to the value specified in the sysprop + // ro.bootanim.set_orientation_. Four values are supported: ORIENTATION_0, + // ORIENTATION_90, ORIENTATION_180 and ORIENTATION_270. + // If the value isn't specified or is ORIENTATION_0, nothing will be changed. + // This is needed to support boot animation in orientations different from the natural + // device orientation. For example, on tablets that may want to keep natural orientation + // portrait for applications compatibility and to have the boot animation in landscape. + rotateAwayFromNaturalOrientationIfNeeded(display); + + projectSceneToWindow(display); + } // end iteration over all display tokens // Register a display event receiver mDisplayEventReceiver = std::make_unique(); status_t status = mDisplayEventReceiver->initCheck(); SLOGE_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver failed with status: %d", - status); + status); mLooper->addFd(mDisplayEventReceiver->getFd(), 0, Looper::EVENT_INPUT, - new DisplayEventCallback(this), nullptr); + new DisplayEventCallback(this), nullptr); return NO_ERROR; } -void BootAnimation::rotateAwayFromNaturalOrientationIfNeeded() { +void BootAnimation::configureDisplayAndLayerStack(const Display& display, + ui::LayerStack layerStack) { + SurfaceComposerClient::Transaction t; + t.setDisplayLayerStack(display.displayToken, layerStack); + t.setLayerStack(display.surfaceControl, layerStack) + .setLayer(display.surfaceControl, 0x40000000) + .apply(); +} + +void BootAnimation::rotateAwayFromNaturalOrientationIfNeeded(Display& display) { ATRACE_CALL(); const auto orientation = parseOrientationProperty(); @@ -651,16 +620,16 @@ void BootAnimation::rotateAwayFromNaturalOrientationIfNeeded() { } if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) { - std::swap(mWidth, mHeight); - std::swap(mInitWidth, mInitHeight); - mFlingerSurfaceControl->updateDefaultBufferSize(mWidth, mHeight); + std::swap(display.width, display.height); + std::swap(display.initWidth, display.initHeight); + display.surfaceControl->updateDefaultBufferSize(display.width, display.height); } - Rect displayRect(0, 0, mWidth, mHeight); - Rect layerStackRect(0, 0, mWidth, mHeight); + Rect displayRect(0, 0, display.width, display.height); + Rect layerStackRect(0, 0, display.width, display.height); SurfaceComposerClient::Transaction t; - t.setDisplayProjection(mDisplayToken, orientation, layerStackRect, displayRect); + t.setDisplayProjection(display.displayToken, orientation, layerStackRect, displayRect); t.apply(); } @@ -691,38 +660,37 @@ ui::Rotation BootAnimation::parseOrientationProperty() { return ui::ROTATION_0; } -void BootAnimation::projectSceneToWindow() { +void BootAnimation::projectSceneToWindow(const Display& display) { ATRACE_CALL(); - glViewport(0, 0, mWidth, mHeight); - glScissor(0, 0, mWidth, mHeight); + glViewport(0, 0, display.width, display.height); + glScissor(0, 0, display.width, display.height); } -void BootAnimation::resizeSurface(int newWidth, int newHeight) { +void BootAnimation::resizeSurface(int newWidth, int newHeight, Display& display) { ATRACE_CALL(); // We assume this function is called on the animation thread. - if (newWidth == mWidth && newHeight == mHeight) { + if (newWidth == display.width && newHeight == display.height) { return; } - SLOGV("Resizing the boot animation surface to %d %d", newWidth, newHeight); - eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroySurface(mDisplay, mSurface); + eglMakeCurrent(mEgl, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(mEgl, display.eglSurface); const auto limitedSize = limitSurfaceSize(newWidth, newHeight); - mWidth = limitedSize.width; - mHeight = limitedSize.height; - - mFlingerSurfaceControl->updateDefaultBufferSize(mWidth, mHeight); - EGLConfig config = getEglConfig(mDisplay); - EGLSurface surface = eglCreateWindowSurface(mDisplay, config, mFlingerSurface.get(), nullptr); - if (eglMakeCurrent(mDisplay, surface, surface, mContext) == EGL_FALSE) { - SLOGE("Can't make the new surface current. Error %d", eglGetError()); + display.width = limitedSize.width; + display.height = limitedSize.height; + + display.surfaceControl->updateDefaultBufferSize(display.width, display.height); + EGLConfig config = getEglConfig(mEgl); + EGLSurface eglSurface = eglCreateWindowSurface(mEgl, config, display.surface.get(), nullptr); + if (eglMakeCurrent(mEgl, eglSurface, eglSurface, mEglContext) == EGL_FALSE) { + SLOGE("Can't make the new eglSurface current. Error %d", eglGetError()); return; } - projectSceneToWindow(); + projectSceneToWindow(display); - mSurface = surface; + display.eglSurface = eglSurface; } bool BootAnimation::preloadAnimation() { @@ -852,24 +820,26 @@ bool BootAnimation::threadLoop() { // animation. if (mZipFileName.empty()) { ALOGD("No animation file"); - result = android(); + result = android(mDisplays.front()); } else { result = movie(); } mCallbacks->shutdown(); - eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroyContext(mDisplay, mContext); - eglDestroySurface(mDisplay, mSurface); - mFlingerSurface.clear(); - mFlingerSurfaceControl.clear(); - eglTerminate(mDisplay); + eglMakeCurrent(mEgl, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(mEgl, mEglContext); + for (auto& display : mDisplays) { + eglDestroySurface(mEgl, display.eglSurface); + display.surface.clear(); + display.surfaceControl.clear(); + } + eglTerminate(mEgl); eglReleaseThread(); IPCThreadState::self()->stopProcess(); return result; } -bool BootAnimation::android() { +bool BootAnimation::android(const Display& display) { ATRACE_CALL(); glActiveTexture(GL_TEXTURE0); @@ -887,7 +857,7 @@ bool BootAnimation::android() { glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(mDisplay, mSurface); + eglSwapBuffers(mEgl, display.eglSurface); // Blend state glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -895,11 +865,11 @@ bool BootAnimation::android() { const nsecs_t startTime = systemTime(); do { processDisplayEvents(); - const GLint xc = (mWidth - mAndroid[0].w) / 2; - const GLint yc = (mHeight - mAndroid[0].h) / 2; + const GLint xc = (display.width - mAndroid[0].w) / 2; + const GLint yc = (display.height - mAndroid[0].h) / 2; const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h); - glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), - updateRect.height()); + glScissor(updateRect.left, display.height - updateRect.bottom, updateRect.width(), + updateRect.height()); nsecs_t now = systemTime(); double time = now - startTime; @@ -913,14 +883,14 @@ bool BootAnimation::android() { glEnable(GL_SCISSOR_TEST); glDisable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, mAndroid[1].name); - drawTexturedQuad(x, yc, mAndroid[1].w, mAndroid[1].h); - drawTexturedQuad(x + mAndroid[1].w, yc, mAndroid[1].w, mAndroid[1].h); + drawTexturedQuad(x, yc, mAndroid[1].w, mAndroid[1].h, display); + drawTexturedQuad(x + mAndroid[1].w, yc, mAndroid[1].w, mAndroid[1].h, display); glEnable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, mAndroid[0].name); - drawTexturedQuad(xc, yc, mAndroid[0].w, mAndroid[0].h); + drawTexturedQuad(xc, yc, mAndroid[0].w, mAndroid[0].h, display); - EGLBoolean res = eglSwapBuffers(mDisplay, mSurface); + EGLBoolean res = eglSwapBuffers(mEgl, display.eglSurface); if (res == EGL_FALSE) break; @@ -939,7 +909,7 @@ bool BootAnimation::android() { void BootAnimation::checkExit() { ATRACE_CALL(); - // Allow surface flinger to gracefully request shutdown + // Allow SurfaceFlinger to gracefully request shutdown char value[PROPERTY_VALUE_MAX]; property_get(EXIT_PROP_NAME, value, "0"); int exitnow = atoi(value); @@ -1085,7 +1055,8 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) { return status; } -void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) { +void BootAnimation::drawText(const char* str, const Font& font, bool bold, + int* x, int* y, const Display& display) { ATRACE_CALL(); glEnable(GL_BLEND); // Allow us to draw on top of the animation glBindTexture(GL_TEXTURE_2D, font.texture.name); @@ -1096,14 +1067,14 @@ void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* const int strWidth = font.char_width * len; if (*x == TEXT_CENTER_VALUE) { - *x = (mWidth - strWidth) / 2; + *x = (display.width - strWidth) / 2; } else if (*x < 0) { - *x = mWidth + *x - strWidth; + *x = display.width + *x - strWidth; } if (*y == TEXT_CENTER_VALUE) { - *y = (mHeight - font.char_height) / 2; + *y = (display.height - font.char_height) / 2; } else if (*y < 0) { - *y = mHeight + *y - font.char_height; + *y = display.height + *y - font.char_height; } for (int i = 0; i < len; i++) { @@ -1123,7 +1094,7 @@ void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* float v1 = v0 + 1.0f / FONT_NUM_ROWS / 2; float u1 = u0 + 1.0f / FONT_NUM_COLS; glUniform4f(mTextCropAreaLocation, u0, v0, u1, v1); - drawTexturedQuad(*x, *y, font.char_width, font.char_height); + drawTexturedQuad(*x, *y, font.char_width, font.char_height, display); *x += font.char_width; } @@ -1133,7 +1104,8 @@ void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* } // We render 12 or 24 hour time. -void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos) { +void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos, + const Display& display) { ATRACE_CALL(); static constexpr char TIME_FORMAT_12[] = "%l:%M"; static constexpr char TIME_FORMAT_24[] = "%H:%M"; @@ -1156,10 +1128,11 @@ void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos) char* out = timeBuff[0] == ' ' ? &timeBuff[1] : &timeBuff[0]; int x = xPos; int y = yPos; - drawText(out, font, false, &x, &y); + drawText(out, font, false, &x, &y, display); } -void BootAnimation::drawProgress(int percent, const Font& font, const int xPos, const int yPos) { +void BootAnimation::drawProgress(int percent, const Font& font, const int xPos, const int yPos, + const Display& display) { ATRACE_CALL(); static constexpr int PERCENT_LENGTH = 5; @@ -1169,7 +1142,7 @@ void BootAnimation::drawProgress(int percent, const Font& font, const int xPos, sprintf(percentBuff, "%d;", percent); int x = xPos; int y = yPos; - drawText(percentBuff, font, false, &x, &y); + drawText(percentBuff, font, false, &x, &y, display); } bool BootAnimation::parseAnimationDesc(Animation& animation) { @@ -1298,8 +1271,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { bool BootAnimation::preloadZip(Animation& animation) { ATRACE_CALL(); - // read all the data structures - const size_t pcount = animation.parts.size(); + const size_t numParts = animation.parts.size(); void *cookie = nullptr; ZipFileRO* zip = animation.zip; if (!zip->startIteration(&cookie)) { @@ -1335,8 +1307,8 @@ bool BootAnimation::preloadZip(Animation& animation) { continue; } - for (size_t j = 0; j < pcount; j++) { - if (path.string() == animation.parts[j].path.c_str()) { + for (size_t partIdx = 0; partIdx < numParts; partIdx++) { + if (path.string() == animation.parts[partIdx].path.c_str()) { uint16_t method; // supports only stored png files if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr, @@ -1344,7 +1316,7 @@ bool BootAnimation::preloadZip(Animation& animation) { if (method == ZipFileRO::kCompressStored) { FileMap* map = zip->createEntryFileMap(entry); if (map) { - Animation::Part& part(animation.parts.editItemAt(j)); + Animation::Part& part(animation.parts.editItemAt(partIdx)); if (leaf == "audio.wav") { // a part may have at most one audio file part.audioData = (uint8_t *)map->getDataPtr(); @@ -1375,7 +1347,8 @@ bool BootAnimation::preloadZip(Animation& animation) { // If there is trimData present, override the positioning defaults. for (Animation::Part& part : animation.parts) { const char* trimDataStr = part.trimData.c_str(); - for (size_t frameIdx = 0; frameIdx < part.frames.size(); frameIdx++) { + const size_t numFramesInPart = part.frames.size(); + for (size_t frameIdxInPart = 0; frameIdxInPart < numFramesInPart; frameIdxInPart++) { const char* endl = strstr(trimDataStr, "\n"); // No more trimData for this part. if (endl == nullptr) { @@ -1386,7 +1359,7 @@ bool BootAnimation::preloadZip(Animation& animation) { trimDataStr = ++endl; int width = 0, height = 0, x = 0, y = 0; if (sscanf(lineStr, "%dx%d+%d+%d", &width, &height, &x, &y) == 4) { - Animation::Frame& frame(part.frames.editItemAt(frameIdx)); + Animation::Frame& frame(part.frames.editItemAt(frameIdxInPart)); frame.trimWidth = width; frame.trimHeight = height; frame.trimX = x; @@ -1509,13 +1482,15 @@ float mapLinear(float x, float a1, float a2, float b1, float b2) { return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); } -void BootAnimation::drawTexturedQuad(float xStart, float yStart, float width, float height) { +void BootAnimation::drawTexturedQuad(float xStart, float yStart, + float width, float height, + const Display& display) { ATRACE_CALL(); // Map coordinates from screen space to world space. - float x0 = mapLinear(xStart, 0, mWidth, -1, 1); - float y0 = mapLinear(yStart, 0, mHeight, -1, 1); - float x1 = mapLinear(xStart + width, 0, mWidth, -1, 1); - float y1 = mapLinear(yStart + height, 0, mHeight, -1, 1); + float x0 = mapLinear(xStart, 0, display.width, -1, 1); + float y0 = mapLinear(yStart, 0, display.height, -1, 1); + float x1 = mapLinear(xStart + width, 0, display.width, -1, 1); + float y1 = mapLinear(yStart + height, 0, display.height, -1, 1); // Update quad vertex positions. quadPositions[0] = x0; quadPositions[1] = y0; @@ -1562,7 +1537,7 @@ void BootAnimation::initDynamicColors() { bool BootAnimation::playAnimation(const Animation& animation) { ATRACE_CALL(); - const size_t pcount = animation.parts.size(); + const size_t numParts = animation.parts.size(); nsecs_t frameDuration = s2ns(1) / animation.fps; SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", @@ -1572,9 +1547,9 @@ bool BootAnimation::playAnimation(const Animation& animation) { int lastDisplayedProgress = 0; int colorTransitionStart = animation.colorTransitionStart; int colorTransitionEnd = animation.colorTransitionEnd; - for (size_t i=0 ; i 0 ; r++) { + for (int frameIdx = 0; + !part.count || frameIdx < part.count || fadedFramesCount > 0; + frameIdx++) { if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break; // It's possible that the sysprops were not loaded yet at this boot phase. @@ -1601,12 +1578,12 @@ bool BootAnimation::playAnimation(const Animation& animation) { const int transitionLength = colorTransitionEnd - colorTransitionStart; if (part.postDynamicColoring) { colorTransitionStart = 0; - colorTransitionEnd = fmin(transitionLength, fcount - 1); + colorTransitionEnd = fmin(transitionLength, numFramesInPart - 1); } } } - mCallbacks->playPart(i, part, r); + mCallbacks->playPart(partIdx, part, frameIdx); glClearColor( part.backgroundColor[0], @@ -1620,11 +1597,10 @@ bool BootAnimation::playAnimation(const Animation& animation) { // For the last animation, if we have progress indicator from // the system, display it. - int currentProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0); - bool displayProgress = animation.progressEnabled && - (i == (pcount -1)) && currentProgress != 0; + const bool displayProgress = animation.progressEnabled && (partIdx == (numParts - 1)) && + android::base::GetIntProperty(PROGRESS_PROP_NAME, 0) != 0; - for (size_t j=0 ; j(frameIdxInPart) - colorTransitionStart) / fmax(colorTransitionEnd - colorTransitionStart, 1.0f), 0.0f), 1.0f) : (part.postDynamicColoring ? 1 : 0); - processDisplayEvents(); - const double ratio_w = static_cast(mWidth) / mInitWidth; - const double ratio_h = static_cast(mHeight) / mInitHeight; - const int animationX = (mWidth - animation.width * ratio_w) / 2; - const int animationY = (mHeight - animation.height * ratio_h) / 2; - - const Animation::Frame& frame(part.frames[j]); + const Animation::Frame& frame(part.frames[frameIdxInPart]); nsecs_t lastFrame = systemTime(); - if (r > 0) { + if (frameIdx > 0) { glBindTexture(GL_TEXTURE_2D, frame.tid); } else { if (part.count != 1) { @@ -1662,17 +1632,6 @@ bool BootAnimation::playAnimation(const Animation& animation) { initTexture(frame.map, &w, &h, false /* don't premultiply alpha */); } - const int trimWidth = frame.trimWidth * ratio_w; - const int trimHeight = frame.trimHeight * ratio_h; - const int trimX = frame.trimX * ratio_w; - const int trimY = frame.trimY * ratio_h; - const int xc = animationX + trimX; - const int yc = animationY + trimY; - glClear(GL_COLOR_BUFFER_BIT); - // specify the y center as ceiling((mHeight - frame.trimHeight) / 2) - // which is equivalent to mHeight - (yc + frame.trimHeight) - const int frameDrawY = mHeight - (yc + trimHeight); - float fade = 0; // if the part hasn't been stopped yet then continue fading if necessary if (exitPending() && part.hasFadingPhase()) { @@ -1681,40 +1640,66 @@ bool BootAnimation::playAnimation(const Animation& animation) { fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading } } - glUseProgram(mImageShader); - glUniform1i(mImageTextureLocation, 0); - glUniform1f(mImageFadeLocation, fade); - if (animation.dynamicColoringEnabled) { - glUniform1f(mImageColorProgressLocation, colorProgress); - } - glEnable(GL_BLEND); - drawTexturedQuad(xc, frameDrawY, trimWidth, trimHeight); - glDisable(GL_BLEND); - if (mClockEnabled && mTimeIsAccurate && validClock(part)) { - drawClock(animation.clockFont, part.clockPosX, part.clockPosY); - } + // Draw the current frame's texture on every physical display that is enabled. + for (const auto& display : mDisplays) { + eglMakeCurrent(mEgl, display.eglSurface, display.eglSurface, mEglContext); + + const double ratioW = + static_cast(display.width) / display.initWidth; + const double ratioH = + static_cast(display.height) / display.initHeight; + const int animationX = (display.width - animation.width * ratioW) / 2; + const int animationY = (display.height - animation.height * ratioH) / 2; + + const int trimWidth = frame.trimWidth * ratioW; + const int trimHeight = frame.trimHeight * ratioH; + const int trimX = frame.trimX * ratioW; + const int trimY = frame.trimY * ratioH; + const int xc = animationX + trimX; + const int yc = animationY + trimY; + projectSceneToWindow(display); + handleViewport(frameDuration, display); + glClear(GL_COLOR_BUFFER_BIT); + // specify the y center as ceiling((height - frame.trimHeight) / 2) + // which is equivalent to height - (yc + frame.trimHeight) + const int frameDrawY = display.height - (yc + trimHeight); + + glUseProgram(mImageShader); + glUniform1i(mImageTextureLocation, 0); + glUniform1f(mImageFadeLocation, fade); + if (animation.dynamicColoringEnabled) { + glUniform1f(mImageColorProgressLocation, colorProgress); + } + glEnable(GL_BLEND); + drawTexturedQuad(xc, frameDrawY, trimWidth, trimHeight, display); + glDisable(GL_BLEND); - if (displayProgress) { - int newProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0); - // In case the new progress jumped suddenly, still show an - // increment of 1. - if (lastDisplayedProgress != 100) { - // Artificially sleep 1/10th a second to slow down the animation. - usleep(100000); - if (lastDisplayedProgress < newProgress) { - lastDisplayedProgress++; - } + if (mClockEnabled && mTimeIsAccurate && validClock(part)) { + drawClock(animation.clockFont, part.clockPosX, part.clockPosY, display); } - // Put the progress percentage right below the animation. - int posY = animation.height / 3; - int posX = TEXT_CENTER_VALUE; - drawProgress(lastDisplayedProgress, animation.progressFont, posX, posY); - } - handleViewport(frameDuration); + if (displayProgress) { + int newProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0); + // In case the new progress jumped suddenly, still show an + // increment of 1. + if (lastDisplayedProgress != 100) { + // Artificially sleep 1/10th a second to slow down the animation. + usleep(100000); + if (lastDisplayedProgress < newProgress) { + lastDisplayedProgress++; + } + } + // Put the progress percentage right below the animation. + int posY = animation.height / 3; + int posX = TEXT_CENTER_VALUE; + drawProgress(lastDisplayedProgress, + animation.progressFont, posX, posY, display); + } + + eglSwapBuffers(mEgl, display.eglSurface); + } - eglSwapBuffers(mDisplay, mSurface); nsecs_t now = systemTime(); nsecs_t delay = frameDuration - (now - lastFrame); @@ -1760,9 +1745,7 @@ bool BootAnimation::playAnimation(const Animation& animation) { // Free textures created for looping parts now that the animation is done. for (const Animation::Part& part : animation.parts) { if (part.count != 1) { - const size_t fcount = part.frames.size(); - for (size_t j = 0; j < fcount; j++) { - const Animation::Frame& frame(part.frames[j]); + for (const auto& frame : part.frames) { glDeleteTextures(1, &frame.tid); } } @@ -1781,16 +1764,17 @@ void BootAnimation::processDisplayEvents() { mLooper->pollOnce(0); } -void BootAnimation::handleViewport(nsecs_t timestep) { +void BootAnimation::handleViewport(nsecs_t timestep, const Display& display) { ATRACE_CALL(); - if (mShuttingDown || !mFlingerSurfaceControl || mTargetInset == 0) { + if (mShuttingDown || !display.surfaceControl || mTargetInset == 0) { return; } if (mTargetInset < 0) { // Poll the amount for the top display inset. This will return -1 until persistent properties // have been loaded. - mTargetInset = android::base::GetIntProperty("persist.sys.displayinset.top", - -1 /* default */, -1 /* min */, mHeight / 2 /* max */); + mTargetInset = + android::base::GetIntProperty("persist.sys.displayinset.top", -1 /* default */, + -1 /* min */, display.height / 2 /* max */); } if (mTargetInset <= 0) { return; @@ -1802,19 +1786,27 @@ void BootAnimation::handleViewport(nsecs_t timestep) { int interpolatedInset = (cosf((fraction + 1) * M_PI) / 2.0f + 0.5f) * mTargetInset; SurfaceComposerClient::Transaction() - .setCrop(mFlingerSurfaceControl, Rect(0, interpolatedInset, mWidth, mHeight)) + .setCrop(display.surfaceControl, + Rect(0, interpolatedInset, display.width, display.height)) .apply(); } else { // At the end of the animation, we switch to the viewport that DisplayManager will apply // later. This changes the coordinate system, and means we must move the surface up by // the inset amount. - Rect layerStackRect(0, 0, mWidth, mHeight - mTargetInset); - Rect displayRect(0, mTargetInset, mWidth, mHeight); - + Rect layerStackRect(0, 0, + display.width, + display.height - mTargetInset); + Rect displayRect(0, mTargetInset, + display.width, + display.height); SurfaceComposerClient::Transaction t; - t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset) - .setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight)); - t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, layerStackRect, displayRect); + t.setPosition(display.surfaceControl, 0, -mTargetInset) + .setCrop(display.surfaceControl, + Rect(0, mTargetInset, + display.width, + display.height)); + t.setDisplayProjection(display.displayToken, + ui::ROTATION_0, layerStackRect, displayRect); t.apply(); mTargetInset = mCurrentInset = 0; diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 8683b71a3762b16755e9d3896bd0863b0a3e441c..0a057461228c7f1d717ce753ba6f67afed791259 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -119,6 +120,18 @@ public: float endColors[4][3]; // End colors of dynamic color transition. }; + // Collects all attributes that must be tracked per physical display. + struct Display { + int width; + int height; + int initWidth; + int initHeight; + EGLDisplay eglSurface; + sp displayToken; + sp surfaceControl; + sp surface; + }; + // All callbacks will be called from this class's internal thread. class Callbacks : public RefBase { public: @@ -181,14 +194,18 @@ private: bool premultiplyAlpha = true); status_t initFont(Font* font, const char* fallback); void initShaders(); - bool android(); + bool android(const Display& display); + status_t initDisplaysAndSurfaces(); bool movie(); - void drawText(const char* str, const Font& font, bool bold, int* x, int* y); - void drawClock(const Font& font, const int xPos, const int yPos); - void drawProgress(int percent, const Font& font, const int xPos, const int yPos); + void drawText(const char* str, const Font& font, bool bold, + int* x, int* y, const Display& display); + void drawClock(const Font& font, const int xPos, const int yPos, const Display& display); + void drawProgress(int percent, const Font& font, + const int xPos, const int yPos, const Display& display); void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight, const Animation::Part& part, int fadedFramesCount); - void drawTexturedQuad(float xStart, float yStart, float width, float height); + void drawTexturedQuad(float xStart, float yStart, + float width, float height, const Display& display); bool validClock(const Animation::Part& part); Animation* loadAnimation(const String8&); bool playAnimation(const Animation&); @@ -200,36 +217,31 @@ private: bool preloadAnimation(); EGLConfig getEglConfig(const EGLDisplay&); ui::Size limitSurfaceSize(int width, int height) const; - void resizeSurface(int newWidth, int newHeight); - void projectSceneToWindow(); - void rotateAwayFromNaturalOrientationIfNeeded(); + void resizeSurface(int newWidth, int newHeight, Display& display); + void projectSceneToWindow(const Display& display); + void rotateAwayFromNaturalOrientationIfNeeded(Display& display); ui::Rotation parseOrientationProperty(); + void configureDisplayAndLayerStack(const Display& display, ui::LayerStack layerStack); bool shouldStopPlayingPart(const Animation::Part& part, int fadedFramesCount, int lastDisplayedProgress); void checkExit(); - void handleViewport(nsecs_t timestep); + void handleViewport(nsecs_t timestep, const Display& display); void initDynamicColors(); sp mSession; AssetManager mAssets; Texture mAndroid[2]; - int mWidth; - int mHeight; - int mInitWidth; - int mInitHeight; int mMaxWidth = 0; int mMaxHeight = 0; int mCurrentInset; int mTargetInset; bool mUseNpotTextures = false; - EGLDisplay mDisplay; - EGLDisplay mContext; - EGLDisplay mSurface; - sp mDisplayToken; - sp mFlingerSurfaceControl; - sp mFlingerSurface; + EGLDisplay mEgl; + EGLDisplay mEglContext; + // Per-Display Attributes (to support multi-display) + std::vector mDisplays; bool mClockEnabled; bool mTimeIsAccurate; bool mTimeFormat12Hour; diff --git a/cmds/bootanimation/bootanimation_flags.aconfig b/cmds/bootanimation/bootanimation_flags.aconfig new file mode 100644 index 0000000000000000000000000000000000000000..04837b98e4142fcc582e4060d74129626c7b8314 --- /dev/null +++ b/cmds/bootanimation/bootanimation_flags.aconfig @@ -0,0 +1,12 @@ +package: "com.android.graphics.bootanimation.flags" +container: "system" + +flag { + name: "multidisplay" + namespace: "bootanimation" + description: "Enable boot animation on multiple displays (e.g. foldables)" + bug: "335406617" + is_fixed_read_only: true +} + + diff --git a/cmds/hid/Android.bp b/cmds/hid/Android.bp index a6e27698e36cc106deb16b1f5cdd67917a5df871..b93227a60d990c67fbdfcb4c4d4ef0e2e7a84e99 100644 --- a/cmds/hid/Android.bp +++ b/cmds/hid/Android.bp @@ -22,5 +22,5 @@ java_binary { name: "hid", wrapper: "hid.sh", srcs: ["**/*.java"], - required: ["libhidcommand_jni"], + jni_libs: ["libhidcommand_jni"], } diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h index e86f81485a41824a1e4c2cfd6164017f902b6bba..b0ba01957ab6dfac7176fda7243f4874c5e73102 100644 --- a/cmds/idmap2/include/idmap2/Idmap.h +++ b/cmds/idmap2/include/idmap2/Idmap.h @@ -21,18 +21,19 @@ * header := magic version target_crc overlay_crc fulfilled_policies * enforce_overlayable target_path overlay_path overlay_name * debug_info - * data := data_header target_entry* target_inline_entry* - target_inline_entry_value* config* overlay_entry* string_pool + * data := data_header target_entries target_inline_entries + target_inline_entry_value* config* overlay_entries string_pool * data_header := target_entry_count target_inline_entry_count target_inline_entry_value_count config_count overlay_entry_count * string_pool_index - * target_entry := target_id overlay_id - * target_inline_entry := target_id start_value_index value_count + * target_entries := target_id* overlay_id* + * target_inline_entries := target_id* target_inline_value_header* + * target_inline_value_header := start_value_index value_count * target_inline_entry_value := config_index Res_value::size padding(1) Res_value::type * Res_value::value * config := target_id Res_value::size padding(1) Res_value::type * Res_value::value - * overlay_entry := overlay_id target_id + * overlay_entries := overlay_id* target_id* * * debug_info := string * enforce_overlayable := diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp index 89769246434a080171a7d746206437aa95bf61ce..00ef0c7f8cf0a6a9a7561bfe34eedd65c655b525 100644 --- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp +++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp @@ -66,43 +66,57 @@ void BinaryStreamVisitor::visit(const IdmapHeader& header) { void BinaryStreamVisitor::visit(const IdmapData& data) { for (const auto& target_entry : data.GetTargetEntries()) { Write32(target_entry.target_id); + } + for (const auto& target_entry : data.GetTargetEntries()) { Write32(target_entry.overlay_id); } - static constexpr uint16_t kValueSize = 8U; - std::vector> target_values; - target_values.reserve(data.GetHeader()->GetTargetInlineEntryValueCount()); - for (const auto& target_entry : data.GetTargetInlineEntries()) { - Write32(target_entry.target_id); - Write32(target_values.size()); - Write32(target_entry.values.size()); - target_values.insert( - target_values.end(), target_entry.values.begin(), target_entry.values.end()); + uint32_t current_inline_entry_values_count = 0; + for (const auto& target_inline_entry : data.GetTargetInlineEntries()) { + Write32(target_inline_entry.target_id); + } + for (const auto& target_inline_entry : data.GetTargetInlineEntries()) { + Write32(current_inline_entry_values_count); + Write32(target_inline_entry.values.size()); + current_inline_entry_values_count += target_inline_entry.values.size(); } std::vector configs; configs.reserve(data.GetHeader()->GetConfigCount()); - for (const auto& target_entry_value : target_values) { - auto config_it = find(configs.begin(), configs.end(), target_entry_value.first); - if (config_it != configs.end()) { - Write32(config_it - configs.begin()); - } else { - Write32(configs.size()); - configs.push_back(target_entry_value.first); + for (const auto& target_entry : data.GetTargetInlineEntries()) { + for (const auto& target_entry_value : target_entry.values) { + auto config_it = std::find(configs.begin(), configs.end(), target_entry_value.first); + if (config_it != configs.end()) { + Write32(config_it - configs.begin()); + } else { + Write32(configs.size()); + configs.push_back(target_entry_value.first); + } + // We're writing a Res_value entry here, and the first 3 bytes of that are + // sizeof() + a padding 0 byte + static constexpr decltype(android::Res_value::size) kSize = sizeof(android::Res_value); + Write16(kSize); + Write8(0U); + Write8(target_entry_value.second.data_type); + Write32(target_entry_value.second.data_value); } - Write16(kValueSize); - Write8(0U); // padding - Write8(target_entry_value.second.data_type); - Write32(target_entry_value.second.data_value); } - for( auto& cd : configs) { - cd.swapHtoD(); - stream_.write(reinterpret_cast(&cd), sizeof(cd)); + if (!configs.empty()) { + stream_.write(reinterpret_cast(&configs.front()), + sizeof(configs.front()) * configs.size()); + if (configs.size() >= 100) { + // Let's write a message to future us so that they know when to replace the linear search + // in `configs` vector with something more efficient. + LOG(WARNING) << "Idmap got " << configs.size() + << " configurations, time to fix the bruteforce search"; + } } for (const auto& overlay_entry : data.GetOverlayEntries()) { Write32(overlay_entry.overlay_id); + } + for (const auto& overlay_entry : data.GetOverlayEntries()) { Write32(overlay_entry.target_id); } diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index 12d9dd9bd1adee775e62f3d6ec31121faba54629..7680109f1d541497435f68cbcaa6443709f7436e 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -204,73 +204,91 @@ std::unique_ptr IdmapData::FromBinaryStream(std::istream& strea } // Read the mapping of target resource id to overlay resource value. + data->target_entries_.resize(data->header_->GetTargetEntryCount()); for (size_t i = 0; i < data->header_->GetTargetEntryCount(); i++) { - TargetEntry target_entry{}; - if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &target_entry.overlay_id)) { + if (!Read32(stream, &data->target_entries_[i].target_id)) { + return nullptr; + } + } + for (size_t i = 0; i < data->header_->GetTargetEntryCount(); i++) { + if (!Read32(stream, &data->target_entries_[i].overlay_id)) { return nullptr; } - data->target_entries_.emplace_back(target_entry); } // Read the mapping of target resource id to inline overlay values. - std::vector> target_inline_entries; + struct TargetInlineEntryHeader { + ResourceId target_id; + uint32_t values_offset; + uint32_t values_count; + }; + std::vector target_inline_entries( + data->header_->GetTargetInlineEntryCount()); + for (size_t i = 0; i < data->header_->GetTargetInlineEntryCount(); i++) { + if (!Read32(stream, &target_inline_entries[i].target_id)) { + return nullptr; + } + } for (size_t i = 0; i < data->header_->GetTargetInlineEntryCount(); i++) { - TargetInlineEntry target_entry{}; - uint32_t entry_offset; - uint32_t entry_count; - if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &entry_offset) - || !Read32(stream, &entry_count)) { + if (!Read32(stream, &target_inline_entries[i].values_offset) || + !Read32(stream, &target_inline_entries[i].values_count)) { return nullptr; } - target_inline_entries.emplace_back(target_entry, entry_offset, entry_count); } // Read the inline overlay resource values - std::vector> target_values; - uint8_t unused1; - uint16_t unused2; - for (size_t i = 0; i < data->header_->GetTargetInlineEntryValueCount(); i++) { + struct TargetValueHeader { uint32_t config_index; - if (!Read32(stream, &config_index)) { + DataType data_type; + DataValue data_value; + }; + std::vector target_values(data->header_->GetTargetInlineEntryValueCount()); + for (size_t i = 0; i < data->header_->GetTargetInlineEntryValueCount(); i++) { + auto& value = target_values[i]; + if (!Read32(stream, &value.config_index)) { return nullptr; } - TargetValue value; - if (!Read16(stream, &unused2) - || !Read8(stream, &unused1) - || !Read8(stream, &value.data_type) - || !Read32(stream, &value.data_value)) { + // skip the padding + stream.seekg(3, std::ios::cur); + if (!Read8(stream, &value.data_type) || !Read32(stream, &value.data_value)) { return nullptr; } - target_values.emplace_back(config_index, value); } // Read the configurations - std::vector configurations; - for (size_t i = 0; i < data->header_->GetConfigCount(); i++) { - ConfigDescription cd; - if (!stream.read(reinterpret_cast(&cd), sizeof(ConfigDescription))) { + std::vector configurations(data->header_->GetConfigCount()); + if (!configurations.empty()) { + if (!stream.read(reinterpret_cast(&configurations.front()), + sizeof(configurations.front()) * configurations.size())) { return nullptr; } - configurations.emplace_back(cd); } // Construct complete target inline entries - for (auto [target_entry, entry_offset, entry_count] : target_inline_entries) { - for(size_t i = 0; i < entry_count; i++) { - const auto& target_value = target_values[entry_offset + i]; - const auto& config = configurations[target_value.first]; - target_entry.values[config] = target_value.second; + data->target_inline_entries_.reserve(target_inline_entries.size()); + for (auto&& entry_header : target_inline_entries) { + TargetInlineEntry& entry = data->target_inline_entries_.emplace_back(); + entry.target_id = entry_header.target_id; + for (size_t i = 0; i < entry_header.values_count; i++) { + const auto& value_header = target_values[entry_header.values_offset + i]; + const auto& config = configurations[value_header.config_index]; + auto& value = entry.values[config]; + value.data_type = value_header.data_type; + value.data_value = value_header.data_value; } - data->target_inline_entries_.emplace_back(target_entry); } // Read the mapping of overlay resource id to target resource id. + data->overlay_entries_.resize(data->header_->GetOverlayEntryCount()); + for (size_t i = 0; i < data->header_->GetOverlayEntryCount(); i++) { + if (!Read32(stream, &data->overlay_entries_[i].overlay_id)) { + return nullptr; + } + } for (size_t i = 0; i < data->header_->GetOverlayEntryCount(); i++) { - OverlayEntry overlay_entry{}; - if (!Read32(stream, &overlay_entry.overlay_id) || !Read32(stream, &overlay_entry.target_id)) { + if (!Read32(stream, &data->overlay_entries_[i].target_id)) { return nullptr; } - data->overlay_entries_.emplace_back(overlay_entry); } // Read raw string pool bytes. @@ -320,7 +338,7 @@ Result> IdmapData::FromResourceMapping( std::unique_ptr data(new IdmapData()); data->string_pool_data_ = std::string(resource_mapping.GetStringPoolData()); uint32_t inline_value_count = 0; - std::set config_set; + std::set config_set; for (const auto& mapping : resource_mapping.GetTargetToOverlayMap()) { if (auto overlay_resource = std::get_if(&mapping.second)) { data->target_entries_.push_back({mapping.first, *overlay_resource}); @@ -329,7 +347,9 @@ Result> IdmapData::FromResourceMapping( for (const auto& [config, value] : std::get(mapping.second)) { config_set.insert(config); ConfigDescription cd; - ConfigDescription::Parse(config, &cd); + if (!ConfigDescription::Parse(config, &cd)) { + return Error("failed to parse configuration string '%s'", config.c_str()); + } values[cd] = value; inline_value_count++; } diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index c85619c1e4bfb265e92da14af7f82a58ed6883c1..1b656e8c2088f3480b7251a5783ddb813b6ca7e5 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -68,7 +68,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) { std::unique_ptr header = IdmapHeader::FromBinaryStream(stream); ASSERT_THAT(header, NotNull()); ASSERT_EQ(header->GetMagic(), 0x504d4449U); - ASSERT_EQ(header->GetVersion(), 0x09U); + ASSERT_EQ(header->GetVersion(), 10); ASSERT_EQ(header->GetTargetCrc(), 0x1234U); ASSERT_EQ(header->GetOverlayCrc(), 0x5678U); ASSERT_EQ(header->GetFulfilledPolicies(), 0x11); @@ -143,7 +143,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x09U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 10); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U); ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), kIdmapRawDataPolicies); @@ -204,7 +204,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x09U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 10); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC); ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC); diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index 68164e26f352653874560f54d7b505fda2e3321c..7fae1c64f0143cffa978874972c7f880c7ffe8ad 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -64,7 +64,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { (*idmap)->accept(&visitor); ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "00000009 version\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "0000000a version\n", stream.str()); ASSERT_CONTAINS_REGEX( StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING), stream.str()); @@ -113,7 +113,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { (*idmap)->accept(&visitor); ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "00000009 version\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "0000000a version\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str()); diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h index bf01c32c4c86ad8aaf82089f43cf8c2c4cbb491e..2b4ebd1ae800352ada3a6145ae21d853bebcb462 100644 --- a/cmds/idmap2/tests/TestHelpers.h +++ b/cmds/idmap2/tests/TestHelpers.h @@ -34,7 +34,7 @@ const unsigned char kIdmapRawData[] = { 0x49, 0x44, 0x4d, 0x50, // 0x4: version - 0x09, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, // 0x8: target crc 0x34, 0x12, 0x00, 0x00, @@ -95,19 +95,15 @@ const unsigned char kIdmapRawData[] = { // TARGET ENTRIES // 0x6c: target id (0x7f020000) 0x00, 0x00, 0x02, 0x7f, - - // 0x70: overlay_id (0x7f020000) - 0x00, 0x00, 0x02, 0x7f, - - // 0x74: target id (0x7f030000) - 0x00, 0x00, 0x03, 0x7f, - - // 0x78: overlay_id (0x7f030000) + // 0x70: target id (0x7f030000) 0x00, 0x00, 0x03, 0x7f, - - // 0x7c: target id (0x7f030002) + // 0x74: target id (0x7f030002) 0x02, 0x00, 0x03, 0x7f, + // 0x78: overlay_id (0x7f020000) + 0x00, 0x00, 0x02, 0x7f, + // 0x7c: overlay_id (0x7f030000) + 0x00, 0x00, 0x03, 0x7f, // 0x80: overlay_id (0x7f030001) 0x01, 0x00, 0x03, 0x7f, @@ -178,16 +174,20 @@ const unsigned char kIdmapRawData[] = { // 0xe1: padding 0x00, 0x00, 0x00, - // OVERLAY ENTRIES - // 0xe4: 0x7f020000 -> 0x7f020000 - 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f, - - // 0xec: 0x7f030000 -> 0x7f030000 - 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f, + // 0xe4: 0x7f020000 -> ... + 0x00, 0x00, 0x02, 0x7f, + // 0xe8: 0x7f030000 -> ... + 0x00, 0x00, 0x03, 0x7f, + // 0xec: 0x7f030001 -> ... + 0x01, 0x00, 0x03, 0x7f, - // 0xf4: 0x7f030001 -> 0x7f030002 - 0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f, + // 0xf0: ... -> 0x7f020000 + 0x00, 0x00, 0x02, 0x7f, + // 0xf4: ... -> 0x7f030000 + 0x00, 0x00, 0x03, 0x7f, + // 0xf8: ... -> 0x7f030002 + 0x02, 0x00, 0x03, 0x7f, // 0xfc: string pool // string length, diff --git a/cmds/uinput/Android.bp b/cmds/uinput/Android.bp index da497dcf908ed9bbba5c462f74ffc7efe229ac87..cec8a0d88b998af54a67f4b4038c374c45368ed7 100644 --- a/cmds/uinput/Android.bp +++ b/cmds/uinput/Android.bp @@ -25,7 +25,7 @@ java_binary { "src/**/*.java", ":uinputcommand_aidl", ], - required: ["libuinputcommand_jni"], + jni_libs: ["libuinputcommand_jni"], } filegroup { diff --git a/core/api/current.txt b/core/api/current.txt index d1c0c42d1487789dd5899f5b340b0a5defb15917..ebc534f6e39162a73920a7df8a2789bc30b18a33 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4950,7 +4950,7 @@ package android.app { field @Deprecated @FlaggedApi("com.android.window.flags.bal_additional_start_modes") public static final int MODE_BACKGROUND_ACTIVITY_START_ALLOWED = 1; // 0x1 field @FlaggedApi("com.android.window.flags.bal_additional_start_modes") public static final int MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS = 3; // 0x3 field @FlaggedApi("com.android.window.flags.bal_additional_start_modes") public static final int MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE = 4; // 0x4 - field @FlaggedApi("com.android.window.flags.bal_additional_start_modes") public static final int MODE_BACKGROUND_ACTIVITY_START_DENIED = 2; // 0x2 + field public static final int MODE_BACKGROUND_ACTIVITY_START_DENIED = 2; // 0x2 field public static final int MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED = 0; // 0x0 } @@ -5103,6 +5103,7 @@ package android.app { method public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int, @Nullable String, @Nullable String); method @Nullable public static String permissionToOp(@NonNull String); method public void setOnOpNotedCallback(@Nullable java.util.concurrent.Executor, @Nullable android.app.AppOpsManager.OnOpNotedCallback); + method @FlaggedApi("android.permission.flags.sync_on_op_noted_api") public void setOnOpNotedCallback(@Nullable java.util.concurrent.Executor, @Nullable android.app.AppOpsManager.OnOpNotedCallback, int); method @Deprecated public int startOp(@NonNull String, int, @NonNull String); method public int startOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String); method @Deprecated public int startOpNoThrow(@NonNull String, int, @NonNull String); @@ -5157,6 +5158,7 @@ package android.app { field public static final String OPSTR_WRITE_CONTACTS = "android:write_contacts"; field public static final String OPSTR_WRITE_EXTERNAL_STORAGE = "android:write_external_storage"; field public static final String OPSTR_WRITE_SETTINGS = "android:write_settings"; + field @FlaggedApi("android.permission.flags.sync_on_op_noted_api") public static final int OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC = 1; // 0x1 field public static final int WATCH_FOREGROUND_CHANGES = 1; // 0x1 } @@ -8792,7 +8794,8 @@ package android.app.appfunctions { ctor public AppFunctionService(); method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); method @Deprecated @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer); - method @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer); + method @Deprecated @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer); + method @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer); field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService"; } @@ -9834,6 +9837,7 @@ package android.companion { public final class AssociationInfo implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.companion.AssociatedDevice getAssociatedDevice(); + method @FlaggedApi("android.companion.association_device_icon") @Nullable public android.graphics.drawable.Icon getDeviceIcon(); method @Nullable public android.net.MacAddress getDeviceMacAddress(); method @Nullable public String getDeviceProfile(); method @Nullable public CharSequence getDisplayName(); @@ -9847,6 +9851,7 @@ package android.companion { public final class AssociationRequest implements android.os.Parcelable { method public int describeContents(); + method @FlaggedApi("android.companion.association_device_icon") @Nullable public android.graphics.drawable.Icon getDeviceIcon(); method @Nullable public String getDeviceProfile(); method @Nullable public CharSequence getDisplayName(); method public boolean isForceConfirmation(); @@ -9866,6 +9871,7 @@ package android.companion { ctor public AssociationRequest.Builder(); method @NonNull public android.companion.AssociationRequest.Builder addDeviceFilter(@Nullable android.companion.DeviceFilter); method @NonNull public android.companion.AssociationRequest build(); + method @FlaggedApi("android.companion.association_device_icon") @NonNull @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public android.companion.AssociationRequest.Builder setDeviceIcon(@NonNull android.graphics.drawable.Icon); method @NonNull public android.companion.AssociationRequest.Builder setDeviceProfile(@NonNull String); method @NonNull public android.companion.AssociationRequest.Builder setDisplayName(@NonNull CharSequence); method @NonNull @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public android.companion.AssociationRequest.Builder setForceConfirmation(boolean); @@ -12286,6 +12292,7 @@ package android.content.pm { method public int getMemtagMode(); method public int getNativeHeapZeroInitialized(); method public int getRequestRawExternalStorageAccess(); + method @FlaggedApi("android.content.pm.audio_playback_capture_allowance") public boolean isAudioPlaybackCaptureAllowed(); method public boolean isProfileable(); method public boolean isProfileableByShell(); method public boolean isResourceOverlay(); @@ -21415,6 +21422,7 @@ package android.media { field public static final int ENCODING_AAC_XHE = 16; // 0x10 field public static final int ENCODING_AC3 = 5; // 0x5 field public static final int ENCODING_AC4 = 17; // 0x11 + field @FlaggedApi("android.media.audio.dolby_ac4_level4_encoding_api") public static final int ENCODING_AC4_L4 = 32; // 0x20 field public static final int ENCODING_DEFAULT = 1; // 0x1 field public static final int ENCODING_DOLBY_MAT = 19; // 0x13 field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe @@ -22600,6 +22608,7 @@ package android.media { method public void sendEvent(int, int, @Nullable byte[]) throws android.media.MediaCasException; method public void setEventListener(@Nullable android.media.MediaCas.EventListener, @Nullable android.os.Handler); method public void setPrivateData(@NonNull byte[]) throws android.media.MediaCasException; + method @FlaggedApi("com.android.media.flags.update_client_profile_priority") public boolean updateResourcePriority(int, int); field public static final int PLUGIN_STATUS_PHYSICAL_MODULE_CHANGED = 0; // 0x0 field public static final int PLUGIN_STATUS_SESSION_NUMBER_CHANGED = 1; // 0x1 field public static final int SCRAMBLING_MODE_AES128 = 9; // 0x9 @@ -26918,7 +26927,6 @@ package android.media.session { field public static final int STATE_FAST_FORWARDING = 4; // 0x4 field public static final int STATE_NONE = 0; // 0x0 field public static final int STATE_PAUSED = 2; // 0x2 - field @FlaggedApi("com.android.media.flags.enable_notifying_activity_manager_with_media_session_status_change") public static final int STATE_PLAYBACK_SUPPRESSED = 12; // 0xc field public static final int STATE_PLAYING = 3; // 0x3 field public static final int STATE_REWINDING = 5; // 0x5 field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa @@ -33923,9 +33931,12 @@ package android.os { public class RemoteCallbackList { ctor public RemoteCallbackList(); method public int beginBroadcast(); + method @FlaggedApi("android.os.binder_frozen_state_change_callback") public void broadcast(@NonNull java.util.function.Consumer); method public void finishBroadcast(); method public Object getBroadcastCookie(int); method public E getBroadcastItem(int); + method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getFrozenCalleePolicy(); + method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getMaxQueueSize(); method public Object getRegisteredCallbackCookie(int); method public int getRegisteredCallbackCount(); method public E getRegisteredCallbackItem(int); @@ -33935,6 +33946,16 @@ package android.os { method public boolean register(E); method public boolean register(E, Object); method public boolean unregister(E); + field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_DROP = 3; // 0x3 + field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_ENQUEUE_ALL = 1; // 0x1 + field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT = 2; // 0x2 + field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_UNSET = 0; // 0x0 + } + + @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final class RemoteCallbackList.Builder { + ctor public RemoteCallbackList.Builder(int); + method @NonNull public android.os.RemoteCallbackList build(); + method @NonNull public android.os.RemoteCallbackList.Builder setMaxQueueSize(int); } public class RemoteException extends android.util.AndroidException { @@ -34334,6 +34355,7 @@ package android.os { method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public boolean areEnvelopeEffectsSupported(); method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...); method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel(); + method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @Nullable public android.os.vibrator.VibratorFrequencyProfile getFrequencyProfile(); method public int getId(); method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMaxEnvelopeEffectControlPointDurationMillis(); method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMaxEnvelopeEffectDurationMillis(); @@ -34687,6 +34709,19 @@ package android.os.strictmode { } +package android.os.vibrator { + + @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public final class VibratorFrequencyProfile { + method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.util.SparseArray getFrequenciesOutputAcceleration(); + method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @Nullable public android.util.Range getFrequencyRange(float); + method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public float getMaxFrequencyHz(); + method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public float getMaxOutputAccelerationGs(); + method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public float getMinFrequencyHz(); + method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public float getOutputAccelerationGs(float); + } + +} + package android.preference { @Deprecated public class CheckBoxPreference extends android.preference.TwoStatePreference { @@ -49240,6 +49275,7 @@ package android.text.style { field public static final String ARG_PROTOCOL = "android.arg.protocol"; field public static final String ARG_QUANTITY = "android.arg.quantity"; field public static final String ARG_QUERY_STRING = "android.arg.query_string"; + field @FlaggedApi("com.android.text.flags.tts_span_duration") public static final String ARG_SECONDS = "android.arg.seconds"; field public static final String ARG_TEXT = "android.arg.text"; field public static final String ARG_UNIT = "android.arg.unit"; field public static final String ARG_USERNAME = "android.arg.username"; @@ -49276,6 +49312,7 @@ package android.text.style { field public static final String TYPE_DATE = "android.type.date"; field public static final String TYPE_DECIMAL = "android.type.decimal"; field public static final String TYPE_DIGITS = "android.type.digits"; + field @FlaggedApi("com.android.text.flags.tts_span_duration") public static final String TYPE_DURATION = "android.type.duration"; field public static final String TYPE_ELECTRONIC = "android.type.electronic"; field public static final String TYPE_FRACTION = "android.type.fraction"; field public static final String TYPE_MEASURE = "android.type.measure"; @@ -49335,6 +49372,13 @@ package android.text.style { method public android.text.style.TtsSpan.DigitsBuilder setDigits(String); } + @FlaggedApi("com.android.text.flags.tts_span_duration") public static class TtsSpan.DurationBuilder extends android.text.style.TtsSpan.SemioticClassBuilder { + ctor @FlaggedApi("com.android.text.flags.tts_span_duration") public TtsSpan.DurationBuilder(); + method @FlaggedApi("com.android.text.flags.tts_span_duration") @NonNull public android.text.style.TtsSpan.DurationBuilder setHours(int); + method @FlaggedApi("com.android.text.flags.tts_span_duration") @NonNull public android.text.style.TtsSpan.DurationBuilder setMinutes(int); + method @FlaggedApi("com.android.text.flags.tts_span_duration") @NonNull public android.text.style.TtsSpan.DurationBuilder setSeconds(int); + } + public static class TtsSpan.ElectronicBuilder extends android.text.style.TtsSpan.SemioticClassBuilder { ctor public TtsSpan.ElectronicBuilder(); method public android.text.style.TtsSpan.ElectronicBuilder setDomain(String); @@ -49417,6 +49461,7 @@ package android.text.style { ctor public TtsSpan.TimeBuilder(int, int); method public android.text.style.TtsSpan.TimeBuilder setHours(int); method public android.text.style.TtsSpan.TimeBuilder setMinutes(int); + method @FlaggedApi("com.android.text.flags.tts_span_duration") @NonNull public android.text.style.TtsSpan.TimeBuilder setSeconds(int); } public static class TtsSpan.VerbatimBuilder extends android.text.style.TtsSpan.SemioticClassBuilder { @@ -52549,10 +52594,12 @@ package android.view { ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int); ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int, int); method public void applyTransactionToFrame(@NonNull android.view.SurfaceControl.Transaction); + method @FlaggedApi("android.view.flags.surface_view_set_composition_order") public int getCompositionOrder(); method public android.view.SurfaceHolder getHolder(); method @Deprecated @Nullable public android.os.IBinder getHostToken(); method public android.view.SurfaceControl getSurfaceControl(); method public void setChildSurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage); + method @FlaggedApi("android.view.flags.surface_view_set_composition_order") public void setCompositionOrder(int); method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public void setDesiredHdrHeadroom(@FloatRange(from=0.0f, to=10000.0) float); method public void setSecure(boolean); method public void setSurfaceLifecycle(int); @@ -54890,6 +54937,7 @@ package android.view.accessibility { method public void setPackageName(CharSequence); method public void setSpeechStateChangeTypes(int); method public void writeToParcel(android.os.Parcel, int); + field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CONTENT_CHANGE_TYPE_CHECKED = 8192; // 0x2000 field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4 field public static final int CONTENT_CHANGE_TYPE_CONTENT_INVALID = 1024; // 0x400 field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200 @@ -55035,6 +55083,7 @@ package android.view.accessibility { method @Deprecated public void getBoundsInParent(android.graphics.Rect); method public void getBoundsInScreen(android.graphics.Rect); method public void getBoundsInWindow(@NonNull android.graphics.Rect); + method @FlaggedApi("android.view.accessibility.tri_state_checked") public int getChecked(); method public android.view.accessibility.AccessibilityNodeInfo getChild(int); method @Nullable public android.view.accessibility.AccessibilityNodeInfo getChild(int, int); method public int getChildCount(); @@ -55077,7 +55126,7 @@ package android.view.accessibility { method public boolean isAccessibilityDataSensitive(); method public boolean isAccessibilityFocused(); method public boolean isCheckable(); - method public boolean isChecked(); + method @Deprecated @FlaggedApi("android.view.accessibility.tri_state_checked") public boolean isChecked(); method public boolean isClickable(); method public boolean isContentInvalid(); method public boolean isContextClickable(); @@ -55122,7 +55171,8 @@ package android.view.accessibility { method public void setBoundsInWindow(@NonNull android.graphics.Rect); method public void setCanOpenPopup(boolean); method public void setCheckable(boolean); - method public void setChecked(boolean); + method @Deprecated @FlaggedApi("android.view.accessibility.tri_state_checked") public void setChecked(boolean); + method @FlaggedApi("android.view.accessibility.tri_state_checked") public void setChecked(int); method public void setClassName(CharSequence); method public void setClickable(boolean); method public void setCollectionInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionInfo); @@ -55218,6 +55268,9 @@ package android.view.accessibility { field public static final int ACTION_SELECT = 4; // 0x4 field public static final int ACTION_SET_SELECTION = 131072; // 0x20000 field public static final int ACTION_SET_TEXT = 2097152; // 0x200000 + field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_FALSE = 0; // 0x0 + field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_PARTIAL = 2; // 0x2 + field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_TRUE = 1; // 0x1 field @NonNull public static final android.os.Parcelable.Creator CREATOR; field public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY"; field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH"; diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 287e7874be4118227b27bd8d3a9fd56a7ace69ca..4d1a42314d976c5d63df7dbf12a415b46cd3875e 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -321,7 +321,7 @@ package android.net.netstats { package android.net.wifi { public final class WifiMigration { - method @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration_read_only") public static int migrateLegacyKeystoreToWifiBlobstore(); + method @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration_read_only") public static void migrateLegacyKeystoreToWifiBlobstore(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer); field @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration_read_only") public static final int KEYSTORE_MIGRATION_FAILURE_ENCOUNTERED_EXCEPTION = 2; // 0x2 field @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration_read_only") public static final int KEYSTORE_MIGRATION_SUCCESS_MIGRATION_COMPLETE = 0; // 0x0 field @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration_read_only") public static final int KEYSTORE_MIGRATION_SUCCESS_MIGRATION_NOT_NEEDED = 1; // 0x1 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 7e43e4664d8a5787d9425598df27c1f3ae044d6f..49b711b8b013f6cd76e619b883ffeffe31b48b21 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -694,6 +694,7 @@ package android.app { field public static final String OPSTR_PROJECT_MEDIA = "android:project_media"; field @FlaggedApi("android.view.contentprotection.flags.rapid_clear_notifications_by_listener_app_op_enabled") public static final String OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER = "android:rapid_clear_notifications_by_listener"; field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard"; + field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") public static final String OPSTR_READ_HEART_RATE = "android:read_heart_rate"; field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms"; field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio"; field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images"; @@ -3488,39 +3489,41 @@ package android.companion.virtual { public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable { method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener); - method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull android.content.ComponentName); - method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption); + method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void addActivityPolicyExemption(@NonNull android.content.ComponentName); + method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void addActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption); method public void addSoundEffectListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); + method public void close(); method @NonNull public android.content.Context createContext(); - method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback); - method @FlaggedApi("android.companion.virtual.flags.virtual_camera") @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.camera.VirtualCamera createVirtualCamera(@NonNull android.companion.virtual.camera.VirtualCameraConfig); + method @NonNull public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback); + method @FlaggedApi("android.companion.virtual.flags.virtual_camera") @NonNull public android.companion.virtual.camera.VirtualCamera createVirtualCamera(@NonNull android.companion.virtual.camera.VirtualCameraConfig); method @Deprecated @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); - method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualDpad createVirtualDpad(@NonNull android.hardware.input.VirtualDpadConfig); - method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.input.VirtualKeyboardConfig); - method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); - method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.input.VirtualMouseConfig); - method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); - method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualNavigationTouchpad createVirtualNavigationTouchpad(@NonNull android.hardware.input.VirtualNavigationTouchpadConfig); - method @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull android.hardware.input.VirtualRotaryEncoderConfig); - method @FlaggedApi("android.companion.virtual.flags.virtual_stylus") @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualStylus createVirtualStylus(@NonNull android.hardware.input.VirtualStylusConfig); - method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig); - method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); + method @NonNull public android.hardware.input.VirtualDpad createVirtualDpad(@NonNull android.hardware.input.VirtualDpadConfig); + method @NonNull public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.input.VirtualKeyboardConfig); + method @Deprecated @NonNull public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); + method @NonNull public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.input.VirtualMouseConfig); + method @Deprecated @NonNull public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); + method @NonNull public android.hardware.input.VirtualNavigationTouchpad createVirtualNavigationTouchpad(@NonNull android.hardware.input.VirtualNavigationTouchpadConfig); + method @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") @NonNull public android.hardware.input.VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull android.hardware.input.VirtualRotaryEncoderConfig); + method @FlaggedApi("android.companion.virtual.flags.virtual_stylus") @NonNull public android.hardware.input.VirtualStylus createVirtualStylus(@NonNull android.hardware.input.VirtualStylusConfig); + method @NonNull public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig); + method @Deprecated @NonNull public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); method public int getDeviceId(); method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @Nullable public String getPersistentDeviceId(); method @NonNull public java.util.List getVirtualSensorList(); + method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public void goToSleep(); method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback); + method public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback); method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener); - method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName); - method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption); + method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void removeActivityPolicyExemption(@NonNull android.content.ComponentName); + method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void removeActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption); method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener); - method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int); - method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int, int); - method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDisplayImePolicy(int, int); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback); + method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void setDevicePolicy(int, int); + method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void setDevicePolicy(int, int, int); + method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public void setDisplayImePolicy(int, int); + method public void setShowPointerIcon(boolean); + method public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback); + method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public void wakeUp(); } public final class VirtualDeviceParams implements android.os.Parcelable { @@ -3629,7 +3632,7 @@ package android.companion.virtual.audio { package android.companion.virtual.camera { @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCamera implements java.io.Closeable { - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); + method public void close(); method @NonNull public android.companion.virtual.camera.VirtualCameraConfig getConfig(); } @@ -3681,7 +3684,7 @@ package android.companion.virtual.sensor { method public int getDeviceId(); method @NonNull public String getName(); method public int getType(); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendEvent(@NonNull android.companion.virtual.sensor.VirtualSensorEvent); + method public void sendEvent(@NonNull android.companion.virtual.sensor.VirtualSensorEvent); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; } @@ -5692,8 +5695,8 @@ package android.hardware.hdmi { package android.hardware.input { public class VirtualDpad implements java.io.Closeable { - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendKeyEvent(@NonNull android.hardware.input.VirtualKeyEvent); + method public void close(); + method public void sendKeyEvent(@NonNull android.hardware.input.VirtualKeyEvent); } public final class VirtualDpadConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable { @@ -5744,8 +5747,8 @@ package android.hardware.input { } public class VirtualKeyboard implements java.io.Closeable { - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendKeyEvent(@NonNull android.hardware.input.VirtualKeyEvent); + method public void close(); + method public void sendKeyEvent(@NonNull android.hardware.input.VirtualKeyEvent); } public final class VirtualKeyboardConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable { @@ -5766,11 +5769,11 @@ package android.hardware.input { } public class VirtualMouse implements java.io.Closeable { - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); - method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.graphics.PointF getCursorPosition(); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull android.hardware.input.VirtualMouseButtonEvent); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendRelativeEvent(@NonNull android.hardware.input.VirtualMouseRelativeEvent); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull android.hardware.input.VirtualMouseScrollEvent); + method public void close(); + method @NonNull public android.graphics.PointF getCursorPosition(); + method public void sendButtonEvent(@NonNull android.hardware.input.VirtualMouseButtonEvent); + method public void sendRelativeEvent(@NonNull android.hardware.input.VirtualMouseRelativeEvent); + method public void sendScrollEvent(@NonNull android.hardware.input.VirtualMouseScrollEvent); } public final class VirtualMouseButtonEvent implements android.os.Parcelable { @@ -5843,8 +5846,8 @@ package android.hardware.input { } public class VirtualNavigationTouchpad implements java.io.Closeable { - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendTouchEvent(@NonNull android.hardware.input.VirtualTouchEvent); + method public void close(); + method public void sendTouchEvent(@NonNull android.hardware.input.VirtualTouchEvent); } public final class VirtualNavigationTouchpadConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable { @@ -5861,8 +5864,8 @@ package android.hardware.input { } @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") public class VirtualRotaryEncoder implements java.io.Closeable { - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull android.hardware.input.VirtualRotaryEncoderScrollEvent); + method public void close(); + method public void sendScrollEvent(@NonNull android.hardware.input.VirtualRotaryEncoderScrollEvent); } @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") public final class VirtualRotaryEncoderConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable { @@ -5892,9 +5895,9 @@ package android.hardware.input { } @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public class VirtualStylus implements java.io.Closeable { - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull android.hardware.input.VirtualStylusButtonEvent); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendMotionEvent(@NonNull android.hardware.input.VirtualStylusMotionEvent); + method public void close(); + method public void sendButtonEvent(@NonNull android.hardware.input.VirtualStylusButtonEvent); + method public void sendMotionEvent(@NonNull android.hardware.input.VirtualStylusMotionEvent); } @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public final class VirtualStylusButtonEvent implements android.os.Parcelable { @@ -5997,8 +6000,8 @@ package android.hardware.input { } public class VirtualTouchscreen implements java.io.Closeable { - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); - method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendTouchEvent(@NonNull android.hardware.input.VirtualTouchEvent); + method public void close(); + method public void sendTouchEvent(@NonNull android.hardware.input.VirtualTouchEvent); } public final class VirtualTouchscreenConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable { @@ -8540,11 +8543,14 @@ package android.media.tv.tuner.filter { method public long getAudioHandle(); method @NonNull public java.util.List getAudioPresentations(); method public long getAvDataId(); + method @FlaggedApi("android.media.tv.flags.tuner_w_apis") public int getDataGroupId(); method public long getDataLength(); method public long getDts(); method @Nullable public android.media.tv.tuner.filter.AudioDescriptor getExtraMetaData(); + method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @IntRange(from=0) public int getIndexInDataGroup(); method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock(); method @IntRange(from=0) public int getMpuSequenceNumber(); + method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @IntRange(from=0) public int getNumDataPieces(); method public long getOffset(); method public long getPts(); method public int getScIndexMask(); @@ -11951,6 +11957,11 @@ package android.provider { } @FlaggedApi("android.provider.new_default_account_api_enabled") public static final class ContactsContract.RawContacts.DefaultAccount { + method @FlaggedApi("android.provider.new_default_account_api_enabled") @NonNull @RequiresPermission(android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS) public static java.util.List getEligibleCloudAccounts(@NonNull android.content.ContentResolver); + method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(allOf={android.Manifest.permission.READ_CONTACTS, android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) public static int getNumberOfMovableLocalContacts(@NonNull android.content.ContentResolver); + method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(allOf={android.Manifest.permission.READ_CONTACTS, android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) public static int getNumberOfMovableSimContacts(@NonNull android.content.ContentResolver); + method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(allOf={android.Manifest.permission.WRITE_CONTACTS, android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) public static void moveLocalContactsToCloudDefaultAccount(@NonNull android.content.ContentResolver); + method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(allOf={android.Manifest.permission.WRITE_CONTACTS, android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) public static void moveSimContactsToCloudDefaultAccount(@NonNull android.content.ContentResolver); method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS) public static void setDefaultAccountForNewContacts(@NonNull android.content.ContentResolver, @NonNull android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState); } @@ -15415,6 +15426,7 @@ package android.telephony { field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7 field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22 field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_EMERGENCY_CALLBACK_MODE_CHANGED = 40; // 0x28 field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19 field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36; // 0x24 @@ -15452,6 +15464,12 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int); } + @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static interface TelephonyCallback.EmergencyCallbackModeListener { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onCallbackModeRestarted(int, @NonNull java.time.Duration, int); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onCallbackModeStarted(int, @NonNull java.time.Duration, int); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onCallbackModeStopped(int, int, int); + } + public static interface TelephonyCallback.LinkCapacityEstimateChangedListener { method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onLinkCapacityEstimateChanged(@NonNull java.util.List); } @@ -15713,6 +15731,8 @@ package android.telephony { field public static final int CELL_BROADCAST_RESULT_SUCCESS = 0; // 0x0 field public static final int CELL_BROADCAST_RESULT_UNKNOWN = -1; // 0xffffffff field public static final int CELL_BROADCAST_RESULT_UNSUPPORTED = 1; // 0x1 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int EMERGENCY_CALLBACK_MODE_CALL = 1; // 0x1 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int EMERGENCY_CALLBACK_MODE_SMS = 2; // 0x2 field public static final int ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE = 4; // 0x4 field public static final int ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED = 1; // 0x1 field public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR = 3; // 0x3 @@ -15770,6 +15790,13 @@ package android.telephony { field public static final int SRVCC_STATE_HANDOVER_FAILED = 2; // 0x2 field public static final int SRVCC_STATE_HANDOVER_NONE = -1; // 0xffffffff field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_EMERGENCY_SMS_SENT = 4; // 0x4 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_NORMAL_SMS_SENT = 2; // 0x2 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED = 3; // 0x3 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED = 1; // 0x1 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_TIMER_EXPIRED = 5; // 0x5 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_UNKNOWN = 0; // 0x0 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_USER_ACTION = 6; // 0x6 field public static final int THERMAL_MITIGATION_RESULT_INVALID_STATE = 3; // 0x3 field public static final int THERMAL_MITIGATION_RESULT_MODEM_ERROR = 1; // 0x1 field public static final int THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE = 2; // 0x2 @@ -18548,6 +18575,7 @@ package android.webkit { method @Deprecated public abstract void setUserAgent(int); method public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean); field public static final long ENABLE_SIMPLIFIED_DARK_MODE = 214741472L; // 0xcccb1e0L + field @FlaggedApi("android.webkit.user_agent_reduction") public static final long ENABLE_USER_AGENT_REDUCTION = 371034303L; // 0x161d88bfL } public class WebStorage { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 9bcdf959a6a7f55046727f99a1d2e3c35cf1cb4f..5e4485c33233f0cc5d367c48e40274ca42554929 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -914,6 +914,7 @@ package android.companion { ctor public AssociationInfo.Builder(@NonNull android.companion.AssociationInfo); method @NonNull public android.companion.AssociationInfo build(); method @NonNull public android.companion.AssociationInfo.Builder setAssociatedDevice(@Nullable android.companion.AssociatedDevice); + method @FlaggedApi("android.companion.association_device_icon") @NonNull public android.companion.AssociationInfo.Builder setDeviceIcon(@Nullable android.graphics.drawable.Icon); method @NonNull public android.companion.AssociationInfo.Builder setDeviceMacAddress(@Nullable android.net.MacAddress); method @NonNull public android.companion.AssociationInfo.Builder setDeviceProfile(@Nullable String); method @NonNull public android.companion.AssociationInfo.Builder setDisplayName(@Nullable CharSequence); @@ -2603,7 +2604,7 @@ package android.os { public abstract class Vibrator { method public int getDefaultVibrationIntensity(int); - method @Nullable public android.os.vibrator.VibratorFrequencyProfile getFrequencyProfile(); + method @Nullable public android.os.vibrator.VibratorFrequencyProfileLegacy getFrequencyProfileLegacy(); method public boolean hasFrequencyControl(); field public static final int VIBRATION_INTENSITY_HIGH = 3; // 0x3 field public static final int VIBRATION_INTENSITY_LOW = 1; // 0x1 @@ -2793,7 +2794,7 @@ package android.os.vibrator { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } - public final class VibratorFrequencyProfile { + public final class VibratorFrequencyProfileLegacy { method public float getMaxAmplitudeMeasurementInterval(); method @FloatRange(from=0, to=1) @NonNull public float[] getMaxAmplitudeMeasurements(); method public float getMaxFrequency(); diff --git a/core/java/Android.bp b/core/java/Android.bp index 2fa418a0ca587be433b5925861eaad83ee49aa4b..1265de1ebb1572cf7a442f221a640de44e0e4ed6 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -647,7 +647,6 @@ filegroup { java_library { name: "protolog-lib", - platform_apis: true, srcs: [ "com/android/internal/protolog/ProtoLogImpl.java", "com/android/internal/protolog/ProtoLogViewerConfigReader.java", diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 8bb28576f1e8c78edae3e0fd927af986c1a1613f..5bc7de943eca24e50be36e80fe23794f130e936e 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -84,19 +84,25 @@ import java.util.List; * @attr ref android.R.styleable#AccessibilityService_accessibilityEventTypes * @attr ref android.R.styleable#AccessibilityService_accessibilityFeedbackType * @attr ref android.R.styleable#AccessibilityService_accessibilityFlags + * @attr ref android.R.styleable#AccessibilityService_animatedImageDrawable + * @attr ref android.R.styleable#AccessibilityService_canControlMagnification + * @attr ref android.R.styleable#AccessibilityService_canPerformGestures * @attr ref android.R.styleable#AccessibilityService_canRequestFilterKeyEvents + * @attr ref android.R.styleable#AccessibilityService_canRequestFingerprintGestures * @attr ref android.R.styleable#AccessibilityService_canRequestTouchExplorationMode * @attr ref android.R.styleable#AccessibilityService_canRetrieveWindowContent - * @attr ref android.R.styleable#AccessibilityService_intro + * @attr ref android.R.styleable#AccessibilityService_canTakeScreenshot * @attr ref android.R.styleable#AccessibilityService_description - * @attr ref android.R.styleable#AccessibilityService_summary + * @attr ref android.R.styleable#AccessibilityService_htmlDescription + * @attr ref android.R.styleable#AccessibilityService_interactiveUiTimeout + * @attr ref android.R.styleable#AccessibilityService_intro + * @attr ref android.R.styleable#AccessibilityService_isAccessibilityTool + * @attr ref android.R.styleable#AccessibilityService_nonInteractiveUiTimeout * @attr ref android.R.styleable#AccessibilityService_notificationTimeout * @attr ref android.R.styleable#AccessibilityService_packageNames * @attr ref android.R.styleable#AccessibilityService_settingsActivity + * @attr ref android.R.styleable#AccessibilityService_summary * @attr ref android.R.styleable#AccessibilityService_tileService - * @attr ref android.R.styleable#AccessibilityService_nonInteractiveUiTimeout - * @attr ref android.R.styleable#AccessibilityService_interactiveUiTimeout - * @attr ref android.R.styleable#AccessibilityService_canTakeScreenshot * @see AccessibilityService * @see android.view.accessibility.AccessibilityEvent * @see android.view.accessibility.AccessibilityManager diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index d31881265064e1f95fa9a41d69f518571c76004e..3bd121a4a19b62d5bfaf424679a49e0c34d12d08 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -1323,4 +1323,12 @@ public abstract class ActivityManagerInternal { */ public abstract void killApplicationSync(String pkgName, int appId, int userId, String reason, int exitInfoReason); + + /** + * Add a creator token for all embedded intents (stored as extra) of the given intent. + * + * @param intent The given intent + * @hide + */ + public abstract void addCreatorToken(Intent intent); } diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 6ab39b0280325c32b19c05d054be4d26a6e6d913..832c88a795e579a036a16dcb2583b8be7fb60029 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -120,7 +120,7 @@ public class ActivityOptions extends ComponentOptions { /** * Grants the {@link PendingIntent} background activity start privileges. * - * This behaves the same as {@link #MODE_BACKGROUND_ACTIVITY_START_ALLOWED_ALWAYS}, except it + * This behaves the same as {@link #MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS}, except it * does not grant background activity launch permissions based on the privileged permission * START_ACTIVITIES_FROM_BACKGROUND. * @@ -136,7 +136,6 @@ public class ActivityOptions extends ComponentOptions { /** * Denies the {@link PendingIntent} any background activity start privileges. */ - @FlaggedApi(Flags.FLAG_BAL_ADDITIONAL_START_MODES) public static final int MODE_BACKGROUND_ACTIVITY_START_DENIED = 2; /** * Grants the {@link PendingIntent} all background activity start privileges, including @@ -146,12 +145,12 @@ public class ActivityOptions extends ComponentOptions { *

Caution: This mode should be used sparingly. Most apps should use * {@link #MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE} instead, relying on notifications * or foreground services for background interactions to minimize user disruption. However, - * this mode is necessary for specific use cases, such as companion apps responding to + * this mode is necessary for specific use cases, such as companion apps responding to * prompts from a connected device. * *

For more information on background activity start restrictions, see: * - * Restrictions on starting activities from the background + * Restrictions on starting activities from the background */ @FlaggedApi(Flags.FLAG_BAL_ADDITIONAL_START_MODES) public static final int MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS = 3; diff --git a/core/java/android/app/AppOps.md b/core/java/android/app/AppOps.md index 7b11a0351ebe62f7a82e35eeda0b2abb6128592e..535d62ce033e1fe3c16b98fa54f01be98bc25bf3 100644 --- a/core/java/android/app/AppOps.md +++ b/core/java/android/app/AppOps.md @@ -119,20 +119,20 @@ those. In addition to proc state, the `AppOpsService` also receives process capability update from the `ActivityManagerService`. Proc capability specifies what while-in-use(`MODE_FOREGROUND`) operations the proc is allowed to perform in its current proc state. There are three proc capabilities - defined so far: + defined so far: `PROCESS_CAPABILITY_FOREGROUND_LOCATION`, `PROCESS_CAPABILITY_FOREGROUND_CAMERA` and `PROCESS_CAPABILITY_FOREGROUND_MICROPHONE`, they correspond to the while-in-use operation of location, camera and microphone (microphone is `RECORD_AUDIO`). In `ActivityManagerService`, `PROCESS_STATE_TOP` and `PROCESS_STATE_PERSISTENT` have all three capabilities, `PROCESS_STATE_FOREGROUND_SERVICE` has capabilities defined by - `foregroundServiceType` that is specified in foreground service's manifest file. A client process + `foregroundServiceType` that is specified in foreground service's manifest file. A client process can pass its capabilities to service using `BIND_INCLUDE_CAPABILITIES` flag. The proc state and capability are used for two use cases: Firstly, Tracking remembers the proc state for each tracked event. Secondly, `noteOp`/`checkOp` calls for app-op that are set to `MODE_FOREGROUND` are translated using the `AppOpsService.UidState.evalMode` method into - `MODE_ALLOWED` when the app has the capability and `MODE_IGNORED` when the app does not have the + `MODE_ALLOWED` when the app has the capability and `MODE_IGNORED` when the app does not have the capability. `checkOpRaw` calls are not affected. The current proc state and capability for an app can be read from `dumpsys appops`. @@ -284,7 +284,7 @@ indicating what code accesses what private data. ##### Self data accesses This is similar to the [synchronous data access](#synchronous-data-accesses) case only that the data -provider and client are in the same process. In this case Android's RPC code is no involved and +provider and client are in the same process. In this case Android's RPC code is not involved and `AppOpsManager.noteOp` directly triggers `OnOpNotedCallback.onSelfNoted`. This should be a uncommon case as it is uncommon for an app to provide data, esp. to itself. diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 5907af0904adb7f1992feb369ab50dfa51ea2670..2e3d22647a0f7cba2439d48a5a2b8dec7a6a0757 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -54,6 +54,7 @@ import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.database.DatabaseUtils; import android.health.connect.HealthConnectManager; +import android.health.connect.HealthPermissions; import android.media.AudioAttributes.AttributeUsage; import android.media.MediaRouter2; import android.os.Binder; @@ -262,6 +263,13 @@ public class AppOpsManager { @GuardedBy("sLock") private static @Nullable OnOpNotedCallback sOnOpNotedCallback; + /** + * Whether OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC was set when sOnOpNotedCallback was registered + * last time. + */ + @GuardedBy("sLock") + private static boolean sIgnoreAsyncNotedCallback; + /** * Sync note-ops collected from {@link #readAndLogNotedAppops(Parcel)} that have not been * delivered to a callback yet. @@ -1607,9 +1615,12 @@ public class AppOpsManager { public static final int OP_RECEIVE_SENSITIVE_NOTIFICATIONS = AppProtoEnums.APP_OP_RECEIVE_SENSITIVE_NOTIFICATIONS; + /** @hide Access to read heart rate sensor. */ + public static final int OP_READ_HEART_RATE = AppProtoEnums.APP_OP_READ_HEART_RATE; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 149; + public static final int _NUM_OP = 150; /** * All app ops represented as strings. @@ -1762,6 +1773,7 @@ public class AppOpsManager { OPSTR_UNARCHIVAL_CONFIRMATION, OPSTR_EMERGENCY_LOCATION, OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS, + OPSTR_READ_HEART_RATE, }) public @interface AppOpString {} @@ -2499,6 +2511,11 @@ public class AppOpsManager { public static final String OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS = "android:receive_sensitive_notifications"; + /** @hide Access to read heart rate sensor. */ + @SystemApi + @FlaggedApi(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED) + public static final String OPSTR_READ_HEART_RATE = "android:read_heart_rate"; + /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */ @@ -2572,6 +2589,8 @@ public class AppOpsManager { OP_NEARBY_WIFI_DEVICES, // Notifications OP_POST_NOTIFICATION, + // Health + Flags.replaceBodySensorPermissionEnabled() ? OP_READ_HEART_RATE : OP_NONE, }; /** @@ -2612,6 +2631,7 @@ public class AppOpsManager { OP_READ_SYSTEM_GRAMMATICAL_GENDER, }; + @SuppressWarnings("FlaggedApi") static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{ new AppOpInfo.Builder(OP_COARSE_LOCATION, OPSTR_COARSE_LOCATION, "COARSE_LOCATION") .setPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) @@ -3079,6 +3099,10 @@ public class AppOpsManager { new AppOpInfo.Builder(OP_RECEIVE_SENSITIVE_NOTIFICATIONS, OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS, "RECEIVE_SENSITIVE_NOTIFICATIONS") .setDefaultMode(MODE_IGNORED).build(), + new AppOpInfo.Builder(OP_READ_HEART_RATE, OPSTR_READ_HEART_RATE, "READ_HEART_RATE") + .setPermission(Flags.replaceBodySensorPermissionEnabled() ? + HealthPermissions.READ_HEART_RATE : null) + .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), }; // The number of longs needed to form a full bitmask of app ops @@ -3132,6 +3156,10 @@ public class AppOpsManager { } } for (int op : RUNTIME_PERMISSION_OPS) { + if (op == OP_NONE) { + // Skip ops with a disabled feature flag. + continue; + } if (sAppOpInfos[op].permission != null) { sPermToOp.put(sAppOpInfos[op].permission, op); } @@ -10090,6 +10118,22 @@ public class AppOpsManager { private static final int COLLECT_SYNC = 2; private static final int COLLECT_ASYNC = 3; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "OP_NOTED_CALLBACK_FLAG_" }, value = { + OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC, + }) + private @interface OpNotedCallbackFlags {} + + /** + * Ignores async op noted events. + * + * @see #setOnOpNotedCallback + */ + @FlaggedApi(android.permission.flags.Flags.FLAG_SYNC_ON_OP_NOTED_API) + public static final int OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC = 1; + private static final int OP_NOTED_CALLBACK_FLAG_ALL = OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC; + /** * Mark an app-op as noted. */ @@ -10235,6 +10279,12 @@ public class AppOpsManager { *

There can only ever be one collector per process. If there currently is another callback * set, this will fail. * + *

Note that if an app has multiple processes registering for this callback, the system would + * fan out async op noted callbacks to each of the processes, resulting in the same data being + * delivered multiple times to an app, which is usually undesired. To avoid this, consider + * listening to async ops only in one process. See + * {@link #setOnOpNotedCallback(Executor, OnOpNotedCallback, int)} for how to do this. + * * @param asyncExecutor executor to execute {@link OnOpNotedCallback#onAsyncNoted} on, {@code * null} to unset * @param callback listener to set, {@code null} to unset @@ -10243,18 +10293,62 @@ public class AppOpsManager { */ public void setOnOpNotedCallback(@Nullable @CallbackExecutor Executor asyncExecutor, @Nullable OnOpNotedCallback callback) { + setOnOpNotedCallback(asyncExecutor, callback, /* flag */ 0); + } + + /** + * Set a new {@link OnOpNotedCallback}. + * + *

There can only ever be one collector per process. If there currently is another callback + * set, this will fail. + * + *

This API allows the caller to listen only to sync and self op noted events, and ignore + * async ops. This is useful in the scenario where an app has multiple processes. Consider an + * example where an app has two processes, A and B. The op noted events are as follows: + *

    + *
  • op 1: process A, sync + *
  • op 2: process A, async + *
  • op 3: process B, sync + *
  • op 4: process B, async + * Any process that listens to async op noted events gets events originating from across ALL + * processes (op 2 and op 4 in this example). So if both process A and B register as listeners, + * both of them get op 2 and 4 which is not ideal. To avoid duplicates, one of the two processes + * should set {@link #OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC}. For example + * process A sets {@link #OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC} and would then only get its own + * sync event (op 1). The other process would then listen to all types of events and get op 2, 3 + * and 4. + * + * Note that even with {@link #OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC}, + * {@link #OnOpNotedCallback.onAsyncNoted} may still be invoked. This happens for sync events + * that were collected before a callback is registered. + * + * @param asyncExecutor executor to execute {@link OnOpNotedCallback#onAsyncNoted} on, {@code + * null} to unset + * @param callback listener to set, {@code null} to unset + * @param flags additional flags to modify the callback behavior, such as + * {@link #OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC} + * + * @throws IllegalStateException If another callback is already registered + */ + @FlaggedApi(android.permission.flags.Flags.FLAG_SYNC_ON_OP_NOTED_API) + public void setOnOpNotedCallback(@Nullable @CallbackExecutor Executor asyncExecutor, + @Nullable OnOpNotedCallback callback, @OpNotedCallbackFlags int flags) { Preconditions.checkState((callback == null) == (asyncExecutor == null)); + Preconditions.checkFlagsArgument(flags, OP_NOTED_CALLBACK_FLAG_ALL); synchronized (sLock) { if (callback == null) { + Preconditions.checkFlagsArgument(flags, 0); Preconditions.checkState(sOnOpNotedCallback != null, "No callback is currently registered"); - try { - mService.stopWatchingAsyncNoted(mContext.getPackageName(), - sOnOpNotedCallback.mAsyncCb); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); + if (!sIgnoreAsyncNotedCallback) { + try { + mService.stopWatchingAsyncNoted(mContext.getPackageName(), + sOnOpNotedCallback.mAsyncCb); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } sOnOpNotedCallback = null; @@ -10264,14 +10358,17 @@ public class AppOpsManager { callback.mAsyncExecutor = asyncExecutor; sOnOpNotedCallback = callback; + sIgnoreAsyncNotedCallback = (flags & OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC) != 0; List missedAsyncOps = null; - try { - mService.startWatchingAsyncNoted(mContext.getPackageName(), - sOnOpNotedCallback.mAsyncCb); - missedAsyncOps = mService.extractAsyncOps(mContext.getPackageName()); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); + if (!sIgnoreAsyncNotedCallback) { + try { + mService.startWatchingAsyncNoted(mContext.getPackageName(), + sOnOpNotedCallback.mAsyncCb); + missedAsyncOps = mService.extractAsyncOps(mContext.getPackageName()); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } // Copy pointer so callback can be dispatched out of lock @@ -10284,17 +10381,15 @@ public class AppOpsManager { () -> onOpNotedCallback.onAsyncNoted(asyncNotedAppOp)); } } - synchronized (this) { - int numMissedSyncOps = sUnforwardedOps.size(); - if (onOpNotedCallback != null) { - for (int i = 0; i < numMissedSyncOps; i++) { - final AsyncNotedAppOp syncNotedAppOp = sUnforwardedOps.get(i); - onOpNotedCallback.getAsyncNotedExecutor().execute( - () -> onOpNotedCallback.onAsyncNoted(syncNotedAppOp)); - } + int numMissedSyncOps = sUnforwardedOps.size(); + if (onOpNotedCallback != null) { + for (int i = 0; i < numMissedSyncOps; i++) { + final AsyncNotedAppOp syncNotedAppOp = sUnforwardedOps.get(i); + onOpNotedCallback.getAsyncNotedExecutor().execute( + () -> onOpNotedCallback.onAsyncNoted(syncNotedAppOp)); } - sUnforwardedOps.clear(); } + sUnforwardedOps.clear(); } } } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 93a9489849affb36d4933e495de2892d3a093ae0..7eacaac29d4b25e3cb3fb51f35bc62a188f96b07 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -48,6 +48,9 @@ import android.os.SystemProperties; import android.os.TestLooperManager; import android.os.UserHandle; import android.os.UserManager; +import android.ravenwood.annotation.RavenwoodKeep; +import android.ravenwood.annotation.RavenwoodKeepPartialClass; +import android.ravenwood.annotation.RavenwoodReplace; import android.util.AndroidRuntimeException; import android.util.Log; import android.view.Display; @@ -80,7 +83,7 @@ import java.util.concurrent.TimeoutException; * implementation is described to the system through an AndroidManifest.xml's * <instrumentation> tag. */ -@android.ravenwood.annotation.RavenwoodKeepPartialClass +@RavenwoodKeepPartialClass public class Instrumentation { /** @@ -136,7 +139,7 @@ public class Instrumentation { private UiAutomation mUiAutomation; private final Object mAnimationCompleteLock = new Object(); - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public Instrumentation() { } @@ -147,7 +150,7 @@ public class Instrumentation { * reflection, but it will serve as noticeable discouragement from * doing such a thing. */ - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep private void checkInstrumenting(String method) { // Check if we have an instrumentation context, as init should only get called by // the system in startup processes that are being instrumented. @@ -162,7 +165,7 @@ public class Instrumentation { * * @hide */ - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public boolean isInstrumenting() { // Check if we have an instrumentation context, as init should only get called by // the system in startup processes that are being instrumented. @@ -326,7 +329,7 @@ public class Instrumentation { * * @see #getTargetContext */ - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public Context getContext() { return mInstrContext; } @@ -351,7 +354,7 @@ public class Instrumentation { * * @see #getContext */ - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public Context getTargetContext() { return mAppContext; } @@ -2407,10 +2410,11 @@ public class Instrumentation { * * @hide */ - @android.ravenwood.annotation.RavenwoodKeep - public final void basicInit(Context instrContext, Context appContext) { + @RavenwoodKeep + public final void basicInit(Context instrContext, Context appContext, UiAutomation ui) { mInstrContext = instrContext; mAppContext = appContext; + mUiAutomation = ui; } /** @hide */ @@ -2501,6 +2505,7 @@ public class Instrumentation { * * @see UiAutomation */ + @RavenwoodKeep public UiAutomation getUiAutomation() { return getUiAutomation(0); } @@ -2539,6 +2544,7 @@ public class Instrumentation { * * @see UiAutomation */ + @RavenwoodReplace public UiAutomation getUiAutomation(@UiAutomationFlags int flags) { boolean mustCreateNewAutomation = (mUiAutomation == null) || (mUiAutomation.isDestroyed()); @@ -2569,11 +2575,15 @@ public class Instrumentation { return null; } + private UiAutomation getUiAutomation$ravenwood(@UiAutomationFlags int flags) { + return mUiAutomation; + } + /** * Takes control of the execution of messages on the specified looper until * {@link TestLooperManager#release} is called. */ - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public TestLooperManager acquireLooperManager(Looper looper) { checkInstrumenting("acquireLooperManager"); return new TestLooperManager(looper); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 38632bdeeff518f593517a6d23c2bf613429c042..8b33417e0a79225a2261cc9e12d5bd13b1c5656d 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -115,6 +115,7 @@ import com.android.internal.graphics.ColorUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ContrastColorUtil; import com.android.internal.util.NotificationBigTextNormalizer; +import com.android.internal.widget.NotificationProgressModel; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -810,6 +811,11 @@ public class Notification implements Parcelable } private static boolean isStandardLayout(int layoutId) { + if (Flags.apiRichOngoing()) { + if (layoutId == R.layout.notification_template_material_progress) { + return true; + } + } return STANDARD_LAYOUTS.contains(layoutId); } @@ -7313,12 +7319,16 @@ public class Notification implements Parcelable */ @VisibleForTesting public static int ensureButtonFillContrast(int color, int bg) { - return isColorDark(bg) - ? ContrastColorUtil.findContrastColorAgainstDark(color, bg, true, 1.3) - : ContrastColorUtil.findContrastColor(color, bg, true, 1.3); + return ensureColorContrast(color, bg, 1.3); } + private static int ensureColorContrast(int color, int bg, double contrastRatio) { + return isColorDark(bg) + ? ContrastColorUtil.findContrastColorAgainstDark(color, bg, true, contrastRatio) + : ContrastColorUtil.findContrastColor(color, bg, true, contrastRatio); + } + /** * @return Whether we are currently building a notification from a legacy (an app that * doesn't create material notifications by itself) app. @@ -7683,6 +7693,10 @@ public class Notification implements Parcelable return R.layout.notification_template_material_conversation; } + private int getProgressLayoutResource() { + return R.layout.notification_template_material_progress; + } + private int getActionLayoutResource() { return R.layout.notification_material_action; } @@ -11640,8 +11654,58 @@ public class Notification implements Parcelable return getStandardView(mBuilder.getHeadsUpBaseLayoutResource(), p, null /* result */); } + /** + * @hide + */ + @Override + public RemoteViews makeBigContentView() { + StandardTemplateParams p = mBuilder.mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .allowTextWithProgress(true) + .hideProgress(true) + .fillTextsFrom(mBuilder); + + // Replace the text with the big text, but only if the big text is not empty. + RemoteViews contentView = getStandardView(mBuilder.getProgressLayoutResource(), p, + null /* result */); + + // Bind progress start and end icons. + if (mStartIcon != null) { + contentView.setViewVisibility(R.id.notification_progress_start_icon, View.VISIBLE); + contentView.setImageViewIcon(R.id.notification_progress_start_icon, mStartIcon); + } else { + contentView.setViewVisibility(R.id.notification_progress_start_icon, View.GONE); + } + + if (mEndIcon != null) { + contentView.setViewVisibility(R.id.notification_progress_end_icon, View.VISIBLE); + contentView.setImageViewIcon(R.id.notification_progress_end_icon, mEndIcon); + } else { + contentView.setViewVisibility(R.id.notification_progress_end_icon, View.GONE); + } + + contentView.setViewVisibility(R.id.progress, View.VISIBLE); + + final int backgroundColor = mBuilder.getColors(p).getBackgroundColor(); + final int defaultProgressColor = mBuilder.getPrimaryAccentColor(p); + final NotificationProgressModel model = createProgressModel( + defaultProgressColor, backgroundColor); + contentView.setBundle(R.id.progress, + "setProgressModel", model.toBundle()); + + if (mTrackerIcon != null) { + contentView.setIcon(R.id.progress, + "setProgressTrackerIcon", + mTrackerIcon); + } + + return contentView; + } - private static @NonNull ArrayList getProgressSegmentsAsBundleList( + /** + * @hide + */ + public static @NonNull ArrayList getProgressSegmentsAsBundleList( @Nullable List progressSegments) { final ArrayList segments = new ArrayList<>(); if (progressSegments != null && !progressSegments.isEmpty()) { @@ -11663,7 +11727,10 @@ public class Notification implements Parcelable return segments; } - private static @NonNull List getProgressSegmentsFromBundleList( + /** + * @hide + */ + public static @NonNull List getProgressSegmentsFromBundleList( @Nullable List segmentBundleList) { final ArrayList segments = new ArrayList<>(); if (segmentBundleList != null && !segmentBundleList.isEmpty()) { @@ -11686,8 +11753,10 @@ public class Notification implements Parcelable return segments; } - - private static @NonNull ArrayList getProgressPointsAsBundleList( + /** + * @hide + */ + public static @NonNull ArrayList getProgressPointsAsBundleList( @Nullable List progressPoints) { final ArrayList points = new ArrayList<>(); if (progressPoints != null && !progressPoints.isEmpty()) { @@ -11709,7 +11778,10 @@ public class Notification implements Parcelable return points; } - private static @NonNull List getProgressPointsFromBundleList( + /** + * @hide + */ + public static @NonNull List getProgressPointsFromBundleList( @Nullable List pointBundleList) { final ArrayList points = new ArrayList<>(); @@ -11731,6 +11803,78 @@ public class Notification implements Parcelable return points; } + @NonNull + private NotificationProgressModel createProgressModel(int defaultProgressColor, + int backgroundColor) { + final NotificationProgressModel model; + if (mIndeterminate) { + final int indeterminateColor; + if (!mProgressSegments.isEmpty()) { + indeterminateColor = mProgressSegments.get(0).mColor; + } else { + indeterminateColor = defaultProgressColor; + } + + model = new NotificationProgressModel( + sanitizeProgressColor(indeterminateColor, + backgroundColor, defaultProgressColor)); + } else { + + // Ensure segment color contrasts. + final List segments = new ArrayList<>(); + for (Segment segment : mProgressSegments) { + segments.add(sanitizeSegment(segment, backgroundColor, + defaultProgressColor)); + } + + // Create default segment when no segments are provided. + if (segments.isEmpty()) { + segments.add(sanitizeSegment(new Segment(100), backgroundColor, + defaultProgressColor)); + } + + // Ensure point color contrasts. + final List points = new ArrayList<>(); + for (Point point : mProgressPoints) { + points.add(sanitizePoint(point, backgroundColor, defaultProgressColor)); + } + + model = new NotificationProgressModel(segments, points, + mProgress, mIsStyledByProgress); + } + return model; + } + + private Segment sanitizeSegment(@NonNull Segment segment, + @ColorInt int bg, + @ColorInt int defaultColor) { + return new Segment(segment.getLength()) + .setId(segment.getId()) + .setColor(sanitizeProgressColor(segment.getColor(), bg, defaultColor)); + } + + private Point sanitizePoint(@NonNull Point point, + @ColorInt int bg, + @ColorInt int defaultColor) { + return new Point(point.getPosition()).setId(point.getId()) + .setColor(sanitizeProgressColor(point.getColor(), bg, defaultColor)); + } + + /** + * Finds steps and points fill color with sufficient contrast over bg (1.3:1) that + * has the same hue as the original color, but is lightened or darkened depending on + * whether the background is dark or light. + * + */ + private int sanitizeProgressColor(@ColorInt int color, + @ColorInt int bg, + @ColorInt int defaultColor) { + return Builder.ensureColorContrast( + Color.alpha(color) == 0 ? defaultColor : color, + bg, + 1.3); + } + /** * A segment of the progress bar, which defines its length and color. * Segments allow for creating progress bars with multiple colors or sections diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 3714e5de23ca4280e96522fca87a87934841ff9b..393ec8c1d66d3053dcc6bccc989f930a142a436a 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -1094,6 +1094,9 @@ public final class PendingIntent implements Parcelable { @Nullable String requiredPermission, @Nullable Bundle options) throws CanceledException { try { + if (intent != null) { + intent.collectExtraIntentKeys(); + } String resolvedType = intent != null ? intent.resolveTypeIfNeeded(context.getContentResolver()) : null; diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index c1c96eaa098dd8219ce4ec476996267712c75a65..014e4660f9446da9aaeb1f30f3372f323f604696 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -284,6 +284,14 @@ public class WallpaperManager { */ public static final String COMMAND_UNFREEZE = "android.wallpaper.unfreeze"; + /** + * Command for {@link #sendWallpaperCommand}: in sendWallpaperCommand put extra to this command + * to give the bounds of space between the bottom of notifications and the top of shortcuts + * @hide + */ + public static final String COMMAND_LOCKSCREEN_LAYOUT_CHANGED = + "android.wallpaper.lockscreen_layout_changed"; + /** * Extra passed back from setWallpaper() giving the new wallpaper's assigned ID. * @hide diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 9be928f7efd0ebc835b37fcf6c86ddb42392181e..102540c010aeec23a21c2234d7bd68bb90fcb72f 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -470,16 +470,9 @@ public class DevicePolicyManager { * that the user backed-out of provisioning or some precondition for provisioning wasn't met. * *

    If a device policy management role holder updater is present on - * the device, an internet connection attempt must be made prior to launching this intent. If - * an internet connection can not be established, provisioning will fail unless {@link - * #EXTRA_PROVISIONING_ALLOW_OFFLINE} is explicitly set to {@code true}, in which case - * provisioning will continue without using the - * device policy management role holder. If an internet connection - * has been established, the device policy management role holder - * updater will be launched, which may update the - * device policy management role holder before continuing - * provisioning. + * the device, an internet connection attempt must be made prior to launching this intent. */ + // See b/365955253 for additional behaviours of this API. @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE"; @@ -960,23 +953,8 @@ public class DevicePolicyManager { * A boolean extra indicating whether offline provisioning should be used. * *

    The default value is {@code false}. - * - *

    Usually during the provisioning flow, there will be - * an attempt to download and install the latest version of the device - * policy management role holder. The platform will then - * delegate provisioning to the device - * * policy management role holder. - * - *

    When this extra is set to {@code true}, the - * provisioning flow will always be handled by the platform - * and the device policy management role holder's part skipped. - * - *

    On Android versions prior to {@link Build.VERSION_CODES#TIRAMISU}, when this extra is - * {@code false}, the provisioning flow will enforce that an - * internet connection is established, or otherwise fail. When this extra is {@code true}, a - * connection will still be attempted but when it cannot be established provisioning will - * continue offline. */ + // See b/365955253 for detailed behaviours of this API. public static final String EXTRA_PROVISIONING_ALLOW_OFFLINE = "android.app.extra.PROVISIONING_ALLOW_OFFLINE"; diff --git a/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java b/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java index fa77e793fbe9d08e8895f780fd2fe6129790caea..cb21d1f3a57755472eb292f4d77d0e0250a832ae 100644 --- a/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java +++ b/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java @@ -20,7 +20,6 @@ import static android.app.appfunctions.flags.Flags.enableAppFunctionManager; import android.annotation.NonNull; import android.content.Context; -import android.content.pm.PackageManager; /** * Represents the system configuration of support for the {@code AppFunctionManager} and associated @@ -29,15 +28,13 @@ import android.content.pm.PackageManager; * @hide */ public class AppFunctionManagerConfiguration { - private final Context mContext; - /** * Constructs a new instance of {@code AppFunctionManagerConfiguration}. * * @param context context */ public AppFunctionManagerConfiguration(@NonNull final Context context) { - mContext = context; + // Context can be used to access system features, etc. } /** @@ -46,7 +43,7 @@ public class AppFunctionManagerConfiguration { * @return {@code true} if supported; otherwise {@code false} */ public boolean isSupported() { - return enableAppFunctionManager() && !isWatch(); + return enableAppFunctionManager(); } /** @@ -58,8 +55,4 @@ public class AppFunctionManagerConfiguration { public static boolean isSupported(@NonNull final Context context) { return new AppFunctionManagerConfiguration(context).isSupported(); } - - private boolean isWatch() { - return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); - } } diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java index 7a68a656564beb17106f955257d928c4cf7fd961..ceca850a1037d3db9338556306ddf10a7fdba252 100644 --- a/core/java/android/app/appfunctions/AppFunctionService.java +++ b/core/java/android/app/appfunctions/AppFunctionService.java @@ -29,11 +29,9 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Binder; -import android.os.Bundle; +import android.os.CancellationSignal; import android.os.IBinder; import android.os.ICancellationSignal; -import android.os.CancellationSignal; -import android.os.RemoteCallback; import android.os.RemoteException; import android.util.Log; @@ -80,6 +78,7 @@ public abstract class AppFunctionService extends Service { */ void perform( @NonNull ExecuteAppFunctionRequest request, + @NonNull String callingPackage, @NonNull CancellationSignal cancellationSignal, @NonNull Consumer callback); } @@ -92,6 +91,7 @@ public abstract class AppFunctionService extends Service { @Override public void executeAppFunction( @NonNull ExecuteAppFunctionRequest request, + @NonNull String callingPackage, @NonNull ICancellationCallback cancellationCallback, @NonNull IExecuteAppFunctionCallback callback) { if (context.checkCallingPermission(BIND_APP_FUNCTION_SERVICE) @@ -103,6 +103,7 @@ public abstract class AppFunctionService extends Service { try { onExecuteFunction.perform( request, + callingPackage, buildCancellationSignal(cancellationCallback), safeCallback::onResult); } catch (Exception ex) { @@ -128,12 +129,11 @@ public abstract class AppFunctionService extends Service { throw e.rethrowFromSystemServer(); } - return cancellationSignal ; + return cancellationSignal; } - private final Binder mBinder = createBinder( - AppFunctionService.this, - AppFunctionService.this::onExecuteFunction); + private final Binder mBinder = + createBinder(AppFunctionService.this, AppFunctionService.this::onExecuteFunction); @NonNull @Override @@ -141,7 +141,6 @@ public abstract class AppFunctionService extends Service { return mBinder; } - /** * Called by the system to execute a specific app function. * @@ -161,7 +160,6 @@ public abstract class AppFunctionService extends Service { * * @param request The function execution request. * @param callback A callback to report back the result. - * * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, CancellationSignal, * Consumer)} instead. This method will be removed once usage references are updated. */ @@ -198,12 +196,50 @@ public abstract class AppFunctionService extends Service { * @param request The function execution request. * @param cancellationSignal A signal to cancel the execution. * @param callback A callback to report back the result. + * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, String, + * CancellationSignal, Consumer)} instead. This method will be removed once usage references + * are updated. */ @MainThread + @Deprecated public void onExecuteFunction( @NonNull ExecuteAppFunctionRequest request, @NonNull CancellationSignal cancellationSignal, @NonNull Consumer callback) { onExecuteFunction(request, callback); } + + /** + * Called by the system to execute a specific app function. + * + *

    This method is triggered when the system requests your AppFunctionService to handle a + * particular function you have registered and made available. + * + *

    To ensure proper routing of function requests, assign a unique identifier to each + * function. This identifier doesn't need to be globally unique, but it must be unique within + * your app. For example, a function to order food could be identified as "orderFood". In most + * cases this identifier should come from the ID automatically generated by the AppFunctions + * SDK. You can determine the specific function to invoke by calling {@link + * ExecuteAppFunctionRequest#getFunctionIdentifier()}. + * + *

    This method is always triggered in the main thread. You should run heavy tasks on a worker + * thread and dispatch the result with the given callback. You should always report back the + * result using the callback, no matter if the execution was successful or not. + * + *

    This method also accepts a {@link CancellationSignal} that the app should listen to cancel + * the execution of function if requested by the system. + * + * @param request The function execution request. + * @param callingPackage The package name of the app that is requesting the execution. + * @param cancellationSignal A signal to cancel the execution. + * @param callback A callback to report back the result. + */ + @MainThread + public void onExecuteFunction( + @NonNull ExecuteAppFunctionRequest request, + @NonNull String callingPackage, + @NonNull CancellationSignal cancellationSignal, + @NonNull Consumer callback) { + onExecuteFunction(request, cancellationSignal, callback); + } } diff --git a/core/java/android/app/appfunctions/IAppFunctionService.aidl b/core/java/android/app/appfunctions/IAppFunctionService.aidl index 291f33ccb1b84b5dbc62ba58f2a309df31df7df1..bf935d2a102b8b5978f5d274f3c9c8e64594d706 100644 --- a/core/java/android/app/appfunctions/IAppFunctionService.aidl +++ b/core/java/android/app/appfunctions/IAppFunctionService.aidl @@ -34,11 +34,13 @@ oneway interface IAppFunctionService { * Called by the system to execute a specific app function. * * @param request the function execution request. + * @param callingPackage The package name of the app that is requesting the execution. * @param cancellationCallback a callback to send back the cancellation transport. * @param callback a callback to report back the result. */ void executeAppFunction( in ExecuteAppFunctionRequest request, + in String callingPackage, in ICancellationCallback cancellationCallback, in IExecuteAppFunctionCallback callback ); diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 508077ed43cc82981919b8f10d9900475ddab8cd..1af2437a5d6a2cc465af786d518bf0b839260d11 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -1,5 +1,6 @@ package android.app.assist; +import static android.app.assist.flags.Flags.addPlaceholderViewForNullChild; import static android.credentials.Constants.FAILURE_CREDMAN_SELECTOR; import static android.credentials.Constants.SUCCESS_CREDMAN_SELECTOR; import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION; @@ -284,12 +285,18 @@ public class AssistStructure implements Parcelable { mCurViewStackEntry = entry; } - void writeView(ViewNode child, Parcel out, PooledStringWriter pwriter, int levelAdj) { + void writeView(@Nullable ViewNode child, Parcel out, PooledStringWriter pwriter, + int levelAdj) { if (DEBUG_PARCEL) Log.d(TAG, "write view: at " + out.dataPosition() + ", windows=" + mNumWrittenWindows + ", views=" + mNumWrittenViews + ", level=" + (mCurViewStackPos+levelAdj)); out.writeInt(VALIDATE_VIEW_TOKEN); + if (addPlaceholderViewForNullChild() && child == null) { + if (DEBUG_PARCEL_TREE) Log.d(TAG, "Detected an empty child" + + "; writing a placeholder for the child."); + child = new ViewNode(); + } int flags = child.writeSelfToParcel(out, pwriter, mSanitizeOnWrite, mTmpMatrix, /*willWriteChildren=*/true); mNumWrittenViews++; @@ -2545,7 +2552,7 @@ public class AssistStructure implements Parcelable { ensureData(); } Log.i(TAG, "Task id: " + mTaskId); - Log.i(TAG, "Activity: " + (mActivityComponent != null + Log.i(TAG, "Activity: " + (mActivityComponent != null ? mActivityComponent.flattenToShortString() : null)); Log.i(TAG, "Sanitize on write: " + mSanitizeOnWrite); diff --git a/core/java/android/app/assist/flags.aconfig b/core/java/android/app/assist/flags.aconfig new file mode 100644 index 0000000000000000000000000000000000000000..bf0aeacbc7f3255303256712fb8651307f26ba35 --- /dev/null +++ b/core/java/android/app/assist/flags.aconfig @@ -0,0 +1,13 @@ +package: "android.app.assist.flags" +container: "system" + +flag { + name: "add_placeholder_view_for_null_child" + namespace: "machine_learning" + description: "Flag to add a placeholder view when a child view is null." + bug: "369503426" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index b139017219094c08934b317bc935e5dd1da07b6c..8014537e8838bce751febfeecacc4b2f799edbb0 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -251,7 +251,7 @@ flag { name: "api_rich_ongoing" is_exported: true namespace: "systemui" - description: "Guards new android.app.richongoingnotification api" + description: "[RONs] Guards new RON-related APIs, including Notification.ProgressStyle" bug: "337261753" } @@ -259,6 +259,6 @@ flag { name: "ui_rich_ongoing" is_exported: true namespace: "systemui" - description: "Guards new android.app.richongoingnotification promotion and new uis" - bug: "337261753" + description: "[RONs] Guards new promotion logic and UI, including AOD notification and Colorization" + bug: "367705002" } diff --git a/core/java/android/app/wearable/flags.aconfig b/core/java/android/app/wearable/flags.aconfig index b68bafe279bf9eb728c5534e188ce3349ecd9fe5..534f46172fc1b444ef5533b3f22433d710b05414 100644 --- a/core/java/android/app/wearable/flags.aconfig +++ b/core/java/android/app/wearable/flags.aconfig @@ -38,4 +38,12 @@ flag { namespace: "machine_learning" description: "This flag enables the APIs related to hotword in WearableSensingManager and WearableSensingService." bug: "310055381" +} + +flag { + name: "enable_concurrent_wearable_connections" + is_exported: true + namespace: "machine_learning" + description: "This flag enables the APIs for providing multiple concurrent connections to the WearableSensingService." + bug: "358133158" } \ No newline at end of file diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java index b4b96e2c69d64f07269a6898e2611d38566c807a..7f30d7cccb57b25f4f687b387228d921985e3915 100644 --- a/core/java/android/companion/AssociationInfo.java +++ b/core/java/android/companion/AssociationInfo.java @@ -22,6 +22,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UserIdInt; +import android.graphics.drawable.Icon; import android.net.MacAddress; import android.os.Parcel; import android.os.Parcelable; @@ -85,6 +86,11 @@ public final class AssociationInfo implements Parcelable { private final long mLastTimeConnectedMs; private final int mSystemDataSyncFlags; + /** + * A device icon displayed on a selfManaged association dialog. + */ + private final Icon mDeviceIcon; + /** * Creates a new Association. * @@ -95,7 +101,7 @@ public final class AssociationInfo implements Parcelable { @Nullable CharSequence displayName, @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice, boolean selfManaged, boolean notifyOnDeviceNearby, boolean revoked, boolean pending, long timeApprovedMs, - long lastTimeConnectedMs, int systemDataSyncFlags) { + long lastTimeConnectedMs, int systemDataSyncFlags, @Nullable Icon deviceIcon) { if (id <= 0) { throw new IllegalArgumentException("Association ID should be greater than 0"); } @@ -119,6 +125,7 @@ public final class AssociationInfo implements Parcelable { mTimeApprovedMs = timeApprovedMs; mLastTimeConnectedMs = lastTimeConnectedMs; mSystemDataSyncFlags = systemDataSyncFlags; + mDeviceIcon = deviceIcon; } /** @@ -277,6 +284,20 @@ public final class AssociationInfo implements Parcelable { return mSystemDataSyncFlags; } + /** + * Get the device icon of the associated device. The device icon represents the device type. + * + * @return the device icon, or {@code null} if no device icon is has been set for the + * associated device. + * + * @see AssociationRequest.Builder#setDeviceIcon(Icon) + */ + @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON) + @Nullable + public Icon getDeviceIcon() { + return mDeviceIcon; + } + /** * Utility method for checking if the association represents a device with the given MAC * address. @@ -370,14 +391,16 @@ public final class AssociationInfo implements Parcelable { && Objects.equals(mDisplayName, that.mDisplayName) && Objects.equals(mDeviceProfile, that.mDeviceProfile) && Objects.equals(mAssociatedDevice, that.mAssociatedDevice) - && mSystemDataSyncFlags == that.mSystemDataSyncFlags; + && mSystemDataSyncFlags == that.mSystemDataSyncFlags + && (mDeviceIcon == null ? that.mDeviceIcon == null + : mDeviceIcon.sameAs(that.mDeviceIcon)); } @Override public int hashCode() { return Objects.hash(mId, mUserId, mPackageName, mTag, mDeviceMacAddress, mDisplayName, mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked, - mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags); + mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags, mDeviceIcon); } @Override @@ -402,6 +425,12 @@ public final class AssociationInfo implements Parcelable { dest.writeLong(mTimeApprovedMs); dest.writeLong(mLastTimeConnectedMs); dest.writeInt(mSystemDataSyncFlags); + if (mDeviceIcon != null) { + dest.writeInt(1); + mDeviceIcon.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } } private AssociationInfo(@NonNull Parcel in) { @@ -420,6 +449,11 @@ public final class AssociationInfo implements Parcelable { mTimeApprovedMs = in.readLong(); mLastTimeConnectedMs = in.readLong(); mSystemDataSyncFlags = in.readInt(); + if (in.readInt() == 1) { + mDeviceIcon = Icon.CREATOR.createFromParcel(in); + } else { + mDeviceIcon = null; + } } @NonNull @@ -459,6 +493,7 @@ public final class AssociationInfo implements Parcelable { private long mTimeApprovedMs; private long mLastTimeConnectedMs; private int mSystemDataSyncFlags; + private Icon mDeviceIcon; /** @hide */ @TestApi @@ -486,6 +521,7 @@ public final class AssociationInfo implements Parcelable { mTimeApprovedMs = info.mTimeApprovedMs; mLastTimeConnectedMs = info.mLastTimeConnectedMs; mSystemDataSyncFlags = info.mSystemDataSyncFlags; + mDeviceIcon = info.mDeviceIcon; } /** @@ -510,6 +546,7 @@ public final class AssociationInfo implements Parcelable { mTimeApprovedMs = info.mTimeApprovedMs; mLastTimeConnectedMs = info.mLastTimeConnectedMs; mSystemDataSyncFlags = info.mSystemDataSyncFlags; + mDeviceIcon = info.mDeviceIcon; } /** @hide */ @@ -622,6 +659,16 @@ public final class AssociationInfo implements Parcelable { return this; } + /** @hide */ + @TestApi + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON) + public Builder setDeviceIcon(@Nullable Icon deviceIcon) { + mDeviceIcon = deviceIcon; + return this; + } + /** @hide */ @TestApi @NonNull @@ -648,7 +695,8 @@ public final class AssociationInfo implements Parcelable { mPending, mTimeApprovedMs, mLastTimeConnectedMs, - mSystemDataSyncFlags + mSystemDataSyncFlags, + mDeviceIcon ); } } diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 2e969f83f8361ad7b638ea3cefb04788c26b52d4..41a6791d8a7bb054d0c8c5f1dbadd7128c22fb47 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -23,12 +23,14 @@ import static com.android.internal.util.CollectionUtils.emptyIfNull; import static java.util.Objects.requireNonNull; import android.Manifest; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.StringDef; import android.annotation.UserIdInt; import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.drawable.Icon; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -233,6 +235,13 @@ public final class AssociationRequest implements Parcelable { */ private boolean mSkipPrompt; + /** + * The device icon displayed in selfManaged association dialog. + * @hide + */ + @Nullable + private Icon mDeviceIcon; + /** * Creates a new AssociationRequest. * @@ -258,15 +267,16 @@ public final class AssociationRequest implements Parcelable { @Nullable @DeviceProfile String deviceProfile, @Nullable CharSequence displayName, boolean selfManaged, - boolean forceConfirmation) { + boolean forceConfirmation, + @Nullable Icon deviceIcon) { mSingleDevice = singleDevice; mDeviceFilters = requireNonNull(deviceFilters); mDeviceProfile = deviceProfile; mDisplayName = displayName; mSelfManaged = selfManaged; mForceConfirmation = forceConfirmation; - mCreationTime = System.currentTimeMillis(); + mDeviceIcon = deviceIcon; } /** @@ -318,6 +328,19 @@ public final class AssociationRequest implements Parcelable { return mSingleDevice; } + /** + * Get the device icon of the self-managed association request. + * + * @return the device icon, or {@code null} if no device icon has been set. + * + * @see Builder#setDeviceIcon(Icon) + */ + @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON) + @Nullable + public Icon getDeviceIcon() { + return mDeviceIcon; + } + /** @hide */ public void setPackageName(@NonNull String packageName) { mPackageName = packageName; @@ -365,6 +388,7 @@ public final class AssociationRequest implements Parcelable { private CharSequence mDisplayName; private boolean mSelfManaged = false; private boolean mForceConfirmation = false; + private Icon mDeviceIcon = null; public Builder() {} @@ -450,6 +474,23 @@ public final class AssociationRequest implements Parcelable { return this; } + /** + * Set the device icon for the self-managed device and this icon will be + * displayed in the self-managed association dialog. + * + * @throws IllegalArgumentException if the icon is not exactly 24dp by 24dp + * or if it is {@link Icon#TYPE_URI} or {@link Icon#TYPE_URI_ADAPTIVE_BITMAP}. + * @see #setSelfManaged(boolean) + */ + @NonNull + @RequiresPermission(REQUEST_COMPANION_SELF_MANAGED) + @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON) + public Builder setDeviceIcon(@NonNull Icon deviceIcon) { + checkNotUsed(); + mDeviceIcon = requireNonNull(deviceIcon); + return this; + } + /** @inheritDoc */ @NonNull @Override @@ -460,7 +501,7 @@ public final class AssociationRequest implements Parcelable { + "provide the display name of the device"); } return new AssociationRequest(mSingleDevice, emptyIfNull(mDeviceFilters), - mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation); + mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation, mDeviceIcon); } } @@ -561,7 +602,9 @@ public final class AssociationRequest implements Parcelable { && Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription) && mCreationTime == that.mCreationTime - && mSkipPrompt == that.mSkipPrompt; + && mSkipPrompt == that.mSkipPrompt + && (mDeviceIcon == null ? that.mDeviceIcon == null + : mDeviceIcon.sameAs(that.mDeviceIcon)); } @Override @@ -579,6 +622,8 @@ public final class AssociationRequest implements Parcelable { _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription); _hash = 31 * _hash + Long.hashCode(mCreationTime); _hash = 31 * _hash + Boolean.hashCode(mSkipPrompt); + _hash = 31 * _hash + Objects.hashCode(mDeviceIcon); + return _hash; } @@ -606,6 +651,12 @@ public final class AssociationRequest implements Parcelable { dest.writeString8(mDeviceProfilePrivilegesDescription); } dest.writeLong(mCreationTime); + if (mDeviceIcon != null) { + dest.writeInt(1); + mDeviceIcon.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } } @Override @@ -650,6 +701,11 @@ public final class AssociationRequest implements Parcelable { this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription; this.mCreationTime = creationTime; this.mSkipPrompt = skipPrompt; + if (in.readInt() == 1) { + mDeviceIcon = Icon.CREATOR.createFromParcel(in); + } else { + mDeviceIcon = null; + } } @NonNull diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 1cdf3b11f247068a8a1e9cab383643571068d963..dfad6de4ba167a8b6c9af1ee2a62d2de97547038 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -20,6 +20,8 @@ import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMIN import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION; import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER; import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH; +import static android.graphics.drawable.Icon.TYPE_URI; +import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP; import android.annotation.CallbackExecutor; @@ -49,6 +51,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.graphics.drawable.VectorDrawable; import android.net.MacAddress; import android.os.Binder; import android.os.Handler; @@ -535,6 +542,13 @@ public final class CompanionDeviceManager { Objects.requireNonNull(executor, "Executor cannot be null"); Objects.requireNonNull(callback, "Callback cannot be null"); + final Icon deviceIcon = request.getDeviceIcon(); + + if (deviceIcon != null && !isValidIcon(deviceIcon, mContext)) { + throw new IllegalArgumentException("The size of the device icon must be 24dp x 24dp to" + + "ensure proper display"); + } + try { mService.associate(request, new AssociationRequestCallbackProxy(executor, callback), mContext.getOpPackageName(), mContext.getUserId()); @@ -2027,4 +2041,34 @@ public final class CompanionDeviceManager { } } } + + private boolean isValidIcon(Icon icon, Context context) { + if (icon.getType() == TYPE_URI_ADAPTIVE_BITMAP || icon.getType() == TYPE_URI) { + throw new IllegalArgumentException("The URI based Icon is not supported."); + } + Drawable drawable = icon.loadDrawable(context); + float density = context.getResources().getDisplayMetrics().density; + + if (drawable instanceof BitmapDrawable) { + Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); + + float widthDp = bitmap.getWidth() / density; + float heightDp = bitmap.getHeight() / density; + + if (widthDp != 24 || heightDp != 24) { + return false; + } + } else if (drawable instanceof VectorDrawable) { + VectorDrawable vectorDrawable = (VectorDrawable) drawable; + float widthDp = vectorDrawable.getIntrinsicWidth() / density; + float heightDp = vectorDrawable.getIntrinsicHeight() / density; + + if (widthDp != 24 || heightDp != 24) { + return false; + } + } else { + throw new IllegalArgumentException("The format of the device icon is unsupported."); + } + return true; + } } diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig index 93d62cfeb537896d6f699ccd5263863385663928..2539a12a2a14dd0568e0887edd1c3c4ec72cdced 100644 --- a/core/java/android/companion/flags.aconfig +++ b/core/java/android/companion/flags.aconfig @@ -55,3 +55,11 @@ flag { description: "Enable association failure code API" bug: "331459560" } + +flag { + name: "association_device_icon" + is_exported: true + namespace: "companion" + description: "Enable set device icon API" + bug: "341057668" +} diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl index 6fe0a7342216d3d448a31e5c265466b24d944e53..d3a1c25b74d5967e0835ec792afb2132af463796 100644 --- a/core/java/android/companion/virtual/IVirtualDevice.aidl +++ b/core/java/android/companion/virtual/IVirtualDevice.aidl @@ -31,6 +31,8 @@ import android.content.ComponentName; import android.content.IntentFilter; import android.graphics.Point; import android.graphics.PointF; +import android.hardware.display.IVirtualDisplayCallback; +import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.VirtualDpadConfig; import android.hardware.input.VirtualKeyboardConfig; import android.hardware.input.VirtualKeyEvent; @@ -93,96 +95,97 @@ interface IVirtualDevice { */ boolean canCreateMirrorDisplays(); + /* + * Turns off all trusted non-mirror displays of the virtual device. + */ + void goToSleep(); + + /** + * Turns on all trusted non-mirror displays of the virtual device. + */ + void wakeUp(); + /** * Closes the virtual device and frees all associated resources. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void close(); /** * Specifies a policy for this virtual device. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void setDevicePolicy(int policyType, int devicePolicy); /** * Adds an exemption to the default activity launch policy. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void addActivityPolicyExemption(in ActivityPolicyExemption exemption); /** * Removes an exemption to the default activity launch policy. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void removeActivityPolicyExemption(in ActivityPolicyExemption exemption); /** * Specifies a policy for this virtual device on the given display. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void setDevicePolicyForDisplay(int displayId, int policyType, int devicePolicy); /** * Notifies that an audio session being started. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void onAudioSessionStarting(int displayId, IAudioRoutingCallback routingCallback, IAudioConfigChangedCallback configChangedCallback); /** * Notifies that an audio session has ended. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void onAudioSessionEnded(); + /** + * Creates a virtual display and registers it with the display framework. + */ + int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig, + in IVirtualDisplayCallback callback); + /** * Creates a new dpad and registers it with the input framework with the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void createVirtualDpad(in VirtualDpadConfig config, IBinder token); /** * Creates a new keyboard and registers it with the input framework with the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void createVirtualKeyboard(in VirtualKeyboardConfig config, IBinder token); /** * Creates a new mouse and registers it with the input framework with the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void createVirtualMouse(in VirtualMouseConfig config, IBinder token); /** * Creates a new touchscreen and registers it with the input framework with the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void createVirtualTouchscreen(in VirtualTouchscreenConfig config, IBinder token); /** * Creates a new navigation touchpad and registers it with the input framework with the given * token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void createVirtualNavigationTouchpad(in VirtualNavigationTouchpadConfig config, IBinder token); /** * Creates a new stylus and registers it with the input framework with the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void createVirtualStylus(in VirtualStylusConfig config, IBinder token); /** * Creates a new rotary encoder and registers it with the input framework with the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void createVirtualRotaryEncoder(in VirtualRotaryEncoderConfig config, IBinder token); /** * Removes the input device corresponding to the given token from the framework. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void unregisterInputDevice(IBinder token); /** @@ -194,67 +197,56 @@ interface IVirtualDevice { /** * Injects a key event to the virtual dpad corresponding to the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") boolean sendDpadKeyEvent(IBinder token, in VirtualKeyEvent event); /** * Injects a key event to the virtual keyboard corresponding to the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") boolean sendKeyEvent(IBinder token, in VirtualKeyEvent event); /** * Injects a button event to the virtual mouse corresponding to the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") boolean sendButtonEvent(IBinder token, in VirtualMouseButtonEvent event); /** * Injects a relative event to the virtual mouse corresponding to the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") boolean sendRelativeEvent(IBinder token, in VirtualMouseRelativeEvent event); /** * Injects a scroll event to the virtual mouse corresponding to the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") boolean sendScrollEvent(IBinder token, in VirtualMouseScrollEvent event); /** * Injects a touch event to the virtual touch input device corresponding to the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event); /** * Injects a motion event from the virtual stylus input device corresponding to the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") boolean sendStylusMotionEvent(IBinder token, in VirtualStylusMotionEvent event); /** * Injects a button event from the virtual stylus input device corresponding to the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") boolean sendStylusButtonEvent(IBinder token, in VirtualStylusButtonEvent event); /** * Injects a scroll event from the virtual rotary encoder corresponding to the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") boolean sendRotaryEncoderScrollEvent(IBinder token, in VirtualRotaryEncoderScrollEvent event); /** * Returns all virtual sensors created for this device. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") List getVirtualSensorList(); /** * Sends an event to the virtual sensor corresponding to the given token. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") boolean sendSensorEvent(IBinder token, in VirtualSensorEvent event); /** @@ -270,11 +262,9 @@ interface IVirtualDevice { PointF getCursorPosition(IBinder token); /** Sets whether to show or hide the cursor while this virtual device is active. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void setShowPointerIcon(boolean showPointerIcon); /** Sets an IME policy for the given display. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void setDisplayImePolicy(int displayId, int policy); /** @@ -282,33 +272,28 @@ interface IVirtualDevice { * when matching the provided IntentFilter and calls the callback with the intercepted * intent. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void registerIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor, in IntentFilter filter); /** * Unregisters a previously registered intent interceptor. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void unregisterIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor); /** * Creates a new virtual camera and registers it with the virtual camera service. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void registerVirtualCamera(in VirtualCameraConfig camera); /** * Destroys the virtual camera with given config and unregisters it from the virtual camera * service. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void unregisterVirtualCamera(in VirtualCameraConfig camera); /** * Returns the id of the virtual camera with given config. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") String getVirtualCameraId(in VirtualCameraConfig camera); /** @@ -318,7 +303,6 @@ interface IVirtualDevice { * This is needed for virtual devices that are created by the system, as the VirtualDeviceImpl * object is created before the returned VirtualDeviceInternal one. */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") void setListeners(in IVirtualDeviceActivityListener activityListener, in IVirtualDeviceSoundEffectListener soundEffectListener); } diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl index 83e18ec055992a6457a613a3ba0540c6bd0f883b..c98238c1d7ba14ad7755cc31fbc6fbb4624f3988 100644 --- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl +++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl @@ -23,8 +23,6 @@ import android.companion.virtual.IVirtualDeviceSoundEffectListener; import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceParams; import android.content.AttributionSource; -import android.hardware.display.IVirtualDisplayCallback; -import android.hardware.display.VirtualDisplayConfig; /** * Interface for communication between VirtualDeviceManager and VirtualDeviceManagerService. @@ -95,18 +93,6 @@ interface IVirtualDeviceManager { */ int getDevicePolicy(int deviceId, int policyType); - /** - * Creates a virtual display owned by a particular virtual device. - * - * @param virtualDisplayConfig The configuration used in creating the display - * @param callback A callback that receives display lifecycle events - * @param virtualDevice The device that will own this display - * @param packageName The package name of the calling app - */ - int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig, - in IVirtualDisplayCallback callback, in IVirtualDevice virtualDevice, - String packageName); - /** * Returns device-specific session id for playback, or AUDIO_SESSION_ID_GENERATE * if there's none. diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java index de20a68e52cb8927c0ba425bf023abcf83c1d522..d63a4434d7d827dd61aa2a7c64b1ecd06382727c 100644 --- a/core/java/android/companion/virtual/VirtualDeviceInternal.java +++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java @@ -26,7 +26,6 @@ import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.PendingIntent; import android.companion.virtual.audio.VirtualAudioDevice; @@ -251,6 +250,22 @@ public class VirtualDeviceInternal { } } + void goToSleep() { + try { + mVirtualDevice.goToSleep(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void wakeUp() { + try { + mVirtualDevice.wakeUp(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + void launchPendingIntent( int displayId, @NonNull PendingIntent pendingIntent, @@ -281,14 +296,12 @@ public class VirtualDeviceInternal { new DisplayManagerGlobal.VirtualDisplayCallback(callback, executor); final int displayId; try { - displayId = mService.createVirtualDisplay(config, callbackWrapper, mVirtualDevice, - mContext.getPackageName()); + displayId = mVirtualDevice.createVirtualDisplay(config, callbackWrapper); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance(); - return displayManager.createVirtualDisplayWrapper(config, callbackWrapper, - displayId); + return displayManager.createVirtualDisplayWrapper(config, callbackWrapper, displayId); } void close() { @@ -407,7 +420,6 @@ public class VirtualDeviceInternal { } } - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull VirtualStylus createVirtualStylus(@NonNull VirtualStylusConfig config) { try { @@ -420,7 +432,6 @@ public class VirtualDeviceInternal { } } - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull VirtualRotaryEncoderConfig config) { try { @@ -471,7 +482,6 @@ public class VirtualDeviceInternal { return mVirtualAudioDevice; } - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull VirtualCamera createVirtualCamera(@NonNull VirtualCameraConfig config) { try { diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 473ab27ee5605c650bb0dcfd5a371b1d8d81c36b..6ea7834243a410e6f8492f8ce34b971fa13e8b47 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -619,6 +619,43 @@ public final class VirtualDeviceManager { return mVirtualDeviceInternal.getVirtualSensorList(); } + /** + * Forces all trusted non-mirror displays of the virtual device to turn off. + * + *

    After this action, if all displays across all devices, including the default one, are + * off, then the physical device will be put to sleep. If the displays of this virtual + * device are already off, then nothing will happen.

    + * + *

    Overrides all the wake locks that are held. This is equivalent to pressing a "virtual + * power key" to turn off the screen.

    + * + * @see #wakeUp() + * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED + * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY + */ + @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER) + public void goToSleep() { + mVirtualDeviceInternal.goToSleep(); + } + + /** + * Forces all trusted non-mirror displays of the virtual device to turn on. + * + *

    If the displays of this virtual device are turned off, then they will be turned on. + * Additionally, if the device is asleep it will be awoken. If the displays of this virtual + * device are already on, then nothing will happen.

    + * + *

    This is equivalent to pressing a "virtual power key" to turn on the screen.

    + * + * @see #goToSleep() + * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED + * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY + */ + @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER) + public void wakeUp() { + mVirtualDeviceInternal.wakeUp(); + } + /** * Launches a given pending intent on the give display ID. * @@ -727,7 +764,6 @@ public final class VirtualDeviceManager { * Closes the virtual device, stopping and tearing down any virtual displays, associated * virtual audio device, and event injection that's currently in progress. */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close() { mVirtualDeviceInternal.close(); } @@ -746,7 +782,6 @@ public final class VirtualDeviceManager { * @see VirtualDeviceParams#POLICY_TYPE_ACTIVITY */ @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType, @VirtualDeviceParams.DevicePolicy int devicePolicy) { mVirtualDeviceInternal.setDevicePolicy(policyType, devicePolicy); @@ -769,7 +804,6 @@ public final class VirtualDeviceManager { * @see #setDevicePolicy */ @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull ComponentName componentName) { addActivityPolicyExemption(new ActivityPolicyExemption.Builder() .setComponentName(componentName) @@ -793,7 +827,6 @@ public final class VirtualDeviceManager { * @see #setDevicePolicy */ @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull ComponentName componentName) { removeActivityPolicyExemption(new ActivityPolicyExemption.Builder() .setComponentName(componentName) @@ -818,7 +851,6 @@ public final class VirtualDeviceManager { * @see #setDevicePolicy */ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API) - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) { mVirtualDeviceInternal.addActivityPolicyExemption(Objects.requireNonNull(exemption)); } @@ -834,7 +866,6 @@ public final class VirtualDeviceManager { * @see #setDevicePolicy */ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API) - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) { mVirtualDeviceInternal.removeActivityPolicyExemption(Objects.requireNonNull(exemption)); } @@ -857,7 +888,6 @@ public final class VirtualDeviceManager { * @see VirtualDeviceParams#POLICY_TYPE_ACTIVITY */ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API) - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy( @VirtualDeviceParams.DynamicDisplayPolicyType int policyType, @VirtualDeviceParams.DevicePolicy int devicePolicy, @@ -870,7 +900,6 @@ public final class VirtualDeviceManager { * * @param config the configurations of the virtual dpad. */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualDpad createVirtualDpad(@NonNull VirtualDpadConfig config) { Objects.requireNonNull(config, "config must not be null"); @@ -882,7 +911,6 @@ public final class VirtualDeviceManager { * * @param config the configurations of the virtual keyboard. */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualKeyboard createVirtualKeyboard(@NonNull VirtualKeyboardConfig config) { Objects.requireNonNull(config, "config must not be null"); @@ -900,7 +928,6 @@ public final class VirtualDeviceManager { * @deprecated Use {@link #createVirtualKeyboard(VirtualKeyboardConfig config)} instead */ @Deprecated - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualKeyboard createVirtualKeyboard(@NonNull VirtualDisplay display, @NonNull String inputDeviceName, int vendorId, int productId) { @@ -919,7 +946,6 @@ public final class VirtualDeviceManager { * * @param config the configurations of the virtual mouse. */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualMouse createVirtualMouse(@NonNull VirtualMouseConfig config) { Objects.requireNonNull(config, "config must not be null"); @@ -937,7 +963,6 @@ public final class VirtualDeviceManager { * @deprecated Use {@link #createVirtualMouse(VirtualMouseConfig config)} instead */ @Deprecated - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualMouse createVirtualMouse(@NonNull VirtualDisplay display, @NonNull String inputDeviceName, int vendorId, int productId) { @@ -956,7 +981,6 @@ public final class VirtualDeviceManager { * * @param config the configurations of the virtual touchscreen. */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualTouchscreen createVirtualTouchscreen( @NonNull VirtualTouchscreenConfig config) { @@ -976,7 +1000,6 @@ public final class VirtualDeviceManager { * instead */ @Deprecated - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualTouchscreen createVirtualTouchscreen(@NonNull VirtualDisplay display, @NonNull String inputDeviceName, int vendorId, int productId) { @@ -1003,7 +1026,6 @@ public final class VirtualDeviceManager { * @param config the configurations of the virtual navigation touchpad. * @see android.view.InputDevice#SOURCE_TOUCH_NAVIGATION */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualNavigationTouchpad createVirtualNavigationTouchpad( @NonNull VirtualNavigationTouchpadConfig config) { @@ -1015,7 +1037,6 @@ public final class VirtualDeviceManager { * * @param config the touchscreen configurations for the virtual stylus. */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS) public VirtualStylus createVirtualStylus( @@ -1029,7 +1050,6 @@ public final class VirtualDeviceManager { * @param config the configuration for the virtual rotary encoder. * @see android.view.InputDevice#SOURCE_ROTARY_ENCODER */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_VIRTUAL_ROTARY) public VirtualRotaryEncoder createVirtualRotaryEncoder( @@ -1057,7 +1077,6 @@ public final class VirtualDeviceManager { * applications running on virtual display is changed. * @return A {@link VirtualAudioDevice} instance. */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualAudioDevice createVirtualAudioDevice( @NonNull VirtualDisplay display, @@ -1078,7 +1097,6 @@ public final class VirtualDeviceManager { * @throws UnsupportedOperationException if virtual camera isn't supported on this device. * @see VirtualDeviceParams#POLICY_TYPE_CAMERA */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull @FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA) public VirtualCamera createVirtualCamera(@NonNull VirtualCameraConfig config) { @@ -1092,10 +1110,12 @@ public final class VirtualDeviceManager { /** * Sets the visibility of the pointer icon for this VirtualDevice's associated displays. * + *

    Only applicable to trusted displays.

    + * * @param showPointerIcon True if the pointer should be shown; false otherwise. The default * visibility is true. + * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean showPointerIcon) { mVirtualDeviceInternal.setShowPointerIcon(showPointerIcon); } @@ -1110,7 +1130,6 @@ public final class VirtualDeviceManager { * @throws SecurityException if the display is not owned by this device or is not * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED trusted} */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @FlaggedApi(Flags.FLAG_VDM_CUSTOM_IME) public void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) { if (Flags.vdmCustomIme()) { @@ -1174,7 +1193,6 @@ public final class VirtualDeviceManager { * is intercepted. * @see #unregisterIntentInterceptor(IntentInterceptorCallback) */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor( @NonNull IntentFilter interceptorFilter, @CallbackExecutor @NonNull Executor executor, @@ -1187,7 +1205,6 @@ public final class VirtualDeviceManager { * Unregisters the intent interceptor previously registered with * {@link #registerIntentInterceptor}. */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor( @NonNull IntentInterceptorCallback interceptorCallback) { mVirtualDeviceInternal.unregisterIntentInterceptor(interceptorCallback); diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index 03b72bdb8823cf44754875a644b7c9a4356020f4..65f9cbefb052fdfe363ba5162527bda2aefdf1d9 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -159,7 +159,7 @@ public final class VirtualDeviceParams implements Parcelable { * @hide */ @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_SENSORS, POLICY_TYPE_AUDIO, - POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY, POLICY_TYPE_CAMERA, + POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY, POLICY_TYPE_CLIPBOARD, POLICY_TYPE_CAMERA, POLICY_TYPE_BLOCKED_ACTIVITY}) @Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) @@ -220,11 +220,16 @@ public final class VirtualDeviceParams implements Parcelable { * Tells the activity manager how to handle recents entries for activities run on this device. * *
      - *
    • {@link #DEVICE_POLICY_DEFAULT}: Activities launched on VirtualDisplays owned by this + *
    • {@link #DEVICE_POLICY_DEFAULT}: Activities launched on trusted displays owned by this * device will appear in the host device recents. - *
    • {@link #DEVICE_POLICY_CUSTOM}: Activities launched on VirtualDisplays owned by this + *
    • {@link #DEVICE_POLICY_CUSTOM}: Activities launched on trusted displays owned by this * device will not appear in recents. *
    + * + *

    Activities launched on untrusted displays will always show in the host device recents, + * regardless of the policy.

    + * + * @see android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED */ public static final int POLICY_TYPE_RECENTS = 2; @@ -254,8 +259,10 @@ public final class VirtualDeviceParams implements Parcelable { * not shared with other devices' clipboards, including the clipboard of the default device. *
  • {@link #DEVICE_POLICY_CUSTOM}: The device's clipboard is shared with the default * device's clipboard. Any clipboard operation on the virtual device is as if it was done on - * the default device. + * the default device. Requires all displays of the virtual device to be trusted. *
+ * + * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED */ @FlaggedApi(Flags.FLAG_CROSS_DEVICE_CLIPBOARD) public static final int POLICY_TYPE_CLIPBOARD = 4; @@ -821,8 +828,8 @@ public final class VirtualDeviceParams implements Parcelable { } /** - * Specifies a component to be used as input method on all displays owned by this virtual - * device. + * Specifies a component to be used as input method on all trusted displays owned by this + * virtual device. * * @param inputMethodComponent The component name to be used as input method. Must comply to * all general input method requirements described in the guide to @@ -831,6 +838,7 @@ public final class VirtualDeviceParams implements Parcelable { * may interact with the virtual device, then there will effectively be no IME on this * device's displays for that user. * + * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED * @see android.inputmethodservice.InputMethodService * @attr ref android.R.styleable#InputMethod_isVirtualDeviceOnly * @attr ref android.R.styleable#InputMethod_showInInputMethodPicker diff --git a/core/java/android/companion/virtual/camera/VirtualCamera.java b/core/java/android/companion/virtual/camera/VirtualCamera.java index f7275894961cf5019acfcc84d0b4de732b784032..ece048d3a95bdbfe28ee3568dbdb16f068d1415a 100644 --- a/core/java/android/companion/virtual/camera/VirtualCamera.java +++ b/core/java/android/companion/virtual/camera/VirtualCamera.java @@ -17,7 +17,6 @@ package android.companion.virtual.camera; import android.annotation.FlaggedApi; -import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -94,7 +93,6 @@ public final class VirtualCamera implements Closeable { } @Override - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close() { try { mVirtualDevice.unregisterVirtualCamera(mConfig); diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java index 37e494bd8efef59f20a53dc94bbbb17e9f08482d..934a1a8ffcbd81ccad0ab081c5edaefa49ef3219 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensor.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java @@ -17,7 +17,6 @@ package android.companion.virtual.sensor; import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -136,7 +135,6 @@ public final class VirtualSensor implements Parcelable { /** * Send a sensor event to the system. */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendEvent(@NonNull VirtualSensorEvent event) { try { mVirtualDevice.sendSensorEvent(mToken, event); diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index b42133939f28dd5cf35dc00befc28a9bc631f51b..ff0bb25bbcccef951c6a88a020da8604b98175e7 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -1149,7 +1149,7 @@ public class ClipData implements Parcelable { for (int i = 0; i < size; i++) { final Item item = mItems.get(i); if (item.mIntent != null) { - item.mIntent.prepareToLeaveProcess(leavingPackage); + item.mIntent.prepareToLeaveProcess(leavingPackage, false); } if (item.mUri != null && leavingPackage) { if (StrictMode.vmFileUriExposureEnabled()) { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 9a93ec4d3b4072d56d39f0579038e3eb73bd5113..0bb0027fb0c3463c5aa66d283decfc375a39fc45 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -12221,6 +12221,8 @@ public class Intent implements Parcelable, Cloneable { /** * Collects keys in the extra bundle whose value are intents. + * With these keys collected on the client side, the system server would only unparcel values + * of these keys and create IntentCreatorToken for them. * @hide */ public void collectExtraIntentKeys() { @@ -12583,22 +12585,29 @@ public class Intent implements Parcelable, Cloneable { */ @android.ravenwood.annotation.RavenwoodThrow public void prepareToLeaveProcess(boolean leavingPackage) { + prepareToLeaveProcess(leavingPackage, true); + } + + /** + * @hide + */ + void prepareToLeaveProcess(boolean leavingPackage, boolean isTopLevel) { setAllowFds(false); if (mSelector != null) { - mSelector.prepareToLeaveProcess(leavingPackage); + mSelector.prepareToLeaveProcess(leavingPackage, false); } if (mClipData != null) { mClipData.prepareToLeaveProcess(leavingPackage, getFlags()); } if (mOriginalIntent != null) { - mOriginalIntent.prepareToLeaveProcess(leavingPackage); + mOriginalIntent.prepareToLeaveProcess(leavingPackage, false); } if (mExtras != null && !mExtras.isParcelled()) { final Object intent = mExtras.get(Intent.EXTRA_INTENT); if (intent instanceof Intent) { - ((Intent) intent).prepareToLeaveProcess(leavingPackage); + ((Intent) intent).prepareToLeaveProcess(leavingPackage, false); } } @@ -12672,6 +12681,10 @@ public class Intent implements Parcelable, Cloneable { StrictMode.onUnsafeIntentLaunch(this); } } + + if (isTopLevel) { + collectExtraIntentKeys(); + } } /** diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java index ca6d86ae2dd81f167c3537df7dc8fd27c08d66c8..f406927b62a573e61a8f3e72847fcf7080a3b30c 100644 --- a/core/java/android/content/IntentSender.java +++ b/core/java/android/content/IntentSender.java @@ -288,6 +288,9 @@ public class IntentSender implements Parcelable { @Nullable Executor executor, @Nullable OnFinished onFinished) throws SendIntentException { try { + if (intent != null) { + intent.collectExtraIntentKeys(); + } String resolvedType = intent != null ? intent.resolveTypeIfNeeded(context.getContentResolver()) : null; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 34bea1a4df6f4beabc6c3bb12e6af26032a190f5..cccfdb0938e594cedbe8e6cfc194ce2ba6a81587 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -2334,9 +2334,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * Whether an app allows its playback audio to be captured by other apps. * * @return {@code true} if the app indicates that its audio can be captured by other apps. - * - * @hide */ + @FlaggedApi(Flags.FLAG_AUDIO_PLAYBACK_CAPTURE_ALLOWANCE) public boolean isAudioPlaybackCaptureAllowed() { return (privateFlags & PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE) != 0; } diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java index d77b2f53fc5be1502b69f482e9833bef18f65903..f7191e605fb84d1fd38d495c7896efa2fcb17f35 100644 --- a/core/java/android/content/pm/SharedLibraryInfo.java +++ b/core/java/android/content/pm/SharedLibraryInfo.java @@ -208,6 +208,24 @@ public final class SharedLibraryInfo implements Parcelable { VersionedPackage.class.getClassLoader(), VersionedPackage.class); } + /** + * @hide + * @param name + * @param versionMajor + */ + public SharedLibraryInfo(String name, long versionMajor, int type) { + mPath = null; + mPackageName = null; + mName = name; + mVersion = versionMajor; + mType = type; + mDeclaringPackage = null; + mDependentPackages = null; + mDependencies = null; + mIsNative = false; + mOptionalDependentPackages = null; + } + /** * Gets the type of this library. * diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index 300740e84c60287536bf2a9c2972f4cacdc18e19..c7d7dc1eb0dedb2c143118032c6ca4ed3ea3e00c 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -287,6 +287,15 @@ flag { bug: "340879905" } +flag { + name: "audio_playback_capture_allowance" + is_exported: true + namespace: "package_manager_service" + description: "Feature flag to enable the feature to retrieve info about audio playback capture allowance at manifest level." + bug: "362425551" + is_fixed_read_only: true +} + flag { name: "get_packages_from_launcher_apps" namespace: "package_manager_service" diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index cf65539946a9f45e3e445cbd8506b740cd3410f9..7de7131fc2ad86b2c97c1a24c1b7f14cc189f9c8 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -46,13 +46,6 @@ flag { bug: "308105403" } -flag { - name: "start_user_before_scheduled_alarms" - namespace: "multiuser" - description: "Persist list of users with alarms scheduled and wakeup stopped users before alarms are due" - bug: "314907186" -} - flag { name: "add_ui_for_sounds_from_background_users" namespace: "multiuser" @@ -74,6 +67,16 @@ flag { bug: "365748524" } +flag { + name: "multiple_alarm_notifications_support" + namespace: "multiuser" + description: "Implement handling of multiple simultaneous alarms/timers on bg users" + bug: "367615180" + metadata { + purpose: PURPOSE_BUGFIX + } +} + flag { name: "enable_biometrics_to_unlock_private_space" is_exported: true @@ -198,23 +201,57 @@ flag { } flag { - name: "cache_profile_parent" + name: "fix_disabling_of_mu_toggle_when_restriction_applied" + namespace: "multiuser" + description: "When no_user_switch is set but no EnforcedAdmin is present, the toggle has to be disabled" + bug: "356387759" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "cache_profile_parent_read_only" namespace: "multiuser" description: "Cache getProfileParent to avoid unnecessary binder calls" bug: "350417399" metadata { purpose: PURPOSE_BUGFIX } + is_fixed_read_only: true } flag { - name: "fix_disabling_of_mu_toggle_when_restriction_applied" + name: "cache_profile_ids_read_only" namespace: "multiuser" - description: "When no_user_switch is set but no EnforcedAdmin is present, the toggle has to be disabled" - bug: "356387759" + description: "Cache getProfileIds to avoid unnecessary binder calls" + bug: "350421409" metadata { purpose: PURPOSE_BUGFIX } + is_fixed_read_only: true +} + +flag { + name: "cache_profile_type_read_only" + namespace: "multiuser" + description: "Cache getProfileType to avoid unnecessary binder calls" + bug: "350417403" + metadata { + purpose: PURPOSE_BUGFIX + } + is_fixed_read_only: true +} + +flag { + name: "cache_profiles_read_only" + namespace: "multiuser" + description: "Cache getProfiles to avoid unnecessary binder calls" + bug: "350419395" + metadata { + purpose: PURPOSE_BUGFIX + } + is_fixed_read_only: true } flag { @@ -259,6 +296,17 @@ flag { is_fixed_read_only: true } +flag { + name: "invalidate_cache_on_users_changed_read_only" + namespace: "multiuser" + description: "Invalidate the cache when users are added or removed to improve caches." + bug: "372383485" + metadata { + purpose: PURPOSE_BUGFIX + } + is_fixed_read_only: true +} + flag { name: "caches_not_invalidated_at_start_read_only" namespace: "multiuser" diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java index 74ce62c7abff274e8eaeccac2c015338c66ba0c9..19a13db15b05d5c1439797e0da7c27b250a77bc3 100644 --- a/core/java/android/content/pm/parsing/ApkLite.java +++ b/core/java/android/content/pm/parsing/ApkLite.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.content.pm.ArchivedPackageParcel; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningDetails; import android.content.pm.VerifierInfo; @@ -149,6 +150,8 @@ public class ApkLite { */ private final @Nullable String mEmergencyInstaller; + private final @NonNull List mDeclaredLibraries; + /** * Archival install info. */ @@ -165,7 +168,7 @@ public class ApkLite { int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy, Set requiredSplitTypes, Set splitTypes, boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean updatableSystem, - String emergencyInstaller) { + String emergencyInstaller, List declaredLibraries) { mPath = path; mPackageName = packageName; mSplitName = splitName; @@ -202,6 +205,7 @@ public class ApkLite { mUpdatableSystem = updatableSystem; mEmergencyInstaller = emergencyInstaller; mArchivedPackage = null; + mDeclaredLibraries = declaredLibraries; } public ApkLite(String path, ArchivedPackageParcel archivedPackage) { @@ -241,6 +245,7 @@ public class ApkLite { mUpdatableSystem = true; mEmergencyInstaller = null; mArchivedPackage = archivedPackage; + mDeclaredLibraries = null; } /** @@ -565,6 +570,11 @@ public class ApkLite { return mEmergencyInstaller; } + @DataClass.Generated.Member + public @NonNull List getDeclaredLibraries() { + return mDeclaredLibraries; + } + /** * Archival install info. */ @@ -574,10 +584,10 @@ public class ApkLite { } @DataClass.Generated( - time = 1706896661616L, + time = 1728333566322L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") + inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index ffb69c0a28218a91f192eec06bf0dc79f131600e..1a7f628ae61c3e28456451f12db9adbc78217044 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -24,6 +24,7 @@ import android.annotation.NonNull; import android.app.admin.DeviceAdminReceiver; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningDetails; import android.content.pm.VerifierInfo; import android.content.pm.parsing.result.ParseInput; @@ -92,6 +93,8 @@ public class ApkLiteParseUtils { private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES; private static final String TAG_PROCESSES = "processes"; private static final String TAG_PROCESS = "process"; + private static final String TAG_STATIC_LIBRARY = "static-library"; + private static final String TAG_LIBRARY = "library"; /** * Parse only lightweight details about the package at the given location. @@ -457,6 +460,7 @@ public class ApkLiteParseUtils { boolean hasDeviceAdminReceiver = false; boolean isSdkLibrary = false; + List declaredLibraries = new ArrayList<>(); // Only search the tree when the tag is the direct child of tag int type; @@ -521,6 +525,51 @@ public class ApkLiteParseUtils { break; case TAG_SDK_LIBRARY: isSdkLibrary = true; + // Mirrors ParsingPackageUtils#parseSdkLibrary until lite and full + // parsing are combined + String sdkLibName = parser.getAttributeValue( + ANDROID_RES_NAMESPACE, "name"); + int sdkLibVersionMajor = parser.getAttributeIntValue( + ANDROID_RES_NAMESPACE, "versionMajor", -1); + if (sdkLibName == null || sdkLibVersionMajor < 0) { + return input.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Bad uses-sdk-library declaration name: " + sdkLibName + + " version: " + sdkLibVersionMajor); + } + declaredLibraries.add(new SharedLibraryInfo( + sdkLibName, sdkLibVersionMajor, + SharedLibraryInfo.TYPE_SDK_PACKAGE)); + break; + case TAG_STATIC_LIBRARY: + // Mirrors ParsingPackageUtils#parseStaticLibrary until lite and full + // parsing are combined + String staticLibName = parser.getAttributeValue( + ANDROID_RES_NAMESPACE, "name"); + int staticLibVersion = parser.getAttributeIntValue( + ANDROID_RES_NAMESPACE, "version", -1); + int staticLibVersionMajor = parser.getAttributeIntValue( + ANDROID_RES_NAMESPACE, "versionMajor", 0); + if (staticLibName == null || staticLibVersion < 0) { + return input.error("Bad static-library declaration name: " + + staticLibName + " version: " + staticLibVersion); + } + declaredLibraries.add(new SharedLibraryInfo(staticLibName, + PackageInfo.composeLongVersionCode(staticLibVersionMajor, + staticLibVersion), SharedLibraryInfo.TYPE_STATIC)); + break; + case TAG_LIBRARY: + // Mirrors ParsingPackageUtils#parseLibrary until lite and full parsing + // are combined + String libName = parser.getAttributeValue( + ANDROID_RES_NAMESPACE, "name"); + if (libName == null) { + return input.error("Bad library declaration name: null"); + } + libName = libName.intern(); + declaredLibraries.add(new SharedLibraryInfo(libName, + SharedLibraryInfo.VERSION_UNDEFINED, + SharedLibraryInfo.TYPE_DYNAMIC)); break; case TAG_PROCESSES: final int processesDepth = parser.getDepth(); @@ -645,7 +694,8 @@ public class ApkLiteParseUtils { overlayIsStatic, overlayPriority, requiredSystemPropertyName, requiredSystemPropertyValue, minSdkVersion, targetSdkVersion, rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second, - hasDeviceAdminReceiver, isSdkLibrary, updatableSystem, emergencyInstaller)); + hasDeviceAdminReceiver, isSdkLibrary, updatableSystem, emergencyInstaller, + declaredLibraries)); } private static boolean isDeviceAdminReceiver( diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java index 116dd1fc9a4285c0365e1e883e8f4df897258095..9a2ee7fe4cc6b076df776218c69b854b35667ab2 100644 --- a/core/java/android/content/pm/parsing/PackageLite.java +++ b/core/java/android/content/pm/parsing/PackageLite.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.ArchivedPackageParcel; import android.content.pm.PackageInfo; +import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningDetails; import android.content.pm.VerifierInfo; @@ -114,6 +115,8 @@ public class PackageLite { */ private final boolean mIsSdkLibrary; + private final @NonNull List mDeclaredLibraries; + /** * Archival install info. */ @@ -154,6 +157,7 @@ public class PackageLite { mSplitApkPaths = splitApkPaths; mSplitRevisionCodes = splitRevisionCodes; mTargetSdk = targetSdk; + mDeclaredLibraries = baseApk.getDeclaredLibraries(); mArchivedPackage = baseApk.getArchivedPackage(); } @@ -433,6 +437,11 @@ public class PackageLite { return mIsSdkLibrary; } + @DataClass.Generated.Member + public @NonNull List getDeclaredLibraries() { + return mDeclaredLibraries; + } + /** * Archival install info. */ @@ -442,10 +451,10 @@ public class PackageLite { } @DataClass.Generated( - time = 1694792176268L, + time = 1728333569917L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") + inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/hardware/DisplayLuts.java b/core/java/android/hardware/DisplayLuts.java new file mode 100644 index 0000000000000000000000000000000000000000..b162ad6e2d156fb3b5d40bee9e8902b60deb7757 --- /dev/null +++ b/core/java/android/hardware/DisplayLuts.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +import android.annotation.NonNull; +import android.util.IntArray; + +import java.util.ArrayList; +import java.util.List; + +/** + * @hide + */ +public final class DisplayLuts { + private IntArray mOffsets; + private int mTotalLength; + + private List mLutBuffers; + private IntArray mLutDimensions; + private IntArray mLutSizes; + private IntArray mLutSamplingKeys; + private static final int LUT_LENGTH_LIMIT = 100000; + + public DisplayLuts() { + mOffsets = new IntArray(); + mTotalLength = 0; + + mLutBuffers = new ArrayList<>(); + mLutDimensions = new IntArray(); + mLutSizes = new IntArray(); + mLutSamplingKeys = new IntArray(); + } + + /** + * Add the lut to be applied. + * + * @param buffer + * @param dimension either 1D or 3D + * @param size + * @param samplingKey + */ + public void addLut(@NonNull float[] buffer, @LutProperties.Dimension int dimension, + int size, @LutProperties.SamplingKey int samplingKey) { + + int lutLength = 0; + if (dimension == LutProperties.ONE_DIMENSION) { + lutLength = size; + } else if (dimension == LutProperties.THREE_DIMENSION) { + lutLength = size * size * size; + } else { + clear(); + throw new IllegalArgumentException("The dimension is either 1D or 3D!"); + } + + if (lutLength >= LUT_LENGTH_LIMIT) { + clear(); + throw new IllegalArgumentException("The lut length is too big to handle!"); + } + + mOffsets.add(mTotalLength); + mTotalLength += lutLength; + + mLutBuffers.add(buffer); + mLutDimensions.add(dimension); + mLutSizes.add(size); + mLutSamplingKeys.add(samplingKey); + } + + private void clear() { + mTotalLength = 0; + mOffsets.clear(); + mLutBuffers.clear(); + mLutDimensions.clear(); + mLutSamplingKeys.clear(); + } + + /** + * @return the array of Lut buffers + */ + public float[] getLutBuffers() { + float[] buffer = new float[mTotalLength]; + + for (int i = 0; i < mLutBuffers.size(); i++) { + float[] lutBuffer = mLutBuffers.get(i); + System.arraycopy(lutBuffer, 0, buffer, mOffsets.get(i), lutBuffer.length); + } + return buffer; + } + + /** + * @return the starting point of each lut memory region of the lut buffer + */ + public int[] getOffsets() { + return mOffsets.toArray(); + } + + /** + * @return the array of Lut size + */ + public int[] getLutSizes() { + return mLutSizes.toArray(); + } + + /** + * @return the array of Lut dimension + */ + public int[] getLutDimensions() { + return mLutDimensions.toArray(); + } + + /** + * @return the array of sampling key + */ + public int[] getLutSamplingKeys() { + return mLutSamplingKeys.toArray(); + } +} diff --git a/core/java/android/hardware/LutProperties.java b/core/java/android/hardware/LutProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..57f8a4ece304413072335bfc861b8969fba8befb --- /dev/null +++ b/core/java/android/hardware/LutProperties.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Lut properties class. + * + * A Lut (Look-Up Table) is a pre-calculated table for color transformation. + * + * @hide + */ +public final class LutProperties { + private final @Dimension int mDimension; + private final long mSize; + private final @SamplingKey int[] mSamplingKeys; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SAMPLING_KEY_"}, value = { + SAMPLING_KEY_RGB, + SAMPLING_KEY_MAX_RGB + }) + public @interface SamplingKey { + } + + /** use r,g,b channel as the gain value of a Lut */ + public static final int SAMPLING_KEY_RGB = 0; + + /** use max of r,g,b channel as the gain value of a Lut */ + public static final int SAMPLING_KEY_MAX_RGB = 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + ONE_DIMENSION, + THREE_DIMENSION + }) + public @interface Dimension { + } + + /** The Lut is one dimensional */ + public static final int ONE_DIMENSION = 1; + + /** The Lut is three dimensional */ + public static final int THREE_DIMENSION = 3; + + public @Dimension int getDimension() { + return mDimension; + } + + /** + * @return the size of the Lut. + */ + public long getSize() { + return mSize; + } + + /** + * @return the list of sampling keys + */ + public @SamplingKey int[] getSamplingKeys() { + if (mSamplingKeys.length == 0) { + throw new IllegalStateException("no sampling key!"); + } + return mSamplingKeys; + } + + /* use in the native code */ + private LutProperties(@Dimension int dimension, long size, @SamplingKey int[] samplingKeys) { + if (dimension != ONE_DIMENSION || dimension != THREE_DIMENSION) { + throw new IllegalArgumentException("The dimension is either 1 or 3!"); + } + mDimension = dimension; + mSize = size; + mSamplingKeys = samplingKeys; + } +} diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS index 43d3f5466ccf7d15d9a9edc8ac7b0a2cea3ae916..f11625ed9d61698302df1b242dbcf320ffa3a1f9 100644 --- a/core/java/android/hardware/OWNERS +++ b/core/java/android/hardware/OWNERS @@ -19,3 +19,6 @@ per-file DataSpace* = file:/graphics/java/android/graphics/OWNERS # OverlayProperties per-file OverlayProperties* = file:/graphics/java/android/graphics/OWNERS + +# Lut related files +per-file *Lut* = file:/graphics/java/android/graphics/OWNERS \ No newline at end of file diff --git a/core/java/android/hardware/OverlayProperties.java b/core/java/android/hardware/OverlayProperties.java index 7b452a8e0857077b727f84200d4514c29e82ad96..24cfc1b53e00de2bdd861213b564dc8ece23f2ea 100644 --- a/core/java/android/hardware/OverlayProperties.java +++ b/core/java/android/hardware/OverlayProperties.java @@ -50,6 +50,8 @@ public final class OverlayProperties implements Parcelable { // Invoked on destruction private Runnable mCloser; + private LutProperties[] mLutProperties; + private OverlayProperties(long nativeObject) { if (nativeObject != 0) { mCloser = sRegistry.registerNativeAllocation(this, nativeObject); @@ -69,6 +71,20 @@ public final class OverlayProperties implements Parcelable { return sDefaultOverlayProperties; } + /** + * Gets the lut properties of the display. + * @hide + */ + public LutProperties[] getLutProperties() { + if (mNativeObject == 0) { + return null; + } + if (mLutProperties == null) { + mLutProperties = nGetLutProperties(mNativeObject); + } + return mLutProperties; + } + /** * Indicates that hardware composition of a buffer encoded with the provided {@link DataSpace} * and {@link HardwareBuffer.Format} is supported on the device. @@ -140,4 +156,5 @@ public final class OverlayProperties implements Parcelable { long nativeObject, int dataspace, int format); private static native void nWriteOverlayPropertiesToParcel(long nativeObject, Parcel dest); private static native long nReadOverlayPropertiesFromParcel(Parcel in); -} + private static native LutProperties[] nGetLutProperties(long nativeObject); +} \ No newline at end of file diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 9e3a9b389b0599cee01baabcb5b08483342ada3e..b7856303fc053e3deb09d7e679760b46e7cb4459 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -19,6 +19,7 @@ package android.hardware.camera2; import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA; import static android.content.Context.DEVICE_ID_DEFAULT; +import static android.content.Context.DEVICE_ID_INVALID; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; @@ -36,6 +37,7 @@ import android.companion.virtual.VirtualDeviceManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.Overridable; +import android.content.AttributionSource; import android.content.AttributionSourceState; import android.content.Context; import android.content.pm.PackageManager; @@ -676,8 +678,8 @@ public final class CameraManager { } try { for (String physicalCameraId : physicalCameraIds) { - AttributionSourceState clientAttribution = getClientAttribution(); - clientAttribution.deviceId = DEVICE_ID_DEFAULT; + AttributionSourceState clientAttribution = getClientAttribution(DEVICE_ID_DEFAULT, + /* useContextAttributionSource= */ false); CameraMetadataNative physicalCameraInfo = cameraService.getCameraCharacteristics( physicalCameraId, @@ -974,27 +976,58 @@ public final class CameraManager { } /** - * Constructs an AttributionSourceState with only the uid, pid, and deviceId fields set + * Retrieves the AttributionSourceState to pass to the CameraService. * - *

This method is a temporary stopgap in the transition to using AttributionSource. Currently - * AttributionSourceState is only used as a vehicle for passing deviceId, uid, and pid - * arguments.

+ * @param deviceIdOverride An override of the AttributionSource's deviceId, if not equal to + * DEVICE_ID_INVALID + * @param useContextAttributionSource Whether to return the full attribution source provided by + * the Context. + * + * @hide + */ + public AttributionSourceState getClientAttribution(int deviceIdOverride, + boolean useContextAttributionSource) { + AttributionSource contextAttributionSource = mContext.getAttributionSource(); + if (deviceIdOverride != DEVICE_ID_INVALID) { + contextAttributionSource = contextAttributionSource.withDeviceId(deviceIdOverride); + } + AttributionSourceState contextAttributionSourceState = + contextAttributionSource.asState(); + + if (Flags.useContextAttributionSource() && useContextAttributionSource) { + return contextAttributionSourceState; + } else { + AttributionSourceState clientAttribution = + new AttributionSourceState(); + clientAttribution.uid = USE_CALLING_UID; + clientAttribution.pid = USE_CALLING_PID; + clientAttribution.deviceId = contextAttributionSourceState.deviceId; + clientAttribution.packageName = mContext.getOpPackageName(); + clientAttribution.attributionTag = mContext.getAttributionTag(); + clientAttribution.next = new AttributionSourceState[0]; + return clientAttribution; + } + } + + /** + * Retrieves the AttributionSourceState to pass to the CameraService. + * + * @param useContextAttributionSource Whether to return the full attribution source provided by + * the Context. + * + * @hide + */ + public AttributionSourceState getClientAttribution(boolean useContextAttributionSource) { + return getClientAttribution(DEVICE_ID_INVALID, useContextAttributionSource); + } + + /** + * Retrieves the AttributionSourceState to pass to the CameraService. * * @hide */ public AttributionSourceState getClientAttribution() { - // TODO: Send the full contextAttribution over aidl, remove USE_CALLING_* - AttributionSourceState contextAttribution = - mContext.getAttributionSource().asState(); - AttributionSourceState clientAttribution = - new AttributionSourceState(); - clientAttribution.uid = USE_CALLING_UID; - clientAttribution.pid = USE_CALLING_PID; - clientAttribution.deviceId = contextAttribution.deviceId; - clientAttribution.packageName = mContext.getOpPackageName(); - clientAttribution.attributionTag = mContext.getAttributionTag(); - clientAttribution.next = new AttributionSourceState[0]; - return clientAttribution; + return getClientAttribution(DEVICE_ID_INVALID, /* useContextAttributionSource= */ false); } /** @@ -1049,7 +1082,7 @@ public final class CameraManager { } AttributionSourceState clientAttribution = - getClientAttribution(); + getClientAttribution(/* useContextAttributionSource= */ true); cameraUser = cameraService.connectDevice( callbacks, diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index a69a3713319204498612b7d51fe7d20ef13af883..acb48f328f1a278f17d16bf12a22946d46748824 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -2270,7 +2270,17 @@ public abstract class CameraMetadata { * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are ignored. The * application has control over the various * android.flash.* fields.

+ *

If the device supports manual flash strength control, i.e., + * if {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and + * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1, then + * the auto-exposure (AE) precapture metering sequence should be + * triggered for the configured flash mode and strength to avoid + * the image being incorrectly exposed at different + * {@link CaptureRequest#FLASH_STRENGTH_LEVEL android.flash.strengthLevel}.

* + * @see CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL + * @see CaptureRequest#FLASH_STRENGTH_LEVEL + * @see CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL * @see CaptureRequest#SENSOR_EXPOSURE_TIME * @see CaptureRequest#SENSOR_FRAME_DURATION * @see CaptureRequest#SENSOR_SENSITIVITY diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 3f5ae919657749dcfae7aa7172a7dfcf8a413ed6..a193ee10b6e6914dc8b8361a4d1e676612725c35 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -1358,6 +1358,13 @@ public final class CaptureRequest extends CameraMetadata> * camera device auto-exposure routine for the overridden * fields for a given capture will be available in its * CaptureResult.

+ *

When {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is AE_MODE_ON and if the device + * supports manual flash strength control, i.e., + * if {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and + * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1, then + * the auto-exposure (AE) precapture metering sequence should be + * triggered to avoid the image being incorrectly exposed at + * different {@link CaptureRequest#FLASH_STRENGTH_LEVEL android.flash.strengthLevel}.

*

Possible values:

*
    *
  • {@link #CONTROL_AE_MODE_OFF OFF}
  • @@ -1373,9 +1380,13 @@ public final class CaptureRequest extends CameraMetadata> *

    This key is available on all devices.

    * * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES + * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#CONTROL_MODE * @see CameraCharacteristics#FLASH_INFO_AVAILABLE * @see CaptureRequest#FLASH_MODE + * @see CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL + * @see CaptureRequest#FLASH_STRENGTH_LEVEL + * @see CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL * @see CaptureRequest#SENSOR_EXPOSURE_TIME * @see CaptureRequest#SENSOR_FRAME_DURATION * @see CaptureRequest#SENSOR_SENSITIVITY diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index a18a634918f91be876f3c28ded8dabd6567f73f3..e5ca46ab0a72133fbe2e43ea6901911ce142dea7 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -759,6 +759,13 @@ public class CaptureResult extends CameraMetadata> { * camera device auto-exposure routine for the overridden * fields for a given capture will be available in its * CaptureResult.

    + *

    When {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is AE_MODE_ON and if the device + * supports manual flash strength control, i.e., + * if {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and + * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1, then + * the auto-exposure (AE) precapture metering sequence should be + * triggered to avoid the image being incorrectly exposed at + * different {@link CaptureRequest#FLASH_STRENGTH_LEVEL android.flash.strengthLevel}.

    *

    Possible values:

    *
      *
    • {@link #CONTROL_AE_MODE_OFF OFF}
    • @@ -774,9 +781,13 @@ public class CaptureResult extends CameraMetadata> { *

      This key is available on all devices.

      * * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES + * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#CONTROL_MODE * @see CameraCharacteristics#FLASH_INFO_AVAILABLE * @see CaptureRequest#FLASH_MODE + * @see CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL + * @see CaptureRequest#FLASH_STRENGTH_LEVEL + * @see CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL * @see CaptureRequest#SENSOR_EXPOSURE_TIME * @see CaptureRequest#SENSOR_FRAME_DURATION * @see CaptureRequest#SENSOR_SENSITIVITY diff --git a/core/java/android/hardware/devicestate/DeviceStateInfo.java b/core/java/android/hardware/devicestate/DeviceStateInfo.java index 28561ec37ee6e578d612d91f4504e36ab6222f57..fd6f0b81b6da9e399e4d6d89959fa7f42063fdea 100644 --- a/core/java/android/hardware/devicestate/DeviceStateInfo.java +++ b/core/java/android/hardware/devicestate/DeviceStateInfo.java @@ -28,7 +28,6 @@ import java.util.ArrayList; import java.util.Objects; import java.util.concurrent.Executor; - /** * Information about the state of the device. * @@ -63,11 +62,13 @@ public final class DeviceStateInfo implements Parcelable { * ignoring any override requests made through a call to {@link DeviceStateManager#requestState( * DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. */ + @NonNull public final DeviceState baseState; /** * The state of the device. */ + @NonNull public final DeviceState currentState; /** @@ -78,8 +79,9 @@ public final class DeviceStateInfo implements Parcelable { */ // Using the specific types to avoid virtual method calls in binder transactions @SuppressWarnings("NonApiType") - public DeviceStateInfo(@NonNull ArrayList supportedStates, DeviceState baseState, - DeviceState state) { + public DeviceStateInfo(@NonNull ArrayList supportedStates, + @NonNull DeviceState baseState, + @NonNull DeviceState state) { this.supportedStates = supportedStates; this.baseState = baseState; this.currentState = state; @@ -97,7 +99,7 @@ public final class DeviceStateInfo implements Parcelable { public boolean equals(@Nullable Object other) { if (this == other) return true; if (other == null || (getClass() != other.getClass())) return false; - DeviceStateInfo that = (DeviceStateInfo) other; + final DeviceStateInfo that = (DeviceStateInfo) other; return baseState.equals(that.baseState) && currentState.equals(that.currentState) && Objects.equals(supportedStates, that.supportedStates); @@ -126,8 +128,16 @@ public final class DeviceStateInfo implements Parcelable { return diff; } + // Parcelable implementation + @Override - public void writeToParcel(Parcel dest, int flags) { + public int describeContents() { + return 0; + } + + /** Writes to Parcel. */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(supportedStates.size()); for (int i = 0; i < supportedStates.size(); i++) { dest.writeTypedObject(supportedStates.get(i).getConfiguration(), flags); @@ -137,28 +147,27 @@ public final class DeviceStateInfo implements Parcelable { dest.writeTypedObject(currentState.getConfiguration(), flags); } - @Override - public int describeContents() { - return 0; + /** Reads from Parcel. */ + private DeviceStateInfo(@NonNull Parcel in) { + final int numberOfSupportedStates = in.readInt(); + final ArrayList supportedStates = new ArrayList<>(numberOfSupportedStates); + for (int i = 0; i < numberOfSupportedStates; i++) { + final DeviceState.Configuration configuration = + Objects.requireNonNull(in.readTypedObject(DeviceState.Configuration.CREATOR)); + supportedStates.add(i, new DeviceState(configuration)); + } + this.supportedStates = supportedStates; + + this.baseState = new DeviceState( + Objects.requireNonNull(in.readTypedObject(DeviceState.Configuration.CREATOR))); + this.currentState = new DeviceState( + Objects.requireNonNull(in.readTypedObject(DeviceState.Configuration.CREATOR))); } public static final @NonNull Creator CREATOR = new Creator<>() { @Override - public DeviceStateInfo createFromParcel(Parcel source) { - final int numberOfSupportedStates = source.readInt(); - final ArrayList supportedStates = new ArrayList<>(numberOfSupportedStates); - for (int i = 0; i < numberOfSupportedStates; i++) { - DeviceState.Configuration configuration = source.readTypedObject( - DeviceState.Configuration.CREATOR); - supportedStates.add(i, new DeviceState(configuration)); - } - - final DeviceState baseState = new DeviceState( - source.readTypedObject(DeviceState.Configuration.CREATOR)); - final DeviceState currentState = new DeviceState( - source.readTypedObject(DeviceState.Configuration.CREATOR)); - - return new DeviceStateInfo(supportedStates, baseState, currentState); + public DeviceStateInfo createFromParcel(@NonNull Parcel in) { + return new DeviceStateInfo(in); } @Override diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java index 0c8401992913654fd6e736180685d0610d715a18..739dbe1569c85b387a99ea887be6c699fb77a386 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java @@ -32,6 +32,7 @@ import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.window.flags.Flags; import java.util.ArrayList; import java.util.List; @@ -46,6 +47,8 @@ import java.util.concurrent.Executor; */ @VisibleForTesting(visibility = Visibility.PACKAGE) public final class DeviceStateManagerGlobal { + @Nullable + @GuardedBy("DeviceStateManagerGlobal.class") private static DeviceStateManagerGlobal sInstance; private static final String TAG = "DeviceStateManagerGlobal"; private static final boolean DEBUG = Build.IS_DEBUGGABLE; @@ -58,7 +61,7 @@ public final class DeviceStateManagerGlobal { public static DeviceStateManagerGlobal getInstance() { synchronized (DeviceStateManagerGlobal.class) { if (sInstance == null) { - IBinder b = ServiceManager.getService(Context.DEVICE_STATE_SERVICE); + final IBinder b = ServiceManager.getService(Context.DEVICE_STATE_SERVICE); if (b != null) { sInstance = new DeviceStateManagerGlobal(IDeviceStateManager .Stub.asInterface(b)); @@ -83,10 +86,12 @@ public final class DeviceStateManagerGlobal { @GuardedBy("mLock") private DeviceStateInfo mLastReceivedInfo; + // Constructor should be called while holding the lock. + // @GuardedBy("DeviceStateManagerGlobal.class") can't be used on constructors. @VisibleForTesting public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) { mDeviceStateManager = deviceStateManager; - registerCallbackIfNeededLocked(); + registerCallbackLocked(); } /** @@ -94,6 +99,7 @@ public final class DeviceStateManagerGlobal { * * @see DeviceStateManager#getSupportedDeviceStates() */ + @NonNull public List getSupportedDeviceStates() { synchronized (mLock) { final DeviceStateInfo currentInfo; @@ -126,8 +132,8 @@ public final class DeviceStateManagerGlobal { conditional = true) public void requestState(@NonNull DeviceStateRequest request, @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) { - DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback, - executor); + final DeviceStateRequestWrapper requestWrapper = + new DeviceStateRequestWrapper(request, callback, executor); synchronized (mLock) { if (findRequestTokenLocked(request) != null) { // This request has already been submitted. @@ -136,7 +142,7 @@ public final class DeviceStateManagerGlobal { // Add the request wrapper to the mRequests array before requesting the state as the // callback could be triggered immediately if the mDeviceStateManager IBinder is in the // same process as this instance. - IBinder token = new Binder(); + final IBinder token = new Binder(); mRequests.put(token, requestWrapper); try { @@ -176,8 +182,8 @@ public final class DeviceStateManagerGlobal { @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestBaseStateOverride(@NonNull DeviceStateRequest request, @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) { - DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback, - executor); + final DeviceStateRequestWrapper requestWrapper = + new DeviceStateRequestWrapper(request, callback, executor); synchronized (mLock) { if (findRequestTokenLocked(request) != null) { // This request has already been submitted. @@ -186,7 +192,7 @@ public final class DeviceStateManagerGlobal { // Add the request wrapper to the mRequests array before requesting the state as the // callback could be triggered immediately if the mDeviceStateManager IBinder is in the // same process as this instance. - IBinder token = new Binder(); + final IBinder token = new Binder(); mRequests.put(token, requestWrapper); try { @@ -225,7 +231,7 @@ public final class DeviceStateManagerGlobal { public void registerDeviceStateCallback(@NonNull DeviceStateCallback callback, @NonNull Executor executor) { synchronized (mLock) { - int index = findCallbackLocked(callback); + final int index = findCallbackLocked(callback); if (index != -1) { // This callback is already registered. return; @@ -233,7 +239,8 @@ public final class DeviceStateManagerGlobal { // Add the callback wrapper to the mCallbacks array after registering the callback as // the callback could be triggered immediately if the mDeviceStateManager IBinder is in // the same process as this instance. - DeviceStateCallbackWrapper wrapper = new DeviceStateCallbackWrapper(callback, executor); + final DeviceStateCallbackWrapper wrapper = + new DeviceStateCallbackWrapper(callback, executor); mCallbacks.add(wrapper); if (mLastReceivedInfo != null) { @@ -253,7 +260,7 @@ public final class DeviceStateManagerGlobal { @VisibleForTesting(visibility = Visibility.PACKAGE) public void unregisterDeviceStateCallback(@NonNull DeviceStateCallback callback) { synchronized (mLock) { - int indexToRemove = findCallbackLocked(callback); + final int indexToRemove = findCallbackLocked(callback); if (indexToRemove != -1) { mCallbacks.remove(indexToRemove); } @@ -276,15 +283,20 @@ public final class DeviceStateManagerGlobal { } } - private void registerCallbackIfNeededLocked() { - if (mCallback == null) { - mCallback = new DeviceStateManagerCallback(); - try { + @GuardedBy("DeviceStateManagerGlobal.class") + private void registerCallbackLocked() { + mCallback = new DeviceStateManagerCallback(); + try { + if (Flags.wlinfoOncreate()) { + synchronized (mLock) { + mLastReceivedInfo = mDeviceStateManager.registerCallback(mCallback); + } + } else { mDeviceStateManager.registerCallback(mCallback); - } catch (RemoteException ex) { - mCallback = null; - throw ex.rethrowFromSystemServer(); } + } catch (RemoteException ex) { + mCallback = null; + throw ex.rethrowFromSystemServer(); } } @@ -298,6 +310,7 @@ public final class DeviceStateManagerGlobal { } @Nullable + @GuardedBy("mLock") private IBinder findRequestTokenLocked(@NonNull DeviceStateRequest request) { for (int i = 0; i < mRequests.size(); i++) { if (mRequests.valueAt(i).mRequest.equals(request)) { @@ -309,8 +322,8 @@ public final class DeviceStateManagerGlobal { /** Handles a call from the server that the device state info has changed. */ private void handleDeviceStateInfoChanged(@NonNull DeviceStateInfo info) { - ArrayList callbacks; - DeviceStateInfo oldInfo; + final ArrayList callbacks; + final DeviceStateInfo oldInfo; synchronized (mLock) { oldInfo = mLastReceivedInfo; mLastReceivedInfo = info; @@ -335,8 +348,8 @@ public final class DeviceStateManagerGlobal { * Handles a call from the server that a request for the supplied {@code token} has become * active. */ - private void handleRequestActive(IBinder token) { - DeviceStateRequestWrapper request; + private void handleRequestActive(@NonNull IBinder token) { + final DeviceStateRequestWrapper request; synchronized (mLock) { request = mRequests.get(token); } @@ -349,8 +362,8 @@ public final class DeviceStateManagerGlobal { * Handles a call from the server that a request for the supplied {@code token} has become * canceled. */ - private void handleRequestCanceled(IBinder token) { - DeviceStateRequestWrapper request; + private void handleRequestCanceled(@NonNull IBinder token) { + final DeviceStateRequestWrapper request; synchronized (mLock) { request = mRequests.remove(token); } @@ -361,17 +374,17 @@ public final class DeviceStateManagerGlobal { private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub { @Override - public void onDeviceStateInfoChanged(DeviceStateInfo info) { + public void onDeviceStateInfoChanged(@NonNull DeviceStateInfo info) { handleDeviceStateInfoChanged(info); } @Override - public void onRequestActive(IBinder token) { + public void onRequestActive(@NonNull IBinder token) { handleRequestActive(token); } @Override - public void onRequestCanceled(IBinder token) { + public void onRequestCanceled(@NonNull IBinder token) { handleRequestCanceled(token); } } @@ -388,7 +401,8 @@ public final class DeviceStateManagerGlobal { mExecutor = executor; } - void notifySupportedDeviceStatesChanged(List newSupportedDeviceStates) { + void notifySupportedDeviceStatesChanged( + @NonNull List newSupportedDeviceStates) { mExecutor.execute(() -> mDeviceStateCallback.onSupportedStatesChanged(newSupportedDeviceStates)); } @@ -398,7 +412,7 @@ public final class DeviceStateManagerGlobal { () -> mDeviceStateCallback.onDeviceStateChanged(newDeviceState)); } - private void execute(String traceName, Runnable r) { + private void execute(@NonNull String traceName, @NonNull Runnable r) { mExecutor.execute(() -> { if (DEBUG) { Trace.beginSection( @@ -416,6 +430,7 @@ public final class DeviceStateManagerGlobal { } private static final class DeviceStateRequestWrapper { + @NonNull private final DeviceStateRequest mRequest; @Nullable private final DeviceStateRequest.Callback mCallback; diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl index ea4fe261b420894b8b77ab554222ed308ce57a0b..50d0623e4497cf4e666b199e8a4ec295d1541648 100644 --- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl +++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl @@ -32,16 +32,24 @@ interface IDeviceStateManager { DeviceStateInfo getDeviceStateInfo(); /** - * Registers a callback to receive notifications from the device state manager. Only one - * callback can be registered per-process. + * Registers a callback to receive notifications from the device state manager and returns the + * current {@link DeviceStateInfo}. Only one callback can be registered per-process. *

      * As the callback mechanism is used to alert the caller of changes to request status a callback * MUST be registered before calling {@link #requestState(IBinder, int, int)} or * {@link #cancelRequest(IBinder)}, otherwise an exception will be thrown. + *

      + * Upon successful registration, this method returns the committed {@link DeviceStateInfo} if + * available, ensuring the availability of the device state after the callback is registered. + * This guarantees that the client will have access to the latest device state immediately upon + * completion of the two-way IPC call. * + * @param callback the callback to register for device state notifications. + * @return DeviceStateInfo the current device state information including the committed state + * or null if no state has been committed by the {@link DeviceStateProvider} yet. * @throws SecurityException if a callback is already registered for the calling process. */ - void registerCallback(in IDeviceStateManagerCallback callback); + @nullable DeviceStateInfo registerCallback(in IDeviceStateManagerCallback callback); /** * Requests that the device enter the supplied {@code state}. A callback MUST have been diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 36e816af843932d7c450e6288f963edfd13e5fb2..75ffcc3a88630f925fecfaa9f4acba1002998425 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -92,9 +92,10 @@ public abstract class DisplayManagerInternal { boolean waitForNegativeProximity); /** - * Returns {@code true} if the proximity sensor screen-off function is available. + * Returns {@code true} if the proximity sensor screen-off function is available for the given + * display. */ - public abstract boolean isProximitySensorAvailable(); + public abstract boolean isProximitySensorAvailable(int displayId); /** * Registers a display group listener which will be informed of the addition, removal, or change @@ -459,6 +460,16 @@ public abstract class DisplayManagerInternal { */ public abstract void stylusGestureStarted(long eventTime); + /** + * Called by {@link com.android.server.wm.ContentRecorder} to verify whether + * the display is allowed to mirror primary display's content. + * @param displayId the id of the display where we mirror to. + * @return true if the mirroring dialog is confirmed (display is enabled), or + * {@link com.android.server.display.ExternalDisplayPolicy#ENABLE_ON_CONNECT} + * system property is enabled. + */ + public abstract boolean isDisplayReadyForMirroring(int displayId); + /** * Describes the requested power state of the display. * diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java index 177ee6f1540a3e98fd7a125847c3b2321a3442ed..897ce4a7b022f9624d2aee19684fa89b576575e0 100644 --- a/core/java/android/hardware/input/InputSettings.java +++ b/core/java/android/hardware/input/InputSettings.java @@ -24,6 +24,8 @@ import static com.android.hardware.input.Flags.keyboardA11yBounceKeysFlag; import static com.android.hardware.input.Flags.keyboardA11ySlowKeysFlag; import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag; import static com.android.hardware.input.Flags.keyboardA11yMouseKeys; +import static com.android.hardware.input.Flags.mouseReverseVerticalScrolling; +import static com.android.hardware.input.Flags.mouseSwapPrimaryButton; import static com.android.hardware.input.Flags.touchpadTapDragging; import static com.android.hardware.input.Flags.touchpadVisualizer; import static com.android.input.flags.Flags.enableInputFilterRustImpl; @@ -362,6 +364,22 @@ public class InputSettings { return touchpadVisualizer(); } + /** + * Returns true if the feature flag for mouse reverse vertical scrolling is enabled. + * @hide + */ + public static boolean isMouseReverseVerticalScrollingFeatureFlagEnabled() { + return mouseReverseVerticalScrolling(); + } + + /** + * Returns true if the feature flag for mouse swap primary button is enabled. + * @hide + */ + public static boolean isMouseSwapPrimaryButtonFeatureFlagEnabled() { + return mouseSwapPrimaryButton(); + } + /** * Returns true if the touchpad visualizer is allowed to appear. * @@ -500,6 +518,86 @@ public class InputSettings { return isStylusPointerIconEnabled(context, false /* forceReloadSetting */); } + /** + * Whether mouse vertical scrolling is enabled, this applies only to connected mice. + * + * @param context The application context. + * @return Whether the mouse will have its vertical scrolling reversed + * (scroll down to move up). + * + * @hide + */ + public static boolean isMouseReverseVerticalScrollingEnabled(@NonNull Context context) { + if (!isMouseReverseVerticalScrollingFeatureFlagEnabled()) { + return false; + } + + return Settings.System.getIntForUser(context.getContentResolver(), + Settings.System.MOUSE_REVERSE_VERTICAL_SCROLLING, 0, UserHandle.USER_CURRENT) + != 0; + } + + /** + * Sets whether the connected mouse will have its vertical scrolling reversed. + * + * @param context The application context. + * @param reverseScrolling Whether reverse scrolling is enabled. + * + * @hide + */ + @RequiresPermission(Manifest.permission.WRITE_SETTINGS) + public static void setMouseReverseVerticalScrolling(@NonNull Context context, + boolean reverseScrolling) { + if (!isMouseReverseVerticalScrollingFeatureFlagEnabled()) { + return; + } + + Settings.System.putIntForUser(context.getContentResolver(), + Settings.System.MOUSE_REVERSE_VERTICAL_SCROLLING, reverseScrolling ? 1 : 0, + UserHandle.USER_CURRENT); + } + + /** + * Whether the primary mouse button is swapped on connected mice. + * + * @param context The application context. + * @return Whether mice will have their primary buttons swapped, so that left clicking will + * perform the secondary action (e.g. show menu) and right clicking will perform the primary + * action. + * + * @hide + */ + public static boolean isMouseSwapPrimaryButtonEnabled(@NonNull Context context) { + if (!isMouseSwapPrimaryButtonFeatureFlagEnabled()) { + return false; + } + + return Settings.System.getIntForUser(context.getContentResolver(), + Settings.System.MOUSE_SWAP_PRIMARY_BUTTON, 0, UserHandle.USER_CURRENT) + != 0; + } + + /** + * Sets whether mice will have their primary buttons swapped between left and right + * clicks. + * + * @param context The application context. + * @param swapPrimaryButton Whether swapping the primary button is enabled. + * + * @hide + */ + @RequiresPermission(Manifest.permission.WRITE_SETTINGS) + public static void setMouseSwapPrimaryButton(@NonNull Context context, + boolean swapPrimaryButton) { + if (!isMouseSwapPrimaryButtonFeatureFlagEnabled()) { + return; + } + + Settings.System.putIntForUser(context.getContentResolver(), + Settings.System.MOUSE_SWAP_PRIMARY_BUTTON, swapPrimaryButton ? 1 : 0, + UserHandle.USER_CURRENT); + } + /** * Whether Accessibility bounce keys feature is enabled. * diff --git a/core/java/android/hardware/input/VirtualDpad.java b/core/java/android/hardware/input/VirtualDpad.java index 5985c39034ea7562ff9ada547abdbb7004eecbd9..5b08a0e70b44a159eafd67e6d9baf9d4f40ba576 100644 --- a/core/java/android/hardware/input/VirtualDpad.java +++ b/core/java/android/hardware/input/VirtualDpad.java @@ -17,7 +17,6 @@ package android.hardware.input; import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.companion.virtual.IVirtualDevice; import android.os.IBinder; @@ -72,7 +71,6 @@ public class VirtualDpad extends VirtualInputDevice { * * @param event the event to send */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendKeyEvent(@NonNull VirtualKeyEvent event) { try { if (!mSupportedKeyCodes.contains(event.getKeyCode())) { diff --git a/core/java/android/hardware/input/VirtualInputDevice.java b/core/java/android/hardware/input/VirtualInputDevice.java index affa4ed7e983f2b52f20a248a5dd0ca6414f7dc2..8e4e097ab8033328b5bec0b090570f882f0a3f03 100644 --- a/core/java/android/hardware/input/VirtualInputDevice.java +++ b/core/java/android/hardware/input/VirtualInputDevice.java @@ -16,7 +16,6 @@ package android.hardware.input; -import android.annotation.RequiresPermission; import android.companion.virtual.IVirtualDevice; import android.os.IBinder; import android.os.RemoteException; @@ -68,7 +67,6 @@ abstract class VirtualInputDevice implements Closeable { } @Override - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close() { Log.d(TAG, "Closing virtual input device " + mConfig.getInputDeviceName()); try { diff --git a/core/java/android/hardware/input/VirtualInputDeviceConfig.java b/core/java/android/hardware/input/VirtualInputDeviceConfig.java index e8ef8cd11585bc28ff6dd813993b21b73545b166..3b74d7f5253d6085a6ed4e46308517ac8984135a 100644 --- a/core/java/android/hardware/input/VirtualInputDeviceConfig.java +++ b/core/java/android/hardware/input/VirtualInputDeviceConfig.java @@ -163,7 +163,6 @@ public abstract class VirtualInputDeviceConfig { return self(); } - /** * Sets the product id of the device, uniquely identifying the device within the address * space of a given vendor, identified by the device's vendor id. @@ -179,6 +178,10 @@ public abstract class VirtualInputDeviceConfig { * *

      The input device is restricted to the display with the given ID and may not send * events to any other display.

      + *

      The corresponding display must be trusted or mirror display.

      + * + * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED + * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR */ @NonNull public T setAssociatedDisplayId(int displayId) { diff --git a/core/java/android/hardware/input/VirtualKeyboard.java b/core/java/android/hardware/input/VirtualKeyboard.java index 6a7d19535ac9b8cf736b641d8a7964dfdc5e6992..9664004d4bb7e2f5dcd7c74da8a0decbd7279cf3 100644 --- a/core/java/android/hardware/input/VirtualKeyboard.java +++ b/core/java/android/hardware/input/VirtualKeyboard.java @@ -17,7 +17,6 @@ package android.hardware.input; import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -31,8 +30,8 @@ import android.view.KeyEvent; * A virtual keyboard representing a key input mechanism on a remote device, such as a built-in * keyboard on a laptop, a software keyboard on a tablet, or a keypad on a TV remote control. * - * This registers an InputDevice that is interpreted like a physically-connected device and - * dispatches received events to it. + *

      This registers an InputDevice that is interpreted like a physically-connected device and + * dispatches received events to it.

      * * @hide */ @@ -52,7 +51,6 @@ public class VirtualKeyboard extends VirtualInputDevice { * * @param event the event to send */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendKeyEvent(@NonNull VirtualKeyEvent event) { try { if (mUnsupportedKeyCode == event.getKeyCode()) { diff --git a/core/java/android/hardware/input/VirtualMouse.java b/core/java/android/hardware/input/VirtualMouse.java index fb0f7004927346902ddbf60a5a48ada6d3d7ea01..f2d113c841dc645fae48b313bfed144720670eed 100644 --- a/core/java/android/hardware/input/VirtualMouse.java +++ b/core/java/android/hardware/input/VirtualMouse.java @@ -17,7 +17,6 @@ package android.hardware.input; import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.companion.virtual.IVirtualDevice; import android.graphics.PointF; @@ -30,8 +29,8 @@ import android.view.MotionEvent; * A virtual mouse representing a relative input mechanism on a remote device, such as a mouse or * trackpad. * - * This registers an InputDevice that is interpreted like a physically-connected device and - * dispatches received events to it. + *

      This registers an InputDevice that is interpreted like a physically-connected device and + * dispatches received events to it.

      * * @hide */ @@ -50,7 +49,6 @@ public class VirtualMouse extends VirtualInputDevice { * @throws IllegalStateException if the display this mouse is associated with is not currently * targeted */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull VirtualMouseButtonEvent event) { try { if (!mVirtualDevice.sendButtonEvent(mToken, event)) { @@ -70,7 +68,6 @@ public class VirtualMouse extends VirtualInputDevice { * @throws IllegalStateException if the display this mouse is associated with is not currently * targeted */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull VirtualMouseScrollEvent event) { try { if (!mVirtualDevice.sendScrollEvent(mToken, event)) { @@ -89,7 +86,6 @@ public class VirtualMouse extends VirtualInputDevice { * @throws IllegalStateException if the display this mouse is associated with is not currently * targeted */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendRelativeEvent(@NonNull VirtualMouseRelativeEvent event) { try { if (!mVirtualDevice.sendRelativeEvent(mToken, event)) { @@ -108,7 +104,6 @@ public class VirtualMouse extends VirtualInputDevice { * @throws IllegalStateException if the display this mouse is associated with is not currently * targeted */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public @NonNull PointF getCursorPosition() { try { return mVirtualDevice.getCursorPosition(mToken); diff --git a/core/java/android/hardware/input/VirtualNavigationTouchpad.java b/core/java/android/hardware/input/VirtualNavigationTouchpad.java index 3dbb38568f68dd570bdf3540eb2e9d4444009b17..94e2151160db4e9f5f5cc6da35eea3ff6f39dc6a 100644 --- a/core/java/android/hardware/input/VirtualNavigationTouchpad.java +++ b/core/java/android/hardware/input/VirtualNavigationTouchpad.java @@ -17,7 +17,6 @@ package android.hardware.input; import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.companion.virtual.IVirtualDevice; import android.os.IBinder; @@ -51,7 +50,6 @@ public class VirtualNavigationTouchpad extends VirtualInputDevice { * * @param event the event to send */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendTouchEvent(@NonNull VirtualTouchEvent event) { try { if (!mVirtualDevice.sendTouchEvent(mToken, event)) { diff --git a/core/java/android/hardware/input/VirtualRotaryEncoder.java b/core/java/android/hardware/input/VirtualRotaryEncoder.java index bc131df9d111214b84c022f3bc73d081090725ba..47c92c8e7cbd1d28616182551b9d98de90ab0558 100644 --- a/core/java/android/hardware/input/VirtualRotaryEncoder.java +++ b/core/java/android/hardware/input/VirtualRotaryEncoder.java @@ -18,7 +18,6 @@ package android.hardware.input; import android.annotation.FlaggedApi; import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.companion.virtual.IVirtualDevice; import android.companion.virtualdevice.flags.Flags; @@ -48,7 +47,6 @@ public class VirtualRotaryEncoder extends VirtualInputDevice { * * @param event the event to send */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull VirtualRotaryEncoderScrollEvent event) { try { if (!mVirtualDevice.sendRotaryEncoderScrollEvent(mToken, event)) { diff --git a/core/java/android/hardware/input/VirtualStylus.java b/core/java/android/hardware/input/VirtualStylus.java index c763f7406f3a05c4d5c8acd1fcb03d575cd4316f..4b79bc482c7be2230632ab633cf42b8018063782 100644 --- a/core/java/android/hardware/input/VirtualStylus.java +++ b/core/java/android/hardware/input/VirtualStylus.java @@ -18,7 +18,6 @@ package android.hardware.input; import android.annotation.FlaggedApi; import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.flags.Flags; @@ -30,8 +29,8 @@ import android.util.Log; * A virtual stylus which can be used to inject input into the framework that represents a stylus * on a remote device. * - * This registers an {@link android.view.InputDevice} that is interpreted like a - * physically-connected device and dispatches received events to it. + *

      This registers an {@link android.view.InputDevice} that is interpreted like a + * physically-connected device and dispatches received events to it.

      * * @hide */ @@ -49,7 +48,6 @@ public class VirtualStylus extends VirtualInputDevice { * * @param event the event to send */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendMotionEvent(@NonNull VirtualStylusMotionEvent event) { try { if (!mVirtualDevice.sendStylusMotionEvent(mToken, event)) { @@ -66,7 +64,6 @@ public class VirtualStylus extends VirtualInputDevice { * * @param event the event to send */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull VirtualStylusButtonEvent event) { try { if (!mVirtualDevice.sendStylusButtonEvent(mToken, event)) { diff --git a/core/java/android/hardware/input/VirtualTouchscreen.java b/core/java/android/hardware/input/VirtualTouchscreen.java index 2c800aadef373a0b09643d88742ae1cb19336ad1..d0537f05bb383624334d679abd95a4a97e864f7f 100644 --- a/core/java/android/hardware/input/VirtualTouchscreen.java +++ b/core/java/android/hardware/input/VirtualTouchscreen.java @@ -17,7 +17,6 @@ package android.hardware.input; import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.companion.virtual.IVirtualDevice; import android.os.IBinder; @@ -27,8 +26,8 @@ import android.util.Log; /** * A virtual touchscreen representing a touch-based display input mechanism on a remote device. * - * This registers an InputDevice that is interpreted like a physically-connected device and - * dispatches received events to it. + *

      This registers an InputDevice that is interpreted like a physically-connected device and + * dispatches received events to it.

      * * @hide */ @@ -45,7 +44,6 @@ public class VirtualTouchscreen extends VirtualInputDevice { * * @param event the event to send */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendTouchEvent(@NonNull VirtualTouchEvent event) { try { if (!mVirtualDevice.sendTouchEvent(mToken, event)) { diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 5f62b8be45a30a90f62b458ae3955e60bd5b7970..e85e58039828fd2d10d8fc15ca9392680c492a22 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -42,7 +42,9 @@ interface IPowerManager void updateWakeLockWorkSource(IBinder lock, in WorkSource ws, String historyTag); void updateWakeLockCallback(IBinder lock, IWakeLockCallback callback); + @UnsupportedAppUsage boolean isWakeLockLevelSupported(int level); + boolean isWakeLockLevelSupportedWithDisplayId(int level, int displayId); void userActivity(int displayId, long time, int event, int flags); void wakeUp(long time, int reason, String details, String opPackageName); diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index 7d3076d6611f7a1013850656c84c075f3a496ef0..a1b75034442e2396b7089e726fff601309db6d8e 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -115,3 +115,10 @@ per-file OomKillRecord.java = file:/MEMORY_OWNERS # MessageQueue per-file MessageQueue.java = mfasheh@google.com, shayba@google.com per-file Message.java = mfasheh@google.com, shayba@google.com + +# Stats +per-file IStatsBootstrapAtomService.aidl = file:/services/core/java/com/android/server/stats/OWNERS +per-file StatsBootstrapAtom.aidl = file:/services/core/java/com/android/server/stats/OWNERS +per-file StatsBootstrapAtomValue.aidl = file:/services/core/java/com/android/server/stats/OWNERS +per-file StatsBootstrapAtomService.java = file:/services/core/java/com/android/server/stats/OWNERS +per-file StatsServiceManager.java = file:/services/core/java/com/android/server/stats/OWNERS diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index b9bae5b9f73781c5bf4e0eafa34cc78f3a77f361..32db3bea7686f48699879ad948317c4f8bedb785 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1344,6 +1344,9 @@ public final class PowerManager { * @see #ON_AFTER_RELEASE */ public WakeLock newWakeLock(int levelAndFlags, String tag) { + if (android.companion.virtualdevice.flags.Flags.displayPowerManagerApis()) { + return newWakeLock(levelAndFlags, tag, mContext.getDisplayId()); + } validateWakeLockParameters(levelAndFlags, tag); return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName(), Display.INVALID_DISPLAY); @@ -1734,6 +1737,10 @@ public final class PowerManager { */ public boolean isWakeLockLevelSupported(int level) { try { + if (android.companion.virtualdevice.flags.Flags.displayPowerManagerApis()) { + return mService.isWakeLockLevelSupportedWithDisplayId( + level, mContext.getDisplayId()); + } return mService.isWakeLockLevelSupported(level); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1797,6 +1804,9 @@ public final class PowerManager { * @see android.content.Intent#ACTION_SCREEN_OFF */ public boolean isInteractive() { + if (android.companion.virtualdevice.flags.Flags.displayPowerManagerApis()) { + return isInteractive(mContext.getDisplayId()); + } return mInteractiveCache.query(null); } diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java index 769cbdd9886da48bcbc35cd76d9c00e022bd9925..f82c8221e4f9c4707ab6fefd59e4f7b886036efe 100644 --- a/core/java/android/os/RemoteCallbackList.java +++ b/core/java/android/os/RemoteCallbackList.java @@ -16,11 +16,18 @@ package android.os; +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.util.ArrayMap; import android.util.Slog; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -30,7 +37,7 @@ import java.util.function.Consumer; * {@link android.app.Service} to its clients. In particular, this: * *
        - *
      • Keeps track of a set of registered {@link IInterface} callbacks, + *
      • Keeps track of a set of registered {@link IInterface} objects, * taking care to identify them through their underlying unique {@link IBinder} * (by calling {@link IInterface#asBinder IInterface.asBinder()}. *
      • Attaches a {@link IBinder.DeathRecipient IBinder.DeathRecipient} to @@ -47,7 +54,7 @@ import java.util.function.Consumer; * the registered clients, use {@link #beginBroadcast}, * {@link #getBroadcastItem}, and {@link #finishBroadcast}. * - *

        If a registered callback's process goes away, this class will take + *

        If a registered interface's process goes away, this class will take * care of automatically removing it from the list. If you want to do * additional work in this situation, you can create a subclass that * implements the {@link #onCallbackDied} method. @@ -56,78 +63,310 @@ import java.util.function.Consumer; public class RemoteCallbackList { private static final String TAG = "RemoteCallbackList"; + private static final int DEFAULT_MAX_QUEUE_SIZE = 1000; + + + /** + * @hide + */ + @IntDef(prefix = {"FROZEN_CALLEE_POLICY_"}, value = { + FROZEN_CALLEE_POLICY_UNSET, + FROZEN_CALLEE_POLICY_ENQUEUE_ALL, + FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT, + FROZEN_CALLEE_POLICY_DROP, + }) + @Retention(RetentionPolicy.SOURCE) + @interface FrozenCalleePolicy { + } + + /** + * Callbacks are invoked immediately regardless of the frozen state of the target process. + * + * Not recommended. Only exists for backward-compatibility. This represents the behavior up to + * SDK 35. Starting with SDK 36, clients should set a policy to govern callback invocations when + * recipients are frozen. + */ + @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) + public static final int FROZEN_CALLEE_POLICY_UNSET = 0; + + /** + * When the callback recipient's process is frozen, callbacks are enqueued so they're invoked + * after the recipient is unfrozen. + * + * This is commonly used when the recipient wants to receive all callbacks without losing any + * history, e.g. the recipient maintains a running count of events that occurred. + * + * Queued callbacks are invoked in the order they were originally broadcasted. + */ + @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) + public static final int FROZEN_CALLEE_POLICY_ENQUEUE_ALL = 1; + + /** + * When the callback recipient's process is frozen, only the most recent callback is enqueued, + * which is later invoked after the recipient is unfrozen. + * + * This can be used when only the most recent state matters, for instance when clients are + * listening to screen brightness changes. + */ + @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) + public static final int FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT = 2; + + /** + * When the callback recipient's process is frozen, callbacks are suppressed as if they never + * happened. + * + * This could be useful in the case where the recipient wishes to react to callbacks only when + * they occur while the recipient is not frozen. For example, certain network events are only + * worth responding to if the response can be immediate. Another example is recipients having + * another way of getting the latest state once it's unfrozen. Therefore there is no need to + * save callbacks that happened while the recipient was frozen. + */ + @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) + public static final int FROZEN_CALLEE_POLICY_DROP = 3; + @UnsupportedAppUsage - /*package*/ ArrayMap mCallbacks - = new ArrayMap(); + /*package*/ ArrayMap mInterfaces = new ArrayMap(); private Object[] mActiveBroadcast; private int mBroadcastCount = -1; private boolean mKilled = false; private StringBuilder mRecentCallers; - private final class Callback implements IBinder.DeathRecipient { - final E mCallback; + private final @FrozenCalleePolicy int mFrozenCalleePolicy; + private final int mMaxQueueSize; + + private final class Interface implements IBinder.DeathRecipient, + IBinder.FrozenStateChangeCallback { + final IBinder mBinder; + final E mInterface; final Object mCookie; + final Queue> mCallbackQueue; + int mCurrentState = IBinder.FrozenStateChangeCallback.STATE_UNFROZEN; - Callback(E callback, Object cookie) { - mCallback = callback; + Interface(E callbackInterface, Object cookie) { + mBinder = callbackInterface.asBinder(); + mInterface = callbackInterface; mCookie = cookie; + mCallbackQueue = mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_ENQUEUE_ALL + || mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT + ? new ConcurrentLinkedQueue<>() : null; + } + + @Override + public synchronized void onFrozenStateChanged(@NonNull IBinder who, int state) { + if (state == STATE_UNFROZEN && mCallbackQueue != null) { + while (!mCallbackQueue.isEmpty()) { + Consumer callback = mCallbackQueue.poll(); + callback.accept(mInterface); + } + } + mCurrentState = state; + } + + void addCallback(@NonNull Consumer callback) { + if (mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_UNSET) { + callback.accept(mInterface); + return; + } + synchronized (this) { + if (mCurrentState == STATE_UNFROZEN) { + callback.accept(mInterface); + return; + } + switch (mFrozenCalleePolicy) { + case FROZEN_CALLEE_POLICY_ENQUEUE_ALL: + if (mCallbackQueue.size() >= mMaxQueueSize) { + mCallbackQueue.poll(); + } + mCallbackQueue.offer(callback); + break; + case FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT: + mCallbackQueue.clear(); + mCallbackQueue.offer(callback); + break; + case FROZEN_CALLEE_POLICY_DROP: + // Do nothing. Just ignore the callback. + break; + case FROZEN_CALLEE_POLICY_UNSET: + // Do nothing. Should have returned at the start of the method. + break; + } + } + } + + public void maybeSubscribeToFrozenCallback() throws RemoteException { + if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) { + mBinder.addFrozenStateChangeCallback(this); + } + } + + public void maybeUnsubscribeFromFrozenCallback() { + if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) { + mBinder.removeFrozenStateChangeCallback(this); + } } public void binderDied() { - synchronized (mCallbacks) { - mCallbacks.remove(mCallback.asBinder()); + synchronized (mInterfaces) { + mInterfaces.remove(mBinder); + maybeUnsubscribeFromFrozenCallback(); + } + onCallbackDied(mInterface, mCookie); + } + } + + /** + * Builder for {@link RemoteCallbackList}. + * + * @param The remote callback interface type. + */ + @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) + public static final class Builder { + private @FrozenCalleePolicy int mFrozenCalleePolicy; + private int mMaxQueueSize = DEFAULT_MAX_QUEUE_SIZE; + + /** + * Creates a Builder for {@link RemoteCallbackList}. + * + * @param frozenCalleePolicy When the callback recipient's process is frozen, this parameter + * specifies when/whether callbacks are invoked. It's important to choose a strategy that's + * right for the use case. Leaving the policy unset with {@link #FROZEN_CALLEE_POLICY_UNSET} + * is not recommended as it allows callbacks to be invoked while the recipient is frozen. + */ + public Builder(@FrozenCalleePolicy int frozenCalleePolicy) { + mFrozenCalleePolicy = frozenCalleePolicy; + } + + /** + * Sets the max queue size. + * + * @param maxQueueSize The max size limit on the queue that stores callbacks added when the + * recipient's process is frozen. Once the limit is reached, the oldest callback is dropped + * to keep the size under the limit. Should only be called for + * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}. + * + * @return This builder. + * @throws IllegalArgumentException if the maxQueueSize is not positive. + * @throws UnsupportedOperationException if frozenCalleePolicy is not + * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}. + */ + public @NonNull Builder setMaxQueueSize(int maxQueueSize) { + if (maxQueueSize <= 0) { + throw new IllegalArgumentException("maxQueueSize must be positive"); + } + if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_ENQUEUE_ALL) { + throw new UnsupportedOperationException( + "setMaxQueueSize can only be called for FROZEN_CALLEE_POLICY_ENQUEUE_ALL"); } - onCallbackDied(mCallback, mCookie); + mMaxQueueSize = maxQueueSize; + return this; + } + + /** + * Builds and returns a {@link RemoteCallbackList}. + * + * @return The built {@link RemoteCallbackList} object. + */ + public @NonNull RemoteCallbackList build() { + return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize); } } + /** + * Returns the frozen callee policy. + * + * @return The frozen callee policy. + */ + @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) + public @FrozenCalleePolicy int getFrozenCalleePolicy() { + return mFrozenCalleePolicy; + } + + /** + * Returns the max queue size. + * + * @return The max queue size. + */ + @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) + public int getMaxQueueSize() { + return mMaxQueueSize; + } + + /** + * Creates a RemoteCallbackList with {@link #FROZEN_CALLEE_POLICY_UNSET}. This is equivalent to + *

        +     * new RemoteCallbackList.Build(RemoteCallbackList.FROZEN_CALLEE_POLICY_UNSET).build()
        +     * 
        + */ + public RemoteCallbackList() { + this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE); + } + + /** + * Creates a RemoteCallbackList with the specified frozen callee policy. + * + * @param frozenCalleePolicy When the callback recipient's process is frozen, this parameter + * specifies when/whether callbacks are invoked. It's important to choose a strategy that's + * right for the use case. Leaving the policy unset with {@link #FROZEN_CALLEE_POLICY_UNSET} + * is not recommended as it allows callbacks to be invoked while the recipient is frozen. + * + * @param maxQueueSize The max size limit on the queue that stores callbacks added when the + * recipient's process is frozen. Once the limit is reached, the oldest callbacks would be + * dropped to keep the size under limit. Ignored except for + * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}. + */ + private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize) { + mFrozenCalleePolicy = frozenCalleePolicy; + mMaxQueueSize = maxQueueSize; + } + /** * Simple version of {@link RemoteCallbackList#register(E, Object)} * that does not take a cookie object. */ - public boolean register(E callback) { - return register(callback, null); + public boolean register(E callbackInterface) { + return register(callbackInterface, null); } /** - * Add a new callback to the list. This callback will remain in the list + * Add a new interface to the list. This interface will remain in the list * until a corresponding call to {@link #unregister} or its hosting process - * goes away. If the callback was already registered (determined by - * checking to see if the {@link IInterface#asBinder callback.asBinder()} - * object is already in the list), then it will be replaced with the new callback. + * goes away. If the interface was already registered (determined by + * checking to see if the {@link IInterface#asBinder callbackInterface.asBinder()} + * object is already in the list), then it will be replaced with the new interface. * Registrations are not counted; a single call to {@link #unregister} - * will remove a callback after any number calls to register it. + * will remove an interface after any number calls to register it. * - * @param callback The callback interface to be added to the list. Must + * @param callbackInterface The callback interface to be added to the list. Must * not be null -- passing null here will cause a NullPointerException. * Most services will want to check for null before calling this with * an object given from a client, so that clients can't crash the * service with bad data. * * @param cookie Optional additional data to be associated with this - * callback. + * interface. * - * @return Returns true if the callback was successfully added to the list. + * @return Returns true if the interface was successfully added to the list. * Returns false if it was not added, either because {@link #kill} had - * previously been called or the callback's process has gone away. + * previously been called or the interface's process has gone away. * * @see #unregister * @see #kill * @see #onCallbackDied */ - public boolean register(E callback, Object cookie) { - synchronized (mCallbacks) { + public boolean register(E callbackInterface, Object cookie) { + synchronized (mInterfaces) { if (mKilled) { return false; } // Flag unusual case that could be caused by a leak. b/36778087 - logExcessiveCallbacks(); - IBinder binder = callback.asBinder(); + logExcessiveInterfaces(); + IBinder binder = callbackInterface.asBinder(); try { - Callback cb = new Callback(callback, cookie); - unregister(callback); - binder.linkToDeath(cb, 0); - mCallbacks.put(binder, cb); + Interface i = new Interface(callbackInterface, cookie); + unregister(callbackInterface); + binder.linkToDeath(i, 0); + i.maybeSubscribeToFrozenCallback(); + mInterfaces.put(binder, i); return true; } catch (RemoteException e) { return false; @@ -136,27 +375,28 @@ public class RemoteCallbackList { } /** - * Remove from the list a callback that was previously added with + * Remove from the list an interface that was previously added with * {@link #register}. This uses the - * {@link IInterface#asBinder callback.asBinder()} object to correctly + * {@link IInterface#asBinder callbackInterface.asBinder()} object to correctly * find the previous registration. * Registrations are not counted; a single unregister call will remove - * a callback after any number calls to {@link #register} for it. + * an interface after any number calls to {@link #register} for it. * - * @param callback The callback to be removed from the list. Passing + * @param callbackInterface The interface to be removed from the list. Passing * null here will cause a NullPointerException, so you will generally want * to check for null before calling. * - * @return Returns true if the callback was found and unregistered. Returns - * false if the given callback was not found on the list. + * @return Returns true if the interface was found and unregistered. Returns + * false if the given interface was not found on the list. * * @see #register */ - public boolean unregister(E callback) { - synchronized (mCallbacks) { - Callback cb = mCallbacks.remove(callback.asBinder()); - if (cb != null) { - cb.mCallback.asBinder().unlinkToDeath(cb, 0); + public boolean unregister(E callbackInterface) { + synchronized (mInterfaces) { + Interface i = mInterfaces.remove(callbackInterface.asBinder()); + if (i != null) { + i.mInterface.asBinder().unlinkToDeath(i, 0); + i.maybeUnsubscribeFromFrozenCallback(); return true; } return false; @@ -164,20 +404,21 @@ public class RemoteCallbackList { } /** - * Disable this callback list. All registered callbacks are unregistered, + * Disable this interface list. All registered interfaces are unregistered, * and the list is disabled so that future calls to {@link #register} will * fail. This should be used when a Service is stopping, to prevent clients - * from registering callbacks after it is stopped. + * from registering interfaces after it is stopped. * * @see #register */ public void kill() { - synchronized (mCallbacks) { - for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) { - Callback cb = mCallbacks.valueAt(cbi); - cb.mCallback.asBinder().unlinkToDeath(cb, 0); + synchronized (mInterfaces) { + for (int cbi = mInterfaces.size() - 1; cbi >= 0; cbi--) { + Interface i = mInterfaces.valueAt(cbi); + i.mInterface.asBinder().unlinkToDeath(i, 0); + i.maybeUnsubscribeFromFrozenCallback(); } - mCallbacks.clear(); + mInterfaces.clear(); mKilled = true; } } @@ -186,15 +427,15 @@ public class RemoteCallbackList { * Old version of {@link #onCallbackDied(E, Object)} that * does not provide a cookie. */ - public void onCallbackDied(E callback) { + public void onCallbackDied(E callbackInterface) { } /** - * Called when the process hosting a callback in the list has gone away. + * Called when the process hosting an interface in the list has gone away. * The default implementation calls {@link #onCallbackDied(E)} * for backwards compatibility. * - * @param callback The callback whose process has died. Note that, since + * @param callbackInterface The interface whose process has died. Note that, since * its process has died, you can not make any calls on to this interface. * You can, however, retrieve its IBinder and compare it with another * IBinder to see if it is the same object. @@ -203,13 +444,15 @@ public class RemoteCallbackList { * * @see #register */ - public void onCallbackDied(E callback, Object cookie) { - onCallbackDied(callback); + public void onCallbackDied(E callbackInterface, Object cookie) { + onCallbackDied(callbackInterface); } /** - * Prepare to start making calls to the currently registered callbacks. - * This creates a copy of the callback list, which you can retrieve items + * Use {@link #broadcast(Consumer)} instead to ensure proper handling of frozen processes. + * + * Prepare to start making calls to the currently registered interfaces. + * This creates a copy of the interface list, which you can retrieve items * from using {@link #getBroadcastItem}. Note that only one broadcast can * be active at a time, so you must be sure to always call this from the * same thread (usually by scheduling with {@link Handler}) or @@ -219,44 +462,56 @@ public class RemoteCallbackList { *

        A typical loop delivering a broadcast looks like this: * *

        -     * int i = callbacks.beginBroadcast();
        +     * int i = interfaces.beginBroadcast();
              * while (i > 0) {
              *     i--;
              *     try {
        -     *         callbacks.getBroadcastItem(i).somethingHappened();
        +     *         interfaces.getBroadcastItem(i).somethingHappened();
              *     } catch (RemoteException e) {
              *         // The RemoteCallbackList will take care of removing
              *         // the dead object for us.
              *     }
              * }
        -     * callbacks.finishBroadcast();
        + * interfaces.finishBroadcast(); * - * @return Returns the number of callbacks in the broadcast, to be used + * Note that this method is only supported for {@link #FROZEN_CALLEE_POLICY_UNSET}. For other + * policies use {@link #broadcast(Consumer)} instead. + * + * @return Returns the number of interfaces in the broadcast, to be used * with {@link #getBroadcastItem} to determine the range of indices you * can supply. * + * @throws UnsupportedOperationException if an frozen callee policy is set. + * * @see #getBroadcastItem * @see #finishBroadcast */ public int beginBroadcast() { - synchronized (mCallbacks) { + if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) { + throw new UnsupportedOperationException(); + } + return beginBroadcastInternal(); + } + + private int beginBroadcastInternal() { + synchronized (mInterfaces) { if (mBroadcastCount > 0) { throw new IllegalStateException( "beginBroadcast() called while already in a broadcast"); } - final int N = mBroadcastCount = mCallbacks.size(); - if (N <= 0) { + final int n = mBroadcastCount = mInterfaces.size(); + if (n <= 0) { return 0; } Object[] active = mActiveBroadcast; - if (active == null || active.length < N) { - mActiveBroadcast = active = new Object[N]; + if (active == null || active.length < n) { + mActiveBroadcast = active = new Object[n]; } - for (int i=0; i { * calling {@link #finishBroadcast}. * *

        Note that it is possible for the process of one of the returned - * callbacks to go away before you call it, so you will need to catch + * interfaces to go away before you call it, so you will need to catch * {@link RemoteException} when calling on to the returned object. - * The callback list itself, however, will take care of unregistering + * The interface list itself, however, will take care of unregistering * these objects once it detects that it is no longer valid, so you can * handle such an exception by simply ignoring it. * - * @param index Which of the registered callbacks you would like to + * @param index Which of the registered interfaces you would like to * retrieve. Ranges from 0 to {@link #beginBroadcast}-1, inclusive. * - * @return Returns the callback interface that you can call. This will - * always be non-null. + * @return Returns the interface that you can call. This will always be non-null. * * @see #beginBroadcast */ public E getBroadcastItem(int index) { - return ((Callback)mActiveBroadcast[index]).mCallback; + return ((Interface) mActiveBroadcast[index]).mInterface; } - + /** * Retrieve the cookie associated with the item * returned by {@link #getBroadcastItem(int)}. @@ -292,7 +546,7 @@ public class RemoteCallbackList { * @see #getBroadcastItem */ public Object getBroadcastCookie(int index) { - return ((Callback)mActiveBroadcast[index]).mCookie; + return ((Interface) mActiveBroadcast[index]).mCookie; } /** @@ -303,7 +557,7 @@ public class RemoteCallbackList { * @see #beginBroadcast */ public void finishBroadcast() { - synchronized (mCallbacks) { + synchronized (mInterfaces) { if (mBroadcastCount < 0) { throw new IllegalStateException( "finishBroadcast() called outside of a broadcast"); @@ -322,16 +576,18 @@ public class RemoteCallbackList { } /** - * Performs {@code action} on each callback, calling - * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping + * Performs {@code callback} on each registered interface. * - * @hide + * This is equivalent to #beginBroadcast, followed by iterating over the items using + * #getBroadcastItem and then @finishBroadcast, except that this method supports + * frozen callee policies. */ - public void broadcast(Consumer action) { - int itemCount = beginBroadcast(); + @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) + public void broadcast(@NonNull Consumer callback) { + int itemCount = beginBroadcastInternal(); try { for (int i = 0; i < itemCount; i++) { - action.accept(getBroadcastItem(i)); + ((Interface) mActiveBroadcast[i]).addCallback(callback); } } finally { finishBroadcast(); @@ -339,16 +595,16 @@ public class RemoteCallbackList { } /** - * Performs {@code action} for each cookie associated with a callback, calling + * Performs {@code callback} for each cookie associated with an interface, calling * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping * * @hide */ - public void broadcastForEachCookie(Consumer action) { + public void broadcastForEachCookie(Consumer callback) { int itemCount = beginBroadcast(); try { for (int i = 0; i < itemCount; i++) { - action.accept((C) getBroadcastCookie(i)); + callback.accept((C) getBroadcastCookie(i)); } } finally { finishBroadcast(); @@ -356,16 +612,16 @@ public class RemoteCallbackList { } /** - * Performs {@code action} on each callback and associated cookie, calling {@link + * Performs {@code callback} on each interface and associated cookie, calling {@link * #beginBroadcast()}/{@link #finishBroadcast()} before/after looping. * * @hide */ - public void broadcast(BiConsumer action) { + public void broadcast(BiConsumer callback) { int itemCount = beginBroadcast(); try { for (int i = 0; i < itemCount; i++) { - action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i)); + callback.accept(getBroadcastItem(i), (C) getBroadcastCookie(i)); } } finally { finishBroadcast(); @@ -373,10 +629,10 @@ public class RemoteCallbackList { } /** - * Returns the number of registered callbacks. Note that the number of registered - * callbacks may differ from the value returned by {@link #beginBroadcast()} since - * the former returns the number of callbacks registered at the time of the call - * and the second the number of callback to which the broadcast will be delivered. + * Returns the number of registered interfaces. Note that the number of registered + * interfaces may differ from the value returned by {@link #beginBroadcast()} since + * the former returns the number of interfaces registered at the time of the call + * and the second the number of interfaces to which the broadcast will be delivered. *

        * This function is useful to decide whether to schedule a broadcast if this * requires doing some work which otherwise would not be performed. @@ -385,39 +641,39 @@ public class RemoteCallbackList { * @return The size. */ public int getRegisteredCallbackCount() { - synchronized (mCallbacks) { + synchronized (mInterfaces) { if (mKilled) { return 0; } - return mCallbacks.size(); + return mInterfaces.size(); } } /** - * Return a currently registered callback. Note that this is + * Return a currently registered interface. Note that this is * not the same as {@link #getBroadcastItem} and should not be used - * interchangeably with it. This method returns the registered callback at the given + * interchangeably with it. This method returns the registered interface at the given * index, not the current broadcast state. This means that it is not itself thread-safe: * any call to {@link #register} or {@link #unregister} will change these indices, so you * must do your own thread safety between these to protect from such changes. * - * @param index Index of which callback registration to return, from 0 to + * @param index Index of which interface registration to return, from 0 to * {@link #getRegisteredCallbackCount()} - 1. * - * @return Returns whatever callback is associated with this index, or null if + * @return Returns whatever interface is associated with this index, or null if * {@link #kill()} has been called. */ public E getRegisteredCallbackItem(int index) { - synchronized (mCallbacks) { + synchronized (mInterfaces) { if (mKilled) { return null; } - return mCallbacks.valueAt(index).mCallback; + return mInterfaces.valueAt(index).mInterface; } } /** - * Return any cookie associated with a currently registered callback. Note that this is + * Return any cookie associated with a currently registered interface. Note that this is * not the same as {@link #getBroadcastCookie} and should not be used * interchangeably with it. This method returns the current cookie registered at the given * index, not the current broadcast state. This means that it is not itself thread-safe: @@ -431,25 +687,25 @@ public class RemoteCallbackList { * {@link #kill()} has been called. */ public Object getRegisteredCallbackCookie(int index) { - synchronized (mCallbacks) { + synchronized (mInterfaces) { if (mKilled) { return null; } - return mCallbacks.valueAt(index).mCookie; + return mInterfaces.valueAt(index).mCookie; } } /** @hide */ public void dump(PrintWriter pw, String prefix) { - synchronized (mCallbacks) { - pw.print(prefix); pw.print("callbacks: "); pw.println(mCallbacks.size()); + synchronized (mInterfaces) { + pw.print(prefix); pw.print("callbacks: "); pw.println(mInterfaces.size()); pw.print(prefix); pw.print("killed: "); pw.println(mKilled); pw.print(prefix); pw.print("broadcasts count: "); pw.println(mBroadcastCount); } } - private void logExcessiveCallbacks() { - final long size = mCallbacks.size(); + private void logExcessiveInterfaces() { + final long size = mInterfaces.size(); final long TOO_MANY = 3000; final long MAX_CHARS = 1000; if (size >= TOO_MANY) { diff --git a/core/java/android/os/StatsBootstrapAtomValue.aidl b/core/java/android/os/StatsBootstrapAtomValue.aidl index a90dfa404ee96fe6832500e53b4f909aaabe007c..b59bc062648f9e26fa640fc25153a735771fee50 100644 --- a/core/java/android/os/StatsBootstrapAtomValue.aidl +++ b/core/java/android/os/StatsBootstrapAtomValue.aidl @@ -26,4 +26,5 @@ union StatsBootstrapAtomValue { float floatValue; String stringValue; byte[] bytesValue; + String[] stringArrayValue; } \ No newline at end of file diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java index dfc591b322df13b6e81df3a2b19abe1c5b6aa8a1..8ef62e3d46281726abab57d606f1f8ffaf48873f 100644 --- a/core/java/android/os/SystemClock.java +++ b/core/java/android/os/SystemClock.java @@ -62,7 +62,7 @@ import java.time.ZoneOffset; * sleep (CPU off, display dark, device waiting for external input), * but is not affected by clock scaling, idle, or other power saving * mechanisms. This is the basis for most interval timing - * such as {@link Thread#sleep(long) Thread.sleep(millls)}, + * such as {@link Thread#sleep(long) Thread.sleep(millis)}, * {@link Object#wait(long) Object.wait(millis)}, and * {@link System#nanoTime System.nanoTime()}. This clock is guaranteed * to be monotonic, and is suitable for interval timing when the diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 3ae951170759ca077b2aaf96899ed55fc7874671..bd3da0d56cbdbb8cb90619206a6a68e9a5fac75e 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -66,6 +66,7 @@ import android.provider.Settings; import android.util.AndroidException; import android.util.ArraySet; import android.util.Log; +import android.util.Pair; import android.view.WindowManager.LayoutParams; import com.android.internal.R; @@ -3908,11 +3909,57 @@ public class UserManager { android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) public @NonNull UserProperties getUserProperties(@NonNull UserHandle userHandle) { final int userId = userHandle.getIdentifier(); - // Avoid calling into system server for invalid user ids. - if (android.multiuser.Flags.fixGetUserPropertyCache() && userId < 0) { + + if (userId < 0 && android.multiuser.Flags.fixGetUserPropertyCache()) { + // Avoid calling into system server for invalid user ids. throw new IllegalArgumentException("Cannot access properties for user " + userId); } - return mUserPropertiesCache.query(userId); + + if (!android.multiuser.Flags.cacheUserPropertiesCorrectlyReadOnly() || userId < 0) { + // This is the historical code path, when all flags are false. + try { + return mService.getUserPropertiesCopy(userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + final int callingUid = Binder.getCallingUid(); + final int processUid = Process.myUid(); + if (Build.isDebuggable() && callingUid != processUid) { + Log.w(TAG, "Uid " + processUid + " is fetching a copy of UserProperties on" + + " behalf of callingUid " + callingUid + ". Possibly" + + " it should carefully first clearCallingIdentity or perhaps use" + + " UserManagerInternal.getUserProperties() instead?", + new Throwable()); + } + + return getUserPropertiesFromQuery(new QueryUserId(userId)); + } + + /** @hide */ + public static final void invalidateUserPropertiesCache() { + UserManagerCache.invalidateUserPropertiesFromQuery(); + } + + /** + * Cachable version of {@link #getUserProperties(UserHandle)}, caching the UserProperties + * corresponding to the given QueryUserId. The cached copy depends on both the queried userId as + * well as the Binder caller querying it, since the result depends on the caller's permissions. + */ + @CachedProperty() + private @NonNull UserProperties getUserPropertiesFromQuery(QueryUserId query) { + return ((UserManagerCache) mIpcDataCache).getUserPropertiesFromQuery( + (QueryUserId q) -> mService.getUserPropertiesCopy(q.getUserId()), query); + } + + /** Class keeping track of a userId, as well as the callingUid that is asking about it. */ + /** @hide */ + static final class QueryUserId extends Pair { + public QueryUserId(@UserIdInt int userId) { + super(Binder.getCallingUid(), userId); + } + public @UserIdInt int getUserId() { return second; } } /** @@ -6368,8 +6415,12 @@ public class UserManager { Settings.Global.DEVICE_DEMO_MODE, 0) > 0; } - /** @hide */ - public static final void invalidateUserSerialNumberCache() { + + /** + * This method is used to invalidate caches, when user was added or removed. + * @hide + */ + public static final void invalidateCacheOnUserListChange() { UserManagerCache.invalidateUserSerialNumber(); } @@ -6382,7 +6433,7 @@ public class UserManager { * @hide */ @UnsupportedAppUsage - @CachedProperty(modsFlagOnOrNone = {}) + @CachedProperty(modsFlagOnOrNone = {}, api = "user_manager_users") public int getUserSerialNumber(@UserIdInt int userId) { // Read only flag should is to fix early access to this API // cacheUserSerialNumber to be removed after the @@ -6652,34 +6703,6 @@ public class UserManager { PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STATIC_USER_PROPERTIES); } - /* Cache key for UserProperties object. */ - private static final String CACHE_KEY_USER_PROPERTIES = - PropertyInvalidatedCache.createPropertyName( - PropertyInvalidatedCache.MODULE_SYSTEM, "user_properties"); - - // TODO: It would be better to somehow have this as static, so that it can work cross-context. - private final PropertyInvalidatedCache mUserPropertiesCache = - new PropertyInvalidatedCache(16, CACHE_KEY_USER_PROPERTIES) { - @Override - public UserProperties recompute(Integer userId) { - try { - // If the userId doesn't exist, this will throw rather than cache garbage. - return mService.getUserPropertiesCopy(userId); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - @Override - public boolean bypass(Integer query) { - return query < 0; - } - }; - - /** @hide */ - public static final void invalidateUserPropertiesCache() { - PropertyInvalidatedCache.invalidateCache(CACHE_KEY_USER_PROPERTIES); - } - /** * @hide * User that enforces a restriction. diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index 64a2dbcb6a838cb23ea838e2c0228a9fe4d5ca11..ffc58c537f2afb79adea2740b6b24c8dc0f42c3e 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -40,6 +40,7 @@ import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.RampSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationEffectSegment; +import android.os.vibrator.VibratorFrequencyProfileLegacy; import android.util.MathUtils; import com.android.internal.util.Preconditions; @@ -1761,7 +1762,7 @@ public abstract class VibrationEffect implements Parcelable { * new value as fast as possible. * *

        Vibration parameter values will be truncated to conform to the device capabilities - * according to the {@link android.os.vibrator.VibratorFrequencyProfile}. + * according to the {@link VibratorFrequencyProfileLegacy}. * * @param duration The length of time this transition should take. Value must be * non-negative and will be truncated to milliseconds. @@ -1792,7 +1793,7 @@ public abstract class VibrationEffect implements Parcelable { * new values as fast as possible. * *

        Vibration parameters values will be truncated to conform to the device capabilities - * according to the {@link android.os.vibrator.VibratorFrequencyProfile}. + * according to the {@link VibratorFrequencyProfileLegacy}. * * @param duration The length of time this transition should take. Value must be * non-negative and will be truncated to milliseconds. diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index 84325b7c28748d6efa608a1e883329e879399280..c4c4580bf0a829da289cfee322c90ca0680b94de 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -22,6 +22,7 @@ import android.annotation.IntDef; 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; @@ -34,6 +35,7 @@ import android.media.AudioAttributes; import android.os.vibrator.Flags; import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibratorFrequencyProfile; +import android.os.vibrator.VibratorFrequencyProfileLegacy; import android.util.Log; import android.view.HapticFeedbackConstants; @@ -290,12 +292,36 @@ public abstract class Vibrator { * @hide */ @TestApi + @SuppressLint("UnflaggedApi") + @Nullable + public VibratorFrequencyProfileLegacy getFrequencyProfileLegacy() { + VibratorInfo.FrequencyProfileLegacy frequencyProfile = + getInfo().getFrequencyProfileLegacy(); + if (frequencyProfile.isEmpty()) { + return null; + } + return new VibratorFrequencyProfileLegacy(frequencyProfile); + } + + /** + * Gets the profile that describes the vibrator output across the supported frequency range. + * + *

        The profile describes the output acceleration that the device can reach when it + * vibrates at different frequencies. + * + * @return The frequency profile for this vibrator, or null if the vibrator does not have + * frequency control. If this vibrator is a composite of multiple physical devices then this + * will return a profile supported in all devices, or null if the intersection is empty or not + * available. + */ + @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) @Nullable public VibratorFrequencyProfile getFrequencyProfile() { VibratorInfo.FrequencyProfile frequencyProfile = getInfo().getFrequencyProfile(); if (frequencyProfile.isEmpty()) { return null; } + return new VibratorFrequencyProfile(frequencyProfile); } diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java index 5378295e3720befeffb6d26d7dfb65663dc180cc..9419032c46f8b13c42572e34dda8f378c4574968 100644 --- a/core/java/android/os/VibratorInfo.java +++ b/core/java/android/os/VibratorInfo.java @@ -20,8 +20,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.vibrator.Braking; import android.hardware.vibrator.IVibrator; +import android.os.vibrator.Flags; import android.util.IndentingPrintWriter; import android.util.MathUtils; +import android.util.Pair; import android.util.Range; import android.util.SparseBooleanArray; import android.util.SparseIntArray; @@ -30,8 +32,11 @@ import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.TreeMap; /** * A VibratorInfo describes the capabilities of a {@link Vibrator}. @@ -59,6 +64,7 @@ public class VibratorInfo implements Parcelable { private final int mPwlePrimitiveDurationMax; private final int mPwleSizeMax; private final float mQFactor; + private final FrequencyProfileLegacy mFrequencyProfileLegacy; private final FrequencyProfile mFrequencyProfile; private final int mMaxEnvelopeEffectSize; private final int mMinEnvelopeEffectControlPointDurationMillis; @@ -75,6 +81,7 @@ public class VibratorInfo implements Parcelable { mPwlePrimitiveDurationMax = in.readInt(); mPwleSizeMax = in.readInt(); mQFactor = in.readFloat(); + mFrequencyProfileLegacy = FrequencyProfileLegacy.CREATOR.createFromParcel(in); mFrequencyProfile = FrequencyProfile.CREATOR.createFromParcel(in); mMaxEnvelopeEffectSize = in.readInt(); mMinEnvelopeEffectControlPointDurationMillis = in.readInt(); @@ -86,8 +93,8 @@ public class VibratorInfo implements Parcelable { baseVibratorInfo.mSupportedBraking, baseVibratorInfo.mSupportedPrimitives, baseVibratorInfo.mPrimitiveDelayMax, baseVibratorInfo.mCompositionSizeMax, baseVibratorInfo.mPwlePrimitiveDurationMax, baseVibratorInfo.mPwleSizeMax, - baseVibratorInfo.mQFactor, baseVibratorInfo.mFrequencyProfile, - baseVibratorInfo.mMaxEnvelopeEffectSize, + baseVibratorInfo.mQFactor, baseVibratorInfo.mFrequencyProfileLegacy, + baseVibratorInfo.mFrequencyProfile, baseVibratorInfo.mMaxEnvelopeEffectSize, baseVibratorInfo.mMinEnvelopeEffectControlPointDurationMillis, baseVibratorInfo.mMaxEnvelopeEffectControlPointDurationMillis); } @@ -112,18 +119,31 @@ public class VibratorInfo implements Parcelable { * @param pwleSizeMax The maximum number of primitives supported by a PWLE * composition. * @param qFactor The vibrator quality factor. - * @param frequencyProfile The description of the vibrator supported frequencies and max + * @param frequencyProfileLegacy The description of the vibrator supported frequencies and max * amplitude mappings. + * @param frequencyProfile The description of the vibrator supported frequencies and + * output acceleration mappings. + * @param maxEnvelopeEffectSize The maximum number of control points supported for an + * envelope effect. + * @param minEnvelopeEffectControlPointDurationMillis The minimum duration supported + * between two control points within an + * envelope effect. + * @param maxEnvelopeEffectControlPointDurationMillis The maximum duration supported + * between two control points within an + * envelope effect. + * * @hide */ public VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects, @Nullable SparseBooleanArray supportedBraking, @NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax, int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax, - float qFactor, @NonNull FrequencyProfile frequencyProfile, - int maxEnvelopeEffectSize, int minEnvelopeEffectControlPointDurationMillis, + float qFactor, @NonNull FrequencyProfileLegacy frequencyProfileLegacy, + @NonNull FrequencyProfile frequencyProfile, int maxEnvelopeEffectSize, + int minEnvelopeEffectControlPointDurationMillis, int maxEnvelopeEffectControlPointDurationMillis) { Preconditions.checkNotNull(supportedPrimitives); + Preconditions.checkNotNull(frequencyProfileLegacy); Preconditions.checkNotNull(frequencyProfile); mId = id; mCapabilities = capabilities; @@ -135,6 +155,7 @@ public class VibratorInfo implements Parcelable { mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax; mPwleSizeMax = pwleSizeMax; mQFactor = qFactor; + mFrequencyProfileLegacy = frequencyProfileLegacy; mFrequencyProfile = frequencyProfile; mMaxEnvelopeEffectSize = maxEnvelopeEffectSize; mMinEnvelopeEffectControlPointDurationMillis = @@ -155,6 +176,7 @@ public class VibratorInfo implements Parcelable { dest.writeInt(mPwlePrimitiveDurationMax); dest.writeInt(mPwleSizeMax); dest.writeFloat(mQFactor); + mFrequencyProfileLegacy.writeToParcel(dest, flags); mFrequencyProfile.writeToParcel(dest, flags); dest.writeInt(mMaxEnvelopeEffectSize); dest.writeInt(mMinEnvelopeEffectControlPointDurationMillis); @@ -205,6 +227,7 @@ public class VibratorInfo implements Parcelable { && Objects.equals(mSupportedEffects, that.mSupportedEffects) && Objects.equals(mSupportedBraking, that.mSupportedBraking) && Objects.equals(mQFactor, that.mQFactor) + && Objects.equals(mFrequencyProfileLegacy, that.mFrequencyProfileLegacy) && Objects.equals(mFrequencyProfile, that.mFrequencyProfile) && mMaxEnvelopeEffectSize == that.mMaxEnvelopeEffectSize && mMinEnvelopeEffectControlPointDurationMillis @@ -216,7 +239,7 @@ public class VibratorInfo implements Parcelable { @Override public int hashCode() { int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking, - mQFactor, mFrequencyProfile); + mQFactor, mFrequencyProfileLegacy, mFrequencyProfile); for (int i = 0; i < mSupportedPrimitives.size(); i++) { hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i); hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i); @@ -238,6 +261,7 @@ public class VibratorInfo implements Parcelable { + ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax + ", mPwleSizeMax=" + mPwleSizeMax + ", mQFactor=" + mQFactor + + ", mFrequencyProfileLegacy=" + mFrequencyProfileLegacy + ", mFrequencyProfile=" + mFrequencyProfile + ", mMaxEnvelopeEffectSize=" + mMaxEnvelopeEffectSize + ", mMinEnvelopeEffectControlPointDurationMillis=" @@ -262,6 +286,7 @@ public class VibratorInfo implements Parcelable { pw.println("pwlePrimitiveDurationMax = " + mPwlePrimitiveDurationMax); pw.println("pwleSizeMax = " + mPwleSizeMax); pw.println("q-factor = " + mQFactor); + pw.println("frequencyProfileLegacy = " + mFrequencyProfileLegacy); pw.println("frequencyProfile = " + mFrequencyProfile); pw.println("mMaxEnvelopeEffectSize = " + mMaxEnvelopeEffectSize); pw.println("mMinEnvelopeEffectControlPointDurationMillis = " @@ -517,7 +542,10 @@ public class VibratorInfo implements Parcelable { * this vibrator is a composite of multiple physical devices. */ public float getResonantFrequencyHz() { - return mFrequencyProfile.mResonantFrequencyHz; + if (Flags.normalizedPwleEffects()) { + return mFrequencyProfile.mResonantFrequencyHz; + } + return mFrequencyProfileLegacy.mResonantFrequencyHz; } /** @@ -537,6 +565,17 @@ public class VibratorInfo implements Parcelable { *

        If the devices does not have frequency control then the profile should be empty. */ @NonNull + public FrequencyProfileLegacy getFrequencyProfileLegacy() { + return mFrequencyProfileLegacy; + } + + /** + * Gets the profile of supported frequencies, including the measurements of maximum + * output acceleration for supported vibration frequencies. + * + *

        If the devices does not have frequency control then the profile should be empty. + */ + @NonNull public FrequencyProfile getFrequencyProfile() { return mFrequencyProfile; } @@ -622,6 +661,304 @@ public class VibratorInfo implements Parcelable { return names; } + /** + * Describes the maximum output acceleration that can be achieved for each supported + * frequency in a specific vibrator. + * + * @hide + */ + public static final class FrequencyProfile implements Parcelable { + + private final float[] mFrequenciesHz; + private final float[] mOutputAccelerationsGs; + private final float mResonantFrequencyHz; + private final float mMaxOutputAccelerationGs; + private final float mMinFrequencyHz; + private final float mMaxFrequencyHz; + + public FrequencyProfile(Parcel in) { + this(in.readFloat(), in.createFloatArray(), in.createFloatArray()); + } + + /** + * Default constructor. + * + * @param resonantFrequencyHz The vibrator resonant frequency, in hertz. + * @param frequenciesHz The supported vibration frequencies, in hertz. + * @param outputAccelerationsGs The maximum achievable output acceleration (in Gs) the + * device can reach at the supported frequencies. + */ + public FrequencyProfile(float resonantFrequencyHz, float[] frequenciesHz, + float[] outputAccelerationsGs) { + + mResonantFrequencyHz = resonantFrequencyHz; + + boolean isValid = !Float.isNaN(resonantFrequencyHz) + && (resonantFrequencyHz > 0) + && (frequenciesHz != null && outputAccelerationsGs != null) + && (frequenciesHz.length == outputAccelerationsGs.length) + && (frequenciesHz.length > 0); + + if (!isValid) { + mFrequenciesHz = null; + mOutputAccelerationsGs = null; + mMinFrequencyHz = Float.NaN; + mMaxFrequencyHz = Float.NaN; + mMaxOutputAccelerationGs = Float.NaN; + return; + } + + TreeMap frequencyToOutputAccelerationMap = new TreeMap<>(); + + for (int i = 0; i < frequenciesHz.length; i++) { + frequencyToOutputAccelerationMap.putIfAbsent(frequenciesHz[i], + outputAccelerationsGs[i]); + } + + float[] frequencies = new float[frequencyToOutputAccelerationMap.size()]; + float[] accelerations = new float[frequencyToOutputAccelerationMap.size()]; + float maxOutputAccelerationGs = 0; + int i = 0; + for (Map.Entry entry : frequencyToOutputAccelerationMap.entrySet()) { + frequencies[i] = entry.getKey(); + accelerations[i] = entry.getValue(); + maxOutputAccelerationGs = Math.max(maxOutputAccelerationGs, entry.getValue()); + i++; + } + + mFrequenciesHz = frequencies; + mOutputAccelerationsGs = accelerations; + mMinFrequencyHz = mFrequenciesHz[0]; + mMaxFrequencyHz = mFrequenciesHz[mFrequenciesHz.length - 1]; + mMaxOutputAccelerationGs = maxOutputAccelerationGs; + } + + /** Returns true if the supported frequency range is null. */ + public boolean isEmpty() { + return mFrequenciesHz == null; + } + + /** + * Returns a list of available frequencies. + */ + @Nullable + public float[] getFrequenciesHz() { + return mFrequenciesHz; + } + + /** Returns the list of available output accelerations */ + @Nullable + public float[] getOutputAccelerationsGs() { + return mOutputAccelerationsGs; + } + + /** Maximum output acceleration reachable in Gs when amplitude is 1.0f. */ + public float getMaxOutputAccelerationGs() { + return mMaxOutputAccelerationGs; + } + + /** + * Calculates the maximum output acceleration for a given frequency using linear + * interpolation. + * + * @param frequencyHz frequency, in hertz, for query. + * @return the maximum output acceleration for the given frequency. + */ + public float getOutputAccelerationGs(float frequencyHz) { + if (mFrequenciesHz == null) { + return Float.NaN; + } + + if (frequencyHz < mMinFrequencyHz || frequencyHz > mMaxFrequencyHz) { + // Outside supported frequency range, not able to vibrate at this frequency. + return 0; + } + + int idx = Arrays.binarySearch(mFrequenciesHz, frequencyHz); + if (idx >= 0) { + return mOutputAccelerationsGs[idx]; + } + + // This indicates that the value was not found in the list. Adjust index of the + // insertion point to be at the lower bound. + idx = -idx - 2; + + // Linearly interpolate the output acceleration based on the frequency. + return MathUtils.constrainedMap( + mOutputAccelerationsGs[idx], mOutputAccelerationsGs[idx + 1], + mFrequenciesHz[idx], mFrequenciesHz[idx + 1], + frequencyHz); + } + + /** The minimum frequency supported, in hertz. */ + public float getMinFrequencyHz() { + return mMinFrequencyHz; + } + + /** The maximum frequency supported, in hertz. */ + public float getMaxFrequencyHz() { + return mMaxFrequencyHz; + } + + /** + * Returns the frequency range that supports the specified minimum output + * acceleration. + * + * @return The frequency range, or null if the specified acceleration + * is not achievable on the device. + */ + @Nullable + public Range getFrequencyRangeHz(float minOutputAcceleration) { + if (mFrequenciesHz == null || mOutputAccelerationsGs == null + || minOutputAcceleration > mMaxOutputAccelerationGs) { + return null; // No frequency range available + } + + if (minOutputAcceleration <= 0) { + return new Range<>(mMinFrequencyHz, mMaxFrequencyHz); + } + + float minFrequency = Float.NaN; + float maxFrequency = Float.NaN; + int lowerFrequencyBoundIndex = 0; + // Find the lower frequency bound + for (int i = 0; i < mOutputAccelerationsGs.length; i++) { + if (mOutputAccelerationsGs[i] >= minOutputAcceleration) { + if (i == 0) { + minFrequency = mMinFrequencyHz; + } else { + minFrequency = MathUtils.constrainedMap( + mFrequenciesHz[i - 1], mFrequenciesHz[i], + mOutputAccelerationsGs[i - 1], mOutputAccelerationsGs[i], + minOutputAcceleration); + } + lowerFrequencyBoundIndex = i; + break; // Found the lower bound + } + } + + if (Float.isNaN(minFrequency)) { + // Lower bound was not found + return null; + } + + // Find the upper frequency bound + for (int i = lowerFrequencyBoundIndex; i < mOutputAccelerationsGs.length; i++) { + if (mOutputAccelerationsGs[i] <= minOutputAcceleration) { + maxFrequency = MathUtils.constrainedMap( + mFrequenciesHz[i - 1], mFrequenciesHz[i], + mOutputAccelerationsGs[i - 1], mOutputAccelerationsGs[i], + minOutputAcceleration); + break; // Found the upper bound + } + } + + if (Float.isNaN(maxFrequency)) { + // If the upper bound was not found, the specified output acceleration is + // achievable at all remaining frequencies. + maxFrequency = mMaxFrequencyHz; + } + + return new Range<>(minFrequency, maxFrequency); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(mResonantFrequencyHz); + dest.writeFloatArray(mFrequenciesHz); + dest.writeFloatArray(mOutputAccelerationsGs); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof FrequencyProfile that)) { + return false; + } + return Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0 + && Arrays.equals(mFrequenciesHz, that.mFrequenciesHz) + && Arrays.equals(mOutputAccelerationsGs, that.mOutputAccelerationsGs); + } + + @Override + public int hashCode() { + return Objects.hash(mResonantFrequencyHz, Arrays.hashCode(mFrequenciesHz), + Arrays.hashCode(mOutputAccelerationsGs)); + } + + @Override + public String toString() { + return "FrequencyProfile{" + + "mResonantFrequency=" + mResonantFrequencyHz + + ", mFrequenciesHz=" + Arrays.toString(mFrequenciesHz) + + ", mOutputAccelerationsGs=" + Arrays.toString(mOutputAccelerationsGs) + + ", mMinFrequencyHz=" + mMinFrequencyHz + + ", mMaxFrequencyHz=" + mMaxFrequencyHz + + ", mMaxOutputAccelerationGs=" + mMaxOutputAccelerationGs + + '}'; + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public FrequencyProfile createFromParcel(Parcel in) { + return new FrequencyProfile(in); + } + + @Override + public FrequencyProfile[] newArray(int size) { + return new FrequencyProfile[size]; + } + }; + + private static void deduplicateAndSortList(List> list) { + if (list == null || list.size() < 2) { + return; // Nothing to dedupe + } + + list.sort(Comparator.comparing(pair -> pair.first)); + + // Remove duplicates from the list + int writeIndex = 1; + for (int i = 1; i < list.size(); i++) { + Pair currentPair = list.get(i); + Pair previousPair = list.get(writeIndex - 1); + + if (currentPair.first.compareTo(previousPair.first) != 0) { + list.set(writeIndex++, currentPair); + } + } + list.subList(writeIndex, list.size()).clear(); + } + + private static ArrayList> extractFrequencyToOutputAccelerationData( + float[] frequencies, float[] outputAccelerations) { + + if (frequencies == null || outputAccelerations == null + || frequencies.length == 0 + || frequencies.length != outputAccelerations.length) { + return new ArrayList<>(); // Return empty list for invalid or mismatched data + } + + ArrayList> frequencyToOutputAccelerationList = new ArrayList<>( + frequencies.length); + for (int i = 0; i < frequencies.length; i++) { + frequencyToOutputAccelerationList.add( + new Pair<>(frequencies[i], outputAccelerations[i])); + } + + return frequencyToOutputAccelerationList; + } + } + /** * Describes the maximum relative output acceleration that can be achieved for each supported * frequency in a specific vibrator. @@ -640,7 +977,7 @@ public class VibratorInfo implements Parcelable { * * @hide */ - public static final class FrequencyProfile implements Parcelable { + public static final class FrequencyProfileLegacy implements Parcelable { @Nullable private final Range mFrequencyRangeHz; private final float mMinFrequencyHz; @@ -648,7 +985,7 @@ public class VibratorInfo implements Parcelable { private final float mFrequencyResolutionHz; private final float[] mMaxAmplitudes; - FrequencyProfile(Parcel in) { + FrequencyProfileLegacy(Parcel in) { this(in.readFloat(), in.readFloat(), in.readFloat(), in.createFloatArray()); } @@ -664,7 +1001,7 @@ public class VibratorInfo implements Parcelable { * resolution. * @hide */ - public FrequencyProfile(float resonantFrequencyHz, float minFrequencyHz, + public FrequencyProfileLegacy(float resonantFrequencyHz, float minFrequencyHz, float frequencyResolutionHz, float[] maxAmplitudes) { mMinFrequencyHz = minFrequencyHz; mResonantFrequencyHz = resonantFrequencyHz; @@ -776,10 +1113,10 @@ public class VibratorInfo implements Parcelable { if (this == o) { return true; } - if (!(o instanceof FrequencyProfile)) { + if (!(o instanceof FrequencyProfileLegacy)) { return false; } - FrequencyProfile that = (FrequencyProfile) o; + FrequencyProfileLegacy that = (FrequencyProfileLegacy) o; return Float.compare(mMinFrequencyHz, that.mMinFrequencyHz) == 0 && Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0 && Float.compare(mFrequencyResolutionHz, that.mFrequencyResolutionHz) == 0 @@ -796,7 +1133,7 @@ public class VibratorInfo implements Parcelable { @Override public String toString() { - return "FrequencyProfile{" + return "FrequencyProfileLegacy{" + "mFrequencyRange=" + mFrequencyRangeHz + ", mMinFrequency=" + mMinFrequencyHz + ", mResonantFrequency=" + mResonantFrequencyHz @@ -806,16 +1143,16 @@ public class VibratorInfo implements Parcelable { } @NonNull - public static final Creator CREATOR = - new Creator() { + public static final Creator CREATOR = + new Creator() { @Override - public FrequencyProfile createFromParcel(Parcel in) { - return new FrequencyProfile(in); + public FrequencyProfileLegacy createFromParcel(Parcel in) { + return new FrequencyProfileLegacy(in); } @Override - public FrequencyProfile[] newArray(int size) { - return new FrequencyProfile[size]; + public FrequencyProfileLegacy[] newArray(int size) { + return new FrequencyProfileLegacy[size]; } }; } @@ -832,8 +1169,10 @@ public class VibratorInfo implements Parcelable { private int mPwlePrimitiveDurationMax; private int mPwleSizeMax; private float mQFactor = Float.NaN; - private FrequencyProfile mFrequencyProfile = - new FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null); + private FrequencyProfileLegacy mFrequencyProfileLegacy = + new FrequencyProfileLegacy(Float.NaN, Float.NaN, Float.NaN, null); + private FrequencyProfile mFrequencyProfile = new FrequencyProfile(Float.NaN, null, + null); private int mMaxEnvelopeEffectSize; private int mMinEnvelopeEffectControlPointDurationMillis; private int mMaxEnvelopeEffectControlPointDurationMillis; @@ -908,6 +1247,16 @@ public class VibratorInfo implements Parcelable { /** Configure the vibrator frequency information like resonant frequency and bandwidth. */ @NonNull + public Builder setFrequencyProfileLegacy(@NonNull FrequencyProfileLegacy frequencyProfile) { + mFrequencyProfileLegacy = frequencyProfile; + return this; + } + + /** + * Configure the vibrator frequency information like resonant frequency and frequency to + * output acceleration data. + */ + @NonNull public Builder setFrequencyProfile(@NonNull FrequencyProfile frequencyProfile) { mFrequencyProfile = frequencyProfile; return this; @@ -950,8 +1299,9 @@ public class VibratorInfo implements Parcelable { public VibratorInfo build() { return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking, mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax, - mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyProfile, - mMaxEnvelopeEffectSize, mMinEnvelopeEffectControlPointDurationMillis, + mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyProfileLegacy, + mFrequencyProfile, mMaxEnvelopeEffectSize, + mMinEnvelopeEffectControlPointDurationMillis, mMaxEnvelopeEffectControlPointDurationMillis); } diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index a1bfe39c0fc4df446754e3896f8dc75e50ee04cb..c7cc653f41784d0dba864242f3219a2e59dbe0c5 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -2,43 +2,74 @@ package: "android.os" container: "system" container: "system" +# keep-sorted start block=yes newline_separated=yes flag { - name: "android_os_build_vanilla_ice_cream" + name: "adpf_gpu_report_actual_work_duration" is_exported: true - namespace: "build" - description: "Feature flag for adding the VANILLA_ICE_CREAM constant." - bug: "264658905" + namespace: "game" + description: "Guards the ADPF GPU APIs." + bug: "284324521" } flag { - name: "state_of_health_public" - is_exported: true - namespace: "system_sw_battery" - description: "Feature flag for making state_of_health a public api." - bug: "288842045" + name: "adpf_hwui_gpu" + namespace: "game" + description: "Guards use of the FMQ channel for ADPF" + is_fixed_read_only: true + bug: "330922490" } flag { - name: "disallow_cellular_null_ciphers_restriction" - namespace: "cellular_security" - description: "Guards a new UserManager user restriction that admins can use to require cellular encryption on their managed devices." - bug: "276752881" + name: "adpf_measure_during_input_event_boost" + namespace: "game" + description: "Guards use of a boost when view measures during input events" + bug: "256549451" } flag { - name: "remove_app_profiler_pss_collection" - is_exported: true - namespace: "backstage_power" - description: "Replaces background PSS collection in AppProfiler with RSS" - bug: "297542292" + name: "adpf_obtainview_boost" + namespace: "game" + description: "Guards use of a boost in response to HWUI obtainView" + is_fixed_read_only: true + bug: "328238660" } flag { - name: "allow_thermal_headroom_thresholds" + name: "adpf_platform_power_efficiency" + namespace: "game" + description: "Guards use of the ADPF power efficiency API within the platform" + is_fixed_read_only: true + bug: "277285195" +} + +flag { + name: "adpf_prefer_power_efficiency" is_exported: true namespace: "game" - description: "Enable thermal headroom thresholds API" - bug: "288119641" + description: "Guards the ADPF power efficiency API" + bug: "288117936" +} + +flag { + name: "adpf_use_fmq_channel" + namespace: "game" + description: "Guards use of the FMQ channel for ADPF" + bug: "315894228" +} + +flag { + name: "adpf_use_fmq_channel_fixed" + namespace: "game" + description: "Guards use of the FMQ channel for ADPF with a readonly flag" + is_fixed_read_only: true + bug: "315894228" +} + +flag { + name: "allow_consentless_bugreport_delegated_consent" + namespace: "crumpet" + description: "Allow privileged apps to call bugreport generation without enforcing user consent and delegate it to the calling app instead" + bug: "324046728" } # This flag guards the private space feature, its APIs, and some of the feature implementations. The flag android.multiuser.Flags.enable_private_space_features exclusively guards all the implementations. @@ -52,27 +83,36 @@ flag { } flag { - name: "adpf_prefer_power_efficiency" + name: "allow_thermal_headroom_thresholds" is_exported: true namespace: "game" - description: "Guards the ADPF power efficiency API" - bug: "288117936" + description: "Enable thermal headroom thresholds API" + bug: "288119641" } flag { - name: "security_state_service" + name: "android_os_build_vanilla_ice_cream" is_exported: true - namespace: "dynamic_spl" - description: "Guards the Security State API." - bug: "302189431" + namespace: "build" + description: "Feature flag for adding the VANILLA_ICE_CREAM constant." + bug: "264658905" } flag { - name: "ordered_broadcast_multiple_permissions" + name: "api_for_backported_fixes" + namespace: "media_reliability" + description: "Public API app developers use to check if a known issue is fixed on a device." + bug: "308461809" is_exported: true - namespace: "bluetooth" - description: "Guards the Context.sendOrderedBroadcastMultiplePermissions API" - bug: "345802719" +} + +flag { + name: "battery_part_status_api" + is_exported: true + namespace: "phoenix" + description: "Feature flag for adding Health HAL v3 APIs." + is_fixed_read_only: true + bug: "309792384" } flag { @@ -84,97 +124,98 @@ flag { } flag { - name: "adpf_gpu_report_actual_work_duration" - is_exported: true - namespace: "game" - description: "Guards the ADPF GPU APIs." - bug: "284324521" + name: "battery_service_support_current_adb_command" + namespace: "backstage_power" + description: "Whether or not BatteryService supports adb commands for Current values." + is_fixed_read_only: true + bug: "315037695" } flag { - name: "adpf_use_fmq_channel" - namespace: "game" - description: "Guards use of the FMQ channel for ADPF" - bug: "315894228" + name: "binder_frozen_state_change_callback" + is_exported: true + namespace: "system_performance" + description: "Guards the frozen state change callback API." + bug: "361157077" } flag { - name: "adpf_use_fmq_channel_fixed" - namespace: "game" - description: "Guards use of the FMQ channel for ADPF with a readonly flag" - is_fixed_read_only: true - bug: "315894228" + name: "disallow_cellular_null_ciphers_restriction" + namespace: "cellular_security" + description: "Guards a new UserManager user restriction that admins can use to require cellular encryption on their managed devices." + bug: "276752881" } flag { - name: "adpf_hwui_gpu" - namespace: "game" - description: "Guards use of the FMQ channel for ADPF" + name: "enable_angle_allow_list" + namespace: "gpu" + description: "Whether to read from angle allowlist to determine if app should use ANGLE" is_fixed_read_only: true - bug: "330922490" + bug: "370845648" } flag { - name: "adpf_obtainview_boost" - namespace: "game" - description: "Guards use of a boost in response to HWUI obtainView" - is_fixed_read_only: true - bug: "328238660" + name: "get_private_space_settings" + namespace: "profile_experiences" + description: "Guards a new Private Profile API in LauncherApps" + bug: "346294653" + is_exported: true } flag { - name: "adpf_platform_power_efficiency" - namespace: "game" - description: "Guards use of the ADPF power efficiency API within the platform" - is_fixed_read_only: true - bug: "277285195" + name: "mainline_vcn_platform_api" + namespace: "vcn" + description: "Expose platform APIs to mainline VCN" + is_exported: true + bug: "366598445" } flag { - name: "adpf_measure_during_input_event_boost" - namespace: "game" - description: "Guards use of a boost when view measures during input events" - bug: "256549451" + name: "message_queue_tail_tracking" + namespace: "system_performance" + description: "track tail of message queue." + bug: "305311707" + is_fixed_read_only: true } flag { - name: "battery_service_support_current_adb_command" - namespace: "backstage_power" - description: "Whether or not BatteryService supports adb commands for Current values." - is_fixed_read_only: true - bug: "315037695" + name: "network_time_uses_shared_memory" + namespace: "system_performance" + description: "SystemClock.currentNetworkTimeMillis() reads network time offset from shared memory" + bug: "361329788" + is_exported: true } flag { - name: "strict_mode_restricted_network" - namespace: "backstage_power" - description: "Guards StrictMode APIs for detecting restricted network access." - bug: "317250784" + name: "ordered_broadcast_multiple_permissions" + is_exported: true + namespace: "bluetooth" + description: "Guards the Context.sendOrderedBroadcastMultiplePermissions API" + bug: "345802719" } flag { - name: "binder_frozen_state_change_callback" + name: "remove_app_profiler_pss_collection" is_exported: true - namespace: "system_performance" - description: "Guards the frozen state change callback API." - bug: "361157077" + namespace: "backstage_power" + description: "Replaces background PSS collection in AppProfiler with RSS" + bug: "297542292" } flag { - name: "message_queue_tail_tracking" - namespace: "system_performance" - description: "track tail of message queue." - bug: "305311707" - is_fixed_read_only: true + name: "security_state_service" + is_exported: true + namespace: "dynamic_spl" + description: "Guards the Security State API." + bug: "302189431" } flag { - name: "battery_part_status_api" + name: "state_of_health_public" is_exported: true - namespace: "phoenix" - description: "Feature flag for adding Health HAL v3 APIs." - is_fixed_read_only: true - bug: "309792384" + namespace: "system_sw_battery" + description: "Feature flag for making state_of_health a public api." + bug: "288842045" } flag { @@ -187,12 +228,10 @@ flag { } flag { - namespace: "system_performance" - name: "telemetry_apis_framework_initialization" - is_exported: true - description: "Control framework initialization APIs of telemetry APIs feature." - is_fixed_read_only: true - bug: "324241334" + name: "strict_mode_restricted_network" + namespace: "backstage_power" + description: "Guards StrictMode APIs for detecting restricted network access." + bug: "317250784" } flag { @@ -203,32 +242,12 @@ flag { } flag { - name: "allow_consentless_bugreport_delegated_consent" - namespace: "crumpet" - description: "Allow privileged apps to call bugreport generation without enforcing user consent and delegate it to the calling app instead" - bug: "324046728" -} - -flag { - name: "get_private_space_settings" - namespace: "profile_experiences" - description: "Guards a new Private Profile API in LauncherApps" - bug: "346294653" - is_exported: true -} - -flag { - name: "mainline_vcn_platform_api" - namespace: "vcn" - description: "Expose platform APIs to mainline VCN" + namespace: "system_performance" + name: "telemetry_apis_framework_initialization" is_exported: true - bug: "366598445" + description: "Control framework initialization APIs of telemetry APIs feature." + is_fixed_read_only: true + bug: "324241334" } -flag { - name: "network_time_uses_shared_memory" - namespace: "system_performance" - description: "SystemClock.currentNetworkTimeMillis() reads network time offset from shared memory" - bug: "361329788" - is_exported: true -} +# keep-sorted end diff --git a/core/java/android/os/vibrator/MultiVibratorInfo.java b/core/java/android/os/vibrator/MultiVibratorInfo.java index 9c2b9782ffb37bde6c4ff65e41ef54356aa7dc6b..1ba8d9973d2b7d25ac181081bb6bf6029bf72183 100644 --- a/core/java/android/os/vibrator/MultiVibratorInfo.java +++ b/core/java/android/os/vibrator/MultiVibratorInfo.java @@ -27,6 +27,9 @@ import android.util.SparseBooleanArray; import android.util.SparseIntArray; import java.util.Arrays; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; import java.util.function.Function; /** @@ -44,13 +47,18 @@ public final class MultiVibratorInfo extends VibratorInfo { private static final float EPSILON = 1e-5f; public MultiVibratorInfo(int id, VibratorInfo[] vibrators) { - this(id, vibrators, frequencyProfileIntersection(vibrators)); + this(id, vibrators, frequencyProfileLegacyIntersection(vibrators), + frequencyProfileIntersection(vibrators)); } private MultiVibratorInfo( - int id, VibratorInfo[] vibrators, VibratorInfo.FrequencyProfile mergedProfile) { + int id, VibratorInfo[] vibrators, + VibratorInfo.FrequencyProfileLegacy mergedLegacyProfile, + FrequencyProfile mergedProfile) { super(id, - capabilitiesIntersection(vibrators, mergedProfile.isEmpty()), + capabilitiesIntersection(vibrators, + Flags.normalizedPwleEffects() ? mergedProfile.isEmpty() + : mergedLegacyProfile.isEmpty()), supportedEffectsIntersection(vibrators), supportedBrakingIntersection(vibrators), supportedPrimitivesAndDurationsIntersection(vibrators), @@ -59,6 +67,7 @@ public final class MultiVibratorInfo extends VibratorInfo { integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax), integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax), floatPropertyIntersection(vibrators, VibratorInfo::getQFactor), + mergedLegacyProfile, mergedProfile, integerLimitIntersection(vibrators, VibratorInfo::getMaxEnvelopeEffectSize), @@ -210,14 +219,89 @@ public final class MultiVibratorInfo extends VibratorInfo { @NonNull private static FrequencyProfile frequencyProfileIntersection(VibratorInfo[] infos) { + if (infos == null || infos.length == 0) { + return new FrequencyProfile(Float.NaN, + /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null); + } + + float resonantFreq = floatPropertyIntersection(infos, VibratorInfo::getResonantFrequencyHz); + + if (Float.isNaN(resonantFreq)) { + return new FrequencyProfile(Float.NaN, + /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null); + } + + float minFrequency = 0.0f; + float maxFrequency = Float.MAX_VALUE; + Set allFrequencies = new TreeSet<>(); // Using TreeSet for automatic sorting + + for (VibratorInfo info : infos) { + float newMinFrequency = info.getFrequencyProfile().getMinFrequencyHz(); + float newMaxFrequency = info.getFrequencyProfile().getMaxFrequencyHz(); + + if (Float.isNaN(newMinFrequency) || Float.isNaN(newMaxFrequency)) { + // If one vibrator is undefined then the intersection is undefined. + return new FrequencyProfile(Float.NaN, + /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null); + } + + minFrequency = Math.max(minFrequency, newMinFrequency); + maxFrequency = Math.min(maxFrequency, newMaxFrequency); + + if (info.getFrequencyProfile().getFrequenciesHz() == null) { + return new FrequencyProfile(Float.NaN, + /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null); + } + + for (float frequency : info.getFrequencyProfile().getFrequenciesHz()) { + allFrequencies.add(frequency); + } + } + + if (minFrequency > maxFrequency) { + // If the range and intersection are disjoint then the intersection is undefined + return new FrequencyProfile(Float.NaN, + /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null); + } + + // Trim frequencies to the min/max range + Iterator iterator = allFrequencies.iterator(); + while (iterator.hasNext()) { + float frequency = iterator.next(); + if (frequency < minFrequency || frequency > maxFrequency) { + iterator.remove(); + } + } + + float[] frequencies = new float[allFrequencies.size()]; + float[] accelerations = new float[allFrequencies.size()]; + int idx = 0; + + for (Float frequency : allFrequencies) { + float outputAcceleration = Float.MAX_VALUE; + for (VibratorInfo info : infos) { + // This will find the mapped value or interpolate it if needed. + outputAcceleration = Math.min(outputAcceleration, + info.getFrequencyProfile().getOutputAccelerationGs(frequency)); + } + frequencies[idx] = frequency; + accelerations[idx] = outputAcceleration; + idx++; + } + + return new FrequencyProfile(resonantFreq, frequencies, accelerations); + } + + @NonNull + private static FrequencyProfileLegacy frequencyProfileLegacyIntersection(VibratorInfo[] infos) { float freqResolution = floatPropertyIntersection(infos, - info -> info.getFrequencyProfile().getFrequencyResolutionHz()); + info -> info.getFrequencyProfileLegacy().getFrequencyResolutionHz()); float resonantFreq = floatPropertyIntersection(infos, VibratorInfo::getResonantFrequencyHz); Range freqRange = frequencyRangeIntersection(infos, freqResolution); if ((freqRange == null) || Float.isNaN(freqResolution)) { - return new FrequencyProfile(resonantFreq, Float.NaN, freqResolution, null); + return new FrequencyProfileLegacy(resonantFreq, Float.NaN, freqResolution, null); } int amplitudeCount = @@ -230,8 +314,8 @@ public final class MultiVibratorInfo extends VibratorInfo { Arrays.fill(maxAmplitudes, Float.MAX_VALUE); for (VibratorInfo info : infos) { - Range vibratorFreqRange = info.getFrequencyProfile().getFrequencyRangeHz(); - float[] vibratorMaxAmplitudes = info.getFrequencyProfile().getMaxAmplitudes(); + Range vibratorFreqRange = info.getFrequencyProfileLegacy().getFrequencyRangeHz(); + float[] vibratorMaxAmplitudes = info.getFrequencyProfileLegacy().getMaxAmplitudes(); int vibratorStartIdx = Math.round( (freqRange.getLower() - vibratorFreqRange.getLower()) / freqResolution); int vibratorEndIdx = vibratorStartIdx + maxAmplitudes.length - 1; @@ -240,7 +324,7 @@ public final class MultiVibratorInfo extends VibratorInfo { Slog.w(TAG, "Error calculating the intersection of vibrator frequency" + " profiles: attempted to fetch from vibrator " + info.getId() + " max amplitude with bad index " + vibratorStartIdx); - return new FrequencyProfile(resonantFreq, Float.NaN, Float.NaN, null); + return new FrequencyProfileLegacy(resonantFreq, Float.NaN, Float.NaN, null); } for (int i = 0; i < maxAmplitudes.length; i++) { @@ -249,14 +333,14 @@ public final class MultiVibratorInfo extends VibratorInfo { } } - return new FrequencyProfile(resonantFreq, freqRange.getLower(), + return new FrequencyProfileLegacy(resonantFreq, freqRange.getLower(), freqResolution, maxAmplitudes); } @Nullable private static Range frequencyRangeIntersection(VibratorInfo[] infos, float frequencyResolution) { - Range firstRange = infos[0].getFrequencyProfile().getFrequencyRangeHz(); + Range firstRange = infos[0].getFrequencyProfileLegacy().getFrequencyRangeHz(); if (firstRange == null) { // If one vibrator is undefined then the intersection is undefined. return null; @@ -268,7 +352,7 @@ public final class MultiVibratorInfo extends VibratorInfo { // min supported frequencies are aligned w.r.t. the frequency resolution. for (int i = 1; i < infos.length; i++) { - Range vibratorRange = infos[i].getFrequencyProfile().getFrequencyRangeHz(); + Range vibratorRange = infos[i].getFrequencyProfileLegacy().getFrequencyRangeHz(); if (vibratorRange == null) { // If one vibrator is undefined then the intersection is undefined. return null; diff --git a/core/java/android/os/vibrator/VibratorFrequencyProfile.java b/core/java/android/os/vibrator/VibratorFrequencyProfile.java index 8392940010e3116e288725a296e3cbe50bcabf7c..2b5f9bf2a22ea62b42452854c3122765037a6933 100644 --- a/core/java/android/os/vibrator/VibratorFrequencyProfile.java +++ b/core/java/android/os/vibrator/VibratorFrequencyProfile.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,95 +16,135 @@ package android.os.vibrator; -import android.annotation.FloatRange; +import android.annotation.FlaggedApi; import android.annotation.NonNull; -import android.annotation.TestApi; +import android.annotation.Nullable; import android.os.VibratorInfo; +import android.util.Range; +import android.util.SparseArray; import com.android.internal.util.Preconditions; +import java.util.Objects; + /** * Describes the output of a {@link android.os.Vibrator} for different vibration frequencies. * - *

        The profile contains the minimum and maximum supported vibration frequencies, if the device - * supports independent frequency control. - * - *

        It also describes the relative output acceleration of a vibration at different supported - * frequencies. The acceleration is defined by a relative amplitude value between 0 and 1, - * inclusive, where 0 represents the vibrator off state and 1 represents the maximum output - * acceleration that the vibrator can reach across all supported frequencies. + *

        The profile contains the vibrator's frequency range (minimum/maximum) and maximum + * acceleration, enabling retrieval of supported acceleration levels for specific frequencies, if + * the device supports independent frequency control. * - *

        The measurements are returned as an array of uniformly distributed amplitude values for - * frequencies between the minimum and maximum supported ones. The measurement interval is the - * frequency increment between each pair of amplitude values. + *

        It also describes the max output acceleration (Gs), of a vibration at different supported + * frequencies (Hz). * *

        Vibrators without independent frequency control do not have a frequency profile. - * @hide */ -@TestApi +@FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public final class VibratorFrequencyProfile { private final VibratorInfo.FrequencyProfile mFrequencyProfile; + private final SparseArray mFrequenciesOutputAcceleration; /** @hide */ public VibratorFrequencyProfile(@NonNull VibratorInfo.FrequencyProfile frequencyProfile) { + Objects.requireNonNull(frequencyProfile); Preconditions.checkArgument(!frequencyProfile.isEmpty(), - "Frequency profile must have a non-empty frequency range"); + "Frequency profile must not be empty"); mFrequencyProfile = frequencyProfile; + mFrequenciesOutputAcceleration = generateFrequencyToAccelerationMap( + frequencyProfile.getFrequenciesHz(), frequencyProfile.getOutputAccelerationsGs()); } /** - * Measurements of the maximum relative amplitude the vibrator can achieve for each supported - * frequency. + * Returns a {@link SparseArray} representing the vibrator's output acceleration capabilities + * across different frequencies. This map defines the maximum acceleration + * the vibrator can achieve at each supported frequency. + *

        The map's keys are frequencies in Hz, and the corresponding values + * are the maximum achievable output accelerations in Gs. * - *

        The frequency of a measurement is determined as: + * @return A map of frequencies (Hz) to maximum accelerations (Gs). + */ + @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + @NonNull + public SparseArray getFrequenciesOutputAcceleration() { + return mFrequenciesOutputAcceleration; + } + + /** + * Returns the maximum output acceleration (in Gs) supported by the vibrator. + * This value represents the highest acceleration the vibrator can achieve + * across its entire frequency range. * - * {@code getMinFrequency() + measurementIndex * getMaxAmplitudeMeasurementInterval()} + * @return The maximum output acceleration in Gs. + */ + @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public float getMaxOutputAccelerationGs() { + return mFrequencyProfile.getMaxOutputAccelerationGs(); + } + + /** + * Returns the frequency range (in Hz) where the vibrator can sustain at least + * the given minimum output acceleration (Gs). * - *

        The returned list will not be empty, and will have entries representing frequencies from - * {@link #getMinFrequency()} to {@link #getMaxFrequency()}, inclusive. + * @param minOutputAccelerationGs The minimum desired output acceleration in Gs. + * @return A {@link Range} object representing the frequency range where the + * vibrator can sustain at least the given minimum acceleration, or null if + * the minimum output acceleration cannot be achieved. * - * @return Array of maximum relative amplitude measurements. - * @hide */ - @TestApi - @NonNull - @FloatRange(from = 0, to = 1) - public float[] getMaxAmplitudeMeasurements() { - // VibratorInfo getters always return a copy or clone of the data objects. - return mFrequencyProfile.getMaxAmplitudes(); + @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + @Nullable + public Range getFrequencyRange(float minOutputAccelerationGs) { + return mFrequencyProfile.getFrequencyRangeHz(minOutputAccelerationGs); } /** - * Gets the frequency interval used to measure the maximum relative amplitudes. + * Returns the output acceleration (in Gs) for the given frequency (Hz). + * This method provides the actual acceleration the vibrator will produce + * when operating at the specified frequency, using linear interpolation over + * the {@link #getFrequenciesOutputAcceleration()}. * - * @return the frequency interval used for the measurement, in hertz. - * @hide + * @param frequencyHz The frequency in Hz. + * @return The output acceleration in Gs for the given frequency. */ - @TestApi - public float getMaxAmplitudeMeasurementInterval() { - return mFrequencyProfile.getFrequencyResolutionHz(); + @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public float getOutputAccelerationGs(float frequencyHz) { + return mFrequencyProfile.getOutputAccelerationGs(frequencyHz); } /** * Gets the minimum frequency supported by the vibrator. * * @return the minimum frequency supported by the vibrator, in hertz. - * @hide */ - @TestApi - public float getMinFrequency() { - return mFrequencyProfile.getFrequencyRangeHz().getLower(); + @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public float getMinFrequencyHz() { + return mFrequencyProfile.getMinFrequencyHz(); } /** * Gets the maximum frequency supported by the vibrator. * * @return the maximum frequency supported by the vibrator, in hertz. - * @hide */ - @TestApi - public float getMaxFrequency() { - return mFrequencyProfile.getFrequencyRangeHz().getUpper(); + @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public float getMaxFrequencyHz() { + return mFrequencyProfile.getMaxFrequencyHz(); + } + + private static SparseArray generateFrequencyToAccelerationMap( + float[] frequencies, float[] accelerations) { + SparseArray sparseArray = new SparseArray<>(frequencies.length); + + for (int i = 0; i < frequencies.length; i++) { + int frequency = (int) frequencies[i]; + float acceleration = accelerations[i]; + + sparseArray.put(frequency, + Math.min(acceleration, sparseArray.get(frequency, Float.MAX_VALUE))); + + } + + return sparseArray; } } diff --git a/core/java/android/os/vibrator/VibratorFrequencyProfileLegacy.java b/core/java/android/os/vibrator/VibratorFrequencyProfileLegacy.java new file mode 100644 index 0000000000000000000000000000000000000000..5c3e785550d33c0d673d80a7cd988cec9992d5e5 --- /dev/null +++ b/core/java/android/os/vibrator/VibratorFrequencyProfileLegacy.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os.vibrator; + +import android.annotation.FloatRange; +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.TestApi; +import android.os.VibratorInfo; + +import com.android.internal.util.Preconditions; + +/** + * Describes the output of a {@link android.os.Vibrator} for different vibration frequencies. + * + *

        The profile contains the minimum and maximum supported vibration frequencies, if the device + * supports independent frequency control. + * + *

        It also describes the relative output acceleration of a vibration at different supported + * frequencies. The acceleration is defined by a relative amplitude value between 0 and 1, + * inclusive, where 0 represents the vibrator off state and 1 represents the maximum output + * acceleration that the vibrator can reach across all supported frequencies. + * + *

        The measurements are returned as an array of uniformly distributed amplitude values for + * frequencies between the minimum and maximum supported ones. The measurement interval is the + * frequency increment between each pair of amplitude values. + * + *

        Vibrators without independent frequency control do not have a frequency profile. + * @hide + */ +@TestApi +@SuppressLint("UnflaggedApi") +public final class VibratorFrequencyProfileLegacy { + + private final VibratorInfo.FrequencyProfileLegacy mFrequencyProfile; + + /** @hide */ + public VibratorFrequencyProfileLegacy( + @NonNull VibratorInfo.FrequencyProfileLegacy frequencyProfile) { + Preconditions.checkArgument(!frequencyProfile.isEmpty(), + "Frequency profile must have a non-empty frequency range"); + mFrequencyProfile = frequencyProfile; + } + + /** + * Measurements of the maximum relative amplitude the vibrator can achieve for each supported + * frequency. + * + *

        The frequency of a measurement is determined as: + * + * {@code getMinFrequency() + measurementIndex * getMaxAmplitudeMeasurementInterval()} + * + *

        The returned list will not be empty, and will have entries representing frequencies from + * {@link #getMinFrequency()} to {@link #getMaxFrequency()}, inclusive. + * + * @return Array of maximum relative amplitude measurements. + * @hide + */ + @TestApi + @SuppressLint("UnflaggedApi") + @NonNull + @FloatRange(from = 0, to = 1) + public float[] getMaxAmplitudeMeasurements() { + // VibratorInfo getters always return a copy or clone of the data objects. + return mFrequencyProfile.getMaxAmplitudes(); + } + + /** + * Gets the frequency interval used to measure the maximum relative amplitudes. + * + * @return the frequency interval used for the measurement, in hertz. + * @hide + */ + @TestApi + @SuppressLint("UnflaggedApi") + public float getMaxAmplitudeMeasurementInterval() { + return mFrequencyProfile.getFrequencyResolutionHz(); + } + + /** + * Gets the minimum frequency supported by the vibrator. + * + * @return the minimum frequency supported by the vibrator, in hertz. + * @hide + */ + @TestApi + @SuppressLint("UnflaggedApi") + public float getMinFrequency() { + return mFrequencyProfile.getFrequencyRangeHz().getLower(); + } + + /** + * Gets the maximum frequency supported by the vibrator. + * + * @return the maximum frequency supported by the vibrator, in hertz. + * @hide + */ + @TestApi + @SuppressLint("UnflaggedApi") + public float getMaxFrequency() { + return mFrequencyProfile.getFrequencyRangeHz().getUpper(); + } +} diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index bca5bcc99c7e53d3ba4176c1dffaa6a84765eb2e..e59501f2742ac48b9522b8586fe11f3dc294e504 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -205,17 +205,6 @@ flag { bug: "308201969" } -flag { - name: "apex_signature_permission_allowlist_enabled" - is_fixed_read_only: true - namespace: "permissions" - description: "Enable reading signature permission allowlist from APEXes" - bug: "308573169" - metadata { - purpose: PURPOSE_BUGFIX - } -} - flag { name: "check_op_validate_package" namespace: "permissions" @@ -242,6 +231,13 @@ flag { } } +flag { + name: "sync_on_op_noted_api" + namespace: "permissions" + description: "New setOnOpNotedCallback API to allow subscribing to only sync ops." + bug: "372910217" +} + flag { name: "wallet_role_icon_property_enabled" is_exported: true @@ -266,3 +262,14 @@ flag { description: "This fixed read-only flag is used to enable replacing permission BODY_SENSORS (and BODY_SENSORS_BACKGROUND) with granular health permission READ_HEART_RATE (and READ_HEALTH_DATA_IN_BACKGROUND)" bug: "364638912" } + +flag { + name: "delay_uid_state_changes_from_capability_updates" + is_fixed_read_only: true + namespace: "permissions" + description: "If proc state is decreasing over the restriction threshold and capability is changed, delay if no new capabilities are added" + bug: "347891382" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index d557046280d9b9c95a44c26f0d830ce88d3318d5..5ecf361e83c7a332e8ae1a5be750c570b59485bf 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -3027,6 +3027,13 @@ public final class ContactsContract { */ @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED) public static final class DefaultAccount { + /** + * no public constructor since this is a utility class + */ + private DefaultAccount() { + + } + /** * Key in the outgoing Bundle for the default account list. * @@ -3063,11 +3070,6 @@ public final class ContactsContract { public static final String QUERY_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD = "queryDefaultAccountForNewContacts"; - private DefaultAccount() { - - } - - /** * Represents the state of the default account, and the actual {@link Account} if it's * a cloud account. @@ -3207,7 +3209,11 @@ public final class ContactsContract { return new DefaultAccountAndState(DEFAULT_ACCOUNT_STATE_NOT_SET, null); } - private static boolean isCloudOrSimAccount(@DefaultAccountState int state) { + /** + * + * @hide + */ + public static boolean isCloudOrSimAccount(@DefaultAccountState int state) { return state == DEFAULT_ACCOUNT_STATE_CLOUD || state == DEFAULT_ACCOUNT_STATE_SIM; } @@ -3285,23 +3291,20 @@ public final class ContactsContract { Bundle response = nullSafeCall(resolver, ContactsContract.AUTHORITY_URI, QUERY_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD, null, null); - int defaultContactsAccountState = response.getInt(KEY_DEFAULT_ACCOUNT_STATE, -1); - if (defaultContactsAccountState - == DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_CLOUD) { + int defaultAccountState = response.getInt(KEY_DEFAULT_ACCOUNT_STATE, -1); + if (DefaultAccountAndState.isCloudOrSimAccount(defaultAccountState)) { String accountName = response.getString(Settings.ACCOUNT_NAME); String accountType = response.getString(Settings.ACCOUNT_TYPE); if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { throw new IllegalStateException( "account name and type cannot be null or empty"); } - return new DefaultAccountAndState( - DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_CLOUD, + return new DefaultAccountAndState(defaultAccountState, new Account(accountName, accountType)); - } else if (defaultContactsAccountState - == DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_LOCAL - || defaultContactsAccountState + } else if (defaultAccountState == DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_LOCAL + || defaultAccountState == DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_NOT_SET) { - return new DefaultAccountAndState(defaultContactsAccountState, /*cloudAccount=*/ + return new DefaultAccountAndState(defaultAccountState, /*account=*/ null); } else { throw new IllegalStateException("Invalid default account state"); @@ -3346,16 +3349,197 @@ public final class ContactsContract { Bundle extras = new Bundle(); extras.putInt(KEY_DEFAULT_ACCOUNT_STATE, defaultAccountAndState.getState()); - if (defaultAccountAndState.getState() - == DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_CLOUD) { - Account cloudAccount = defaultAccountAndState.getAccount(); - assert cloudAccount != null; - extras.putString(Settings.ACCOUNT_NAME, cloudAccount.name); - extras.putString(Settings.ACCOUNT_TYPE, cloudAccount.type); + if (DefaultAccountAndState.isCloudOrSimAccount(defaultAccountAndState.getState())) { + Account account = defaultAccountAndState.getAccount(); + assert account != null; + extras.putString(Settings.ACCOUNT_NAME, account.name); + extras.putString(Settings.ACCOUNT_TYPE, account.type); } nullSafeCall(resolver, ContactsContract.AUTHORITY_URI, SET_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD, null, extras); } + + /** + * Get a list of cloud accounts that is eligible to set as default account with state of + * {@link DefaultAccountAndState#DEFAULT_ACCOUNT_STATE_CLOUD}. May be empty but never + * null. + * + * @param resolver content resolver to query. + * @return a of cloud accounts that is eligible to set as default account with state of + * {@link DefaultAccountAndState#DEFAULT_ACCOUNT_STATE_CLOUD}. + * @throws RuntimeException if the query fails. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS) + @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED) + @SystemApi + public static @NonNull List getEligibleCloudAccounts( + @NonNull ContentResolver resolver) { + Bundle response = nullSafeCall(resolver, ContactsContract.AUTHORITY_URI, + QUERY_ELIGIBLE_DEFAULT_ACCOUNTS_METHOD, null, null); + List result = response.getParcelableArrayList( + KEY_ELIGIBLE_DEFAULT_ACCOUNTS, Account.class); + if (result == null) { + return new ArrayList<>(); + } + return result; + } + + + + /** + * The method to invoke to move local {@link RawContacts} and {@link Groups} from local + * account(s) to the Cloud Default Account (if any). + * + * @hide + */ + public static final String MOVE_LOCAL_CONTACTS_TO_CLOUD_DEFAULT_ACCOUNT_METHOD = + "moveLocalContactsToCloudDefaultAccount"; + + /** + * Move {@link RawContacts} and {@link Groups} (if any) from the local account to the + * Cloud Default Account (if any). + * @param resolver the ContentResolver to query. + * @throws RuntimeException if it fails to move contacts to the default account. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED) + @RequiresPermission(allOf = {android.Manifest.permission.WRITE_CONTACTS, + android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) + public static void moveLocalContactsToCloudDefaultAccount( + @NonNull ContentResolver resolver) { + + Bundle extras = new Bundle(); + Bundle result = nullSafeCall( + resolver, + ContactsContract.AUTHORITY_URI, + MOVE_LOCAL_CONTACTS_TO_CLOUD_DEFAULT_ACCOUNT_METHOD, + null, + extras); + } + + /** + * The method to invoke to move {@link RawContacts} and {@link Groups} from SIM + * account(s) to the Cloud Default Account (if any). + * + * @hide + */ + public static final String MOVE_SIM_CONTACTS_TO_CLOUD_DEFAULT_ACCOUNT_METHOD = + "moveSimContactsToCloudDefaultAccount"; + + /** + * Move {@link RawContacts} and {@link Groups} (if any) from the local account to the + * Cloud Default Account (if any). + * @param resolver the ContentResolver to query. + * @throws RuntimeException if it fails to move contacts to the default account. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED) + @RequiresPermission(allOf = {android.Manifest.permission.WRITE_CONTACTS, + android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) + public static void moveSimContactsToCloudDefaultAccount( + @NonNull ContentResolver resolver) { + Bundle result = nullSafeCall( + resolver, + ContactsContract.AUTHORITY_URI, + MOVE_SIM_CONTACTS_TO_CLOUD_DEFAULT_ACCOUNT_METHOD, + /* arg= */ null, + /* extras= */ null); + } + + /** + * The method to invoke to get the number of {@link RawContacts} that are in local + * account(s) and movable to the Cloud Default Account (if any). + * + * @hide + */ + public static final String GET_NUMBER_OF_MOVABLE_LOCAL_CONTACTS_METHOD = + "getNumberOfMovableLocalContacts"; + + /** + * The result key for moving local {@link RawContacts} and {@link Groups} from SIM + * account(s) to the Cloud Default Account (if any). + * + * @hide + */ + public static final String KEY_NUMBER_OF_MOVABLE_LOCAL_CONTACTS = + "key_number_of_movable_local_contacts"; + + /** + * Gets the number of {@link RawContacts} in the local account(s) which may be moved + * using {@link DefaultAccount#moveLocalContactsToCloudDefaultAccount} (if any). + * @param resolver the ContentResolver to query. + * @return the number of {@link RawContacts} in the local account(s), or 0 if there is + * no Cloud Default Account. + * @throws RuntimeException if it fails get the number of movable local contacts. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED) + @RequiresPermission(allOf = {android.Manifest.permission.READ_CONTACTS, + android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) + public static int getNumberOfMovableLocalContacts( + @NonNull ContentResolver resolver) { + Bundle result = nullSafeCall( + resolver, + ContactsContract.AUTHORITY_URI, + GET_NUMBER_OF_MOVABLE_LOCAL_CONTACTS_METHOD, + /* arg= */ null, + /* extras= */ null); + return result.getInt(KEY_NUMBER_OF_MOVABLE_LOCAL_CONTACTS, + /* defaultValue= */ 0); + } + + /** + * The method to invoke to get the number of {@link RawContacts} that are in SIM + * account(s) and movable to the Cloud Default Account (if any). + * + * @hide + */ + public static final String GET_NUMBER_OF_MOVABLE_SIM_CONTACTS_METHOD = + "getNumberOfMovableSimContacts"; + + /** + * The result key for moving local {@link RawContacts} and {@link Groups} from SIM + * account(s) to the Cloud Default Account (if any). + * + * @hide + */ + public static final String KEY_NUMBER_OF_MOVABLE_SIM_CONTACTS = + "key_number_of_movable_sim_contacts"; + + /** + * Gets the number of {@link RawContacts} in the SIM account(s) which may be moved using + * {@link DefaultAccount#moveSimContactsToCloudDefaultAccount} (if any). + * @param resolver the ContentResolver to query. + * @return the number of {@link RawContacts} in the SIM account(s), or 0 if there is + * no Cloud Default Account. + * @throws RuntimeException if it fails get the number of movable sim contacts. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED) + @RequiresPermission(allOf = {android.Manifest.permission.READ_CONTACTS, + android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) + public static int getNumberOfMovableSimContacts( + @NonNull ContentResolver resolver) { + Bundle result = nullSafeCall( + resolver, + ContactsContract.AUTHORITY_URI, + GET_NUMBER_OF_MOVABLE_SIM_CONTACTS_METHOD, + /* arg= */ null, + /* extras= */ null); + return result.getInt(KEY_NUMBER_OF_MOVABLE_SIM_CONTACTS, + /* defaultValue= */ 0); + } + } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1a15d09c0a5034d929a815e6e7688489efb253bd..594005c3ebd6eb3df1a3eccc8da9bedbf33b1fa0 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2351,6 +2351,11 @@ public final class Settings { /** * Activity Action: Show the permission screen for allowing apps to post promoted notifications. + * Properly formatted priority notifications are elevated in appearance. For example they may be + * able to use colors, have richer progress bars, show as chips in the status bar, and/or + * permanently appear on always-on-displays. This functionality is intended to be reserved for + * user initiated ongoing activities like navigation, phone calls, and ride sharing. + * *

        * Input: {@link #EXTRA_APP_PACKAGE}, the package to display. *

        @@ -6204,6 +6209,25 @@ public final class Settings { */ public static final String TOUCHPAD_RIGHT_CLICK_ZONE = "touchpad_right_click_zone"; + /** + * Whether to enable reversed vertical scrolling for connected mice. + * + * When enabled, scrolling down on the mouse wheel will move the screen up and vice versa. + * @hide + */ + public static final String MOUSE_REVERSE_VERTICAL_SCROLLING = + "mouse_reverse_vertical_scrolling"; + + /** + * Whether to enable swapping the primary button for connected mice. + * + * When enabled, right clicking will be the primary button and left clicking will be the + * secondary button (e.g. show menu). + * @hide + */ + public static final String MOUSE_SWAP_PRIMARY_BUTTON = + "mouse_swap_primary_button"; + /** * Pointer fill style, specified by * {@link android.view.PointerIcon.PointerIconVectorStyleFill} constants. @@ -6442,6 +6466,8 @@ public final class Settings { PRIVATE_SETTINGS.add(SCREEN_FLASH_NOTIFICATION); PRIVATE_SETTINGS.add(SCREEN_FLASH_NOTIFICATION_COLOR); PRIVATE_SETTINGS.add(DEFAULT_DEVICE_FONT_SCALE); + PRIVATE_SETTINGS.add(MOUSE_REVERSE_VERTICAL_SCROLLING); + PRIVATE_SETTINGS.add(MOUSE_SWAP_PRIMARY_BUTTON); } /** diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java index f123a962def96708216fb1634a7b1ce876c17629..3181556eded791bce24438717f7d6e85b699ed6b 100644 --- a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java +++ b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java @@ -129,6 +129,16 @@ public abstract class OnDeviceSandboxedInferenceService extends Service { * @hide */ public static final String MODEL_UNLOADED_BUNDLE_KEY = "model_unloaded"; + /** + * @hide + */ + public static final String MODEL_LOADED_BROADCAST_INTENT = + "android.service.ondeviceintelligence.MODEL_LOADED"; + /** + * @hide + */ + public static final String MODEL_UNLOADED_BROADCAST_INTENT = + "android.service.ondeviceintelligence.MODEL_UNLOADED"; /** * @hide diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index 5ac0c50a312ed6a2fe84c855df0b1efad7191f34..e8ef9d65a2b49aa97b2d94c3a0bc09fb30d17358 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -1671,14 +1671,22 @@ public class PhoneStateListener { } /** @hide */ - public final void onCallBackModeStarted( - @TelephonyManager.EmergencyCallbackModeType int type) { + public final void onCallbackModeStarted( + @TelephonyManager.EmergencyCallbackModeType int type, long durationMillis, + int subId) { // not support. Can't override. Use TelephonyCallback. } /** @hide */ - public final void onCallBackModeStopped(@EmergencyCallbackModeType int type, - @EmergencyCallbackModeStopReason int reason) { + public final void onCallbackModeRestarted( + @TelephonyManager.EmergencyCallbackModeType int type, long durationMillis, + int subId) { + // not support. Can't override. Use TelephonyCallback. + } + + /** @hide */ + public final void onCallbackModeStopped(@EmergencyCallbackModeType int type, + @EmergencyCallbackModeStopReason int reason, int subId) { // not support. Can't override. Use TelephonyCallback. } diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java index c360e64c8c1a9f369fb1c47b24c47f2622c53e76..14d5800e4db718e2b890770edb70bbfb55da4b30 100644 --- a/core/java/android/telephony/TelephonyCallback.java +++ b/core/java/android/telephony/TelephonyCallback.java @@ -41,6 +41,7 @@ import dalvik.system.VMRuntime; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; +import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -619,16 +620,20 @@ public class TelephonyCallback { /** - * Event for changes to the Emergency callback mode + * Event for changes to the emergency callback mode * *

        Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} * - * @see EmergencyCallbackModeListener#onCallbackModeStarted(int) - * @see EmergencyCallbackModeListener#onCallbackModeStopped(int, int) + * @see EmergencyCallbackModeListener#onCallbackModeStarted(int, Duration, int) + * @see EmergencyCallbackModeListener#onCallbackModeRestarted(int, Duration, int) + * @see EmergencyCallbackModeListener#onCallbackModeStopped(int, int, int) * * @hide */ + + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi public static final int EVENT_EMERGENCY_CALLBACK_MODE_CHANGED = 40; /** @@ -1671,39 +1676,64 @@ public class TelephonyCallback { } /** - * Interface for emergency callback mode listener. + * Interface for the emergency callback mode listener. * * @hide */ + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + @SystemApi public interface EmergencyCallbackModeListener { /** - * Indicates that Callback Mode has been started. + * Indicates that emergency callback mode has been started. *

        - * This method will be called when an emergency sms/emergency call is sent - * and the callback mode is supported by the carrier. - * If an emergency SMS is transmitted during callback mode for SMS, this API will be called - * once again with TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS. + * This method will be called when an emergency SMS or emergency call is ended and + * the emergency callback mode is supported by the carrier. + * If the emergency callback mode was started for an emergency call and an emergency SMS is + * transmitted during callback mode for SMS then this API will be called once again with + * TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS. * - * @param type for callback mode entry + * @param type for the emergency callback mode entry * See {@link TelephonyManager.EmergencyCallbackModeType}. * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS + * + * @param timerDuration is the time remaining in the emergency callback mode. + * @param subId The subscription ID used to start the emergency callback mode. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - void onCallBackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type); + void onCallbackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type, + @NonNull Duration timerDuration, int subId); /** - * Indicates that Callback Mode has been stopped. + * Indicates that emergency callback mode has been re-started. *

        - * This method will be called when the callback mode timer expires or when - * a normal call/SMS is sent + * This method will be called when an emergency SMS or emergency call is ended + * in the emergency callback mode. + * This is used to restart the emergency callback mode when it is already in progress. * - * @param type for callback mode entry + * @param type for the emergency callback mode entry + * See {@link TelephonyManager.EmergencyCallbackModeType}. * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS * - * @param reason for changing callback mode + * @param timerDuration is the time remaining in the emergency callback mode. + * @param subId The subscription ID used to restart the emergency callback mode. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + void onCallbackModeRestarted(@TelephonyManager.EmergencyCallbackModeType int type, + @NonNull Duration timerDuration, int subId); + + /** + * Indicates that emergency callback mode has been stopped. + *

        + * This method will be called when the emergency callback mode timer expires or when + * a normal call/SMS is sent + * + * @param type for the emergency callback mode entry + * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL + * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS * + * @param reason for changing emergency callback mode * @see TelephonyManager#STOP_REASON_UNKNOWN * @see TelephonyManager#STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED * @see TelephonyManager#STOP_REASON_NORMAL_SMS_SENT @@ -1711,10 +1741,12 @@ public class TelephonyCallback { * @see TelephonyManager#STOP_REASON_EMERGENCY_SMS_SENT * @see TelephonyManager#STOP_REASON_TIMER_EXPIRED * @see TelephonyManager#STOP_REASON_USER_ACTION + * + * @param subId is the current subscription used the emergency callback mode. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - void onCallBackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type, - @TelephonyManager.EmergencyCallbackModeStopReason int reason); + void onCallbackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type, + @TelephonyManager.EmergencyCallbackModeStopReason int reason, int subId); } /** @@ -2132,18 +2164,43 @@ public class TelephonyCallback { mediaQualityStatus))); } - public void onCallBackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type) { + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onCallbackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type, + long durationMillis, int subId) { + if (!Flags.emergencyCallbackModeNotification()) return; + EmergencyCallbackModeListener listener = (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get(); Log.d(LOG_TAG, "onCallBackModeStarted:type=" + type + ", listener=" + listener); if (listener == null) return; + final Duration timerDuration = Duration.ofMillis(durationMillis); Binder.withCleanCallingIdentity( - () -> mExecutor.execute(() -> listener.onCallBackModeStarted(type))); + () -> mExecutor.execute(() -> listener.onCallbackModeStarted(type, + timerDuration, subId))); } - public void onCallBackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type, - @TelephonyManager.EmergencyCallbackModeStopReason int reason) { + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onCallbackModeRestarted(@TelephonyManager.EmergencyCallbackModeType int type, + long durationMillis, int subId) { + if (!Flags.emergencyCallbackModeNotification()) return; + + EmergencyCallbackModeListener listener = + (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get(); + Log.d(LOG_TAG, "onCallbackModeRestarted:type=" + type + ", listener=" + listener); + if (listener == null) return; + + final Duration timerDuration = Duration.ofMillis(durationMillis); + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onCallbackModeRestarted(type, + timerDuration, subId))); + } + + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onCallbackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type, + @TelephonyManager.EmergencyCallbackModeStopReason int reason, int subId) { + if (!Flags.emergencyCallbackModeNotification()) return; + EmergencyCallbackModeListener listener = (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get(); Log.d(LOG_TAG, "onCallBackModeStopped:type=" + type @@ -2151,7 +2208,8 @@ public class TelephonyCallback { if (listener == null) return; Binder.withCleanCallingIdentity( - () -> mExecutor.execute(() -> listener.onCallBackModeStopped(type, reason))); + () -> mExecutor.execute(() -> listener.onCallbackModeStopped(type, reason, + subId))); } public void onCarrierRoamingNtnModeChanged(boolean active) { diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 10f03c15310c1b26051302fe4f25ada27b87df76..3c7e924f07dfc63c4c3a9d468113828bded2404f 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -115,7 +115,6 @@ public class TelephonyRegistryManager { ICarrierConfigChangeListener> mCarrierConfigChangeListenerMap = new ConcurrentHashMap<>(); - /** @hide **/ public TelephonyRegistryManager(@NonNull Context context) { mContext = context; @@ -1721,13 +1720,36 @@ public class TelephonyRegistryManager { * @param subId Sender subscription ID. * @param type for callback mode entry. * See {@link TelephonyManager.EmergencyCallbackModeType}. + * @param durationMillis is the number of milliseconds remaining in the emergency callback + * mode. + * @hide + */ + public void notifyCallbackModeStarted(int phoneId, int subId, + @TelephonyManager.EmergencyCallbackModeType int type, long durationMillis) { + try { + Log.d(TAG, "notifyCallbackModeStarted:type=" + type); + sRegistry.notifyCallbackModeStarted(phoneId, subId, type, durationMillis); + } catch (RemoteException ex) { + // system process is dead + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Notify Callback Mode has been restarted. + * @param phoneId Sender phone ID. + * @param subId Sender subscription ID. + * @param type for callback mode entry. + * See {@link TelephonyManager.EmergencyCallbackModeType}. + * @param durationMillis is the number of milliseconds remaining in the emergency callback + * mode. * @hide */ - public void notifyCallBackModeStarted(int phoneId, int subId, - @TelephonyManager.EmergencyCallbackModeType int type) { + public void notifyCallbackModeRestarted(int phoneId, int subId, + @TelephonyManager.EmergencyCallbackModeType int type, long durationMillis) { try { - Log.d(TAG, "notifyCallBackModeStarted:type=" + type); - sRegistry.notifyCallbackModeStarted(phoneId, subId, type); + Log.d(TAG, "notifyCallbackModeRestarted:type=" + type); + sRegistry.notifyCallbackModeRestarted(phoneId, subId, type, durationMillis); } catch (RemoteException ex) { // system process is dead throw ex.rethrowFromSystemServer(); diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig index ec5b488ccbbdf058c51fe3933e866bfcbfd3ce9b..09b2201efb4e64f6418d882981ea7e6a8a8a5481 100644 --- a/core/java/android/text/flags/flags.aconfig +++ b/core/java/android/text/flags/flags.aconfig @@ -188,3 +188,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "tts_span_duration" + namespace: "text" + description: "Feature flag for adding a TYPE_DURATION to TtsSpan" + bug: "337103893" +} diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java index f9a1a0d36d302ac0449d7a024a583ba86805f53c..b7b8f0b1b5d8bf3126d4b843b8c3df845d919431 100644 --- a/core/java/android/text/style/TtsSpan.java +++ b/core/java/android/text/style/TtsSpan.java @@ -16,6 +16,10 @@ package android.text.style; +import static com.android.text.flags.Flags.FLAG_TTS_SPAN_DURATION; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; import android.os.Parcel; import android.os.PersistableBundle; import android.text.ParcelableSpan; @@ -111,6 +115,16 @@ public class TtsSpan implements ParcelableSpan { */ public static final String TYPE_TIME = "android.type.time"; + /** + * The text associated with this span is a duration, consisting of a number of + * hours, minutes, and seconds specified with {@link #ARG_HOURS}, + * {@link #ARG_MINUTES}, and {@link #ARG_SECONDS}. This is different from {@link #TYPE_TIME}. + * This should be used to convey an interval of time, while {@link #TYPE_TIME} should be used to + * convey a particular moment in time, such as a clock time. + */ + @FlaggedApi(FLAG_TTS_SPAN_DURATION) + public static final String TYPE_DURATION = "android.type.duration"; + /** * The text associated with this span is a date. At least one of the * arguments {@link #ARG_MONTH} and {@link #ARG_YEAR} has to be provided. @@ -302,12 +316,20 @@ public class TtsSpan implements ParcelableSpan { public static final String ARG_HOURS = "android.arg.hours"; /** - * Argument used to specify the minutes of a time. The hours should be + * Argument used to specify the minutes of a time. The minutes should be * provided as an integer in the range from 0 up to and including 59. * Can be used with {@link #TYPE_TIME}. */ public static final String ARG_MINUTES = "android.arg.minutes"; + /** + * Argument used to specify the seconds of a time or duration. The seconds should be + * provided as an integer in the range from 0 up to and including 59. + * Can be used with {@link #TYPE_TIME} or {@link #TYPE_DURATION}. + */ + @FlaggedApi(FLAG_TTS_SPAN_DURATION) + public static final String ARG_SECONDS = "android.arg.seconds"; + /** * Argument used to specify the weekday of a date. The value should be * provided as an integer and can be any of {@link #WEEKDAY_SUNDAY}, @@ -1132,8 +1154,69 @@ public class TtsSpan implements ParcelableSpan { public TimeBuilder setMinutes(int minutes) { return setIntArgument(TtsSpan.ARG_MINUTES, minutes); } + + /** + * Sets the {@link #ARG_SECONDS} argument. + * @param seconds The value to be set for seconds. + * @return This instance. + */ + @FlaggedApi(FLAG_TTS_SPAN_DURATION) + @NonNull + public TimeBuilder setSeconds(int seconds) { + return setIntArgument(TtsSpan.ARG_SECONDS, seconds); + } } + /** + * A builder for TtsSpans of type {@link #TYPE_DURATION}. + */ + @FlaggedApi(FLAG_TTS_SPAN_DURATION) + public static class DurationBuilder + extends SemioticClassBuilder { + + /** + * Creates a builder for a TtsSpan of type {@link #TYPE_DURATION}. + */ + @FlaggedApi(FLAG_TTS_SPAN_DURATION) + public DurationBuilder() { + super(TtsSpan.TYPE_DURATION); + } + + /** + * Sets the {@link #ARG_HOURS} argument. + * @param hours The value to be set for hours. + * @return This instance. + */ + @FlaggedApi(FLAG_TTS_SPAN_DURATION) + @NonNull + public DurationBuilder setHours(int hours) { + return setIntArgument(TtsSpan.ARG_HOURS, hours); + } + + /** + * Sets the {@link #ARG_MINUTES} argument. + * @param minutes The value to be set for minutes. + * @return This instance. + */ + @FlaggedApi(FLAG_TTS_SPAN_DURATION) + @NonNull + public DurationBuilder setMinutes(int minutes) { + return setIntArgument(TtsSpan.ARG_MINUTES, minutes); + } + + /** + * Sets the {@link #ARG_SECONDS} argument. + * @param seconds The value to be set for seconds. + * @return This instance. + */ + @FlaggedApi(FLAG_TTS_SPAN_DURATION) + @NonNull + public DurationBuilder setSeconds(int seconds) { + return setIntArgument(TtsSpan.ARG_SECONDS, seconds); + } + } + + /** * A builder for TtsSpans of type {@link #TYPE_DATE}. */ diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 53935e8109133e57e716da0c6fac122cb2f0dd43..8a8022c0206a321e931060ec23691fa97aea6f2e 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -448,6 +448,13 @@ public final class Display { @TestApi public static final int TYPE_VIRTUAL = 5; + /** + * The maximum display type value. + * Helpful to get all possible display values. + * @hide + */ + public static final int TYPE_MAX = TYPE_VIRTUAL; + /** * Display state: The display state is unknown. * diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index 229e8ee75844a53d2c4fd627667b4e8a6423bbc7..4f741982081b992344ad28539cb97898fc0ed97c 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -221,13 +221,13 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { @Override public boolean setControl(@Nullable InsetsSourceControl control, int[] showTypes, - int[] hideTypes) { + int[] hideTypes, int[] cancelTypes) { if (Flags.refactorInsetsController()) { - return super.setControl(control, showTypes, hideTypes); + return super.setControl(control, showTypes, hideTypes, cancelTypes); } else { ImeTracing.getInstance().triggerClientDump("ImeInsetsSourceConsumer#setControl", mController.getHost().getInputMethodManager(), null /* icProto */); - if (!super.setControl(control, showTypes, hideTypes)) { + if (!super.setControl(control, showTypes, hideTypes, cancelTypes)) { return false; } if (control == null && !mIsRequestedVisibleAwaitingLeash) { diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index 97facc1ba4727f2844325adfb499c4015bf4c4c8..4fead2ad524665a35e9ad55765c84595fdaf9e7d 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -371,6 +371,7 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro mPendingInsets = mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN ? mShownInsets : mHiddenInsets; mPendingAlpha = 1f; + mPendingFraction = 1f; applyChangeInsets(null); mCancelled = true; mListener.onCancelled(mReadyDispatched ? this : null); @@ -486,6 +487,17 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro if (controls == null) { return; } + + final boolean visible = mPendingFraction == 0 + // The first frame of ANIMATION_TYPE_SHOW should be invisible since it is + // animated from the hidden state. + ? mAnimationType != ANIMATION_TYPE_SHOW + : mPendingFraction < 1f || (mFinished + ? mShownOnFinish + // If the animation is cancelled, mFinished and mShownOnFinish are not set. + // Here uses mLayoutInsetsDuringAnimation to decide if it should be visible. + : mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN); + // TODO: Implement behavior when inset spans over multiple types for (int i = controls.size() - 1; i >= 0; i--) { final InsetsSourceControl control = controls.valueAt(i); @@ -498,12 +510,6 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro } addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame); - // The first frame of ANIMATION_TYPE_SHOW should be invisible since it is animated from - // the hidden state. - final boolean visible = mPendingFraction == 0 - ? mAnimationType != ANIMATION_TYPE_SHOW - : !mFinished || mShownOnFinish; - if (outState != null && source != null) { outState.addSource(new InsetsSource(source) .setVisible(visible) diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 8ac5532493780c6ee46d36a544d652840b5481f9..d08873c56e6a1392ee4332aef051eeff5b94898e 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -957,6 +957,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation int consumedControlCount = 0; final @InsetsType int[] showTypes = new int[1]; final @InsetsType int[] hideTypes = new int[1]; + final @InsetsType int[] cancelTypes = new int[1]; ImeTracker.Token statsToken = null; // Ensure to update all existing source consumers @@ -982,7 +983,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // control may be null, but we still need to update the control to null if it got // revoked. - consumer.setControl(control, showTypes, hideTypes); + consumer.setControl(control, showTypes, hideTypes, cancelTypes); } // Ensure to create source consumers if not available yet. @@ -990,7 +991,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation for (int i = mTmpControlArray.size() - 1; i >= 0; i--) { final InsetsSourceControl control = mTmpControlArray.valueAt(i); getSourceConsumer(control.getId(), control.getType()) - .setControl(control, showTypes, hideTypes); + .setControl(control, showTypes, hideTypes, cancelTypes); } } @@ -1002,6 +1003,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } mTmpControlArray.clear(); + if (cancelTypes[0] != 0) { + cancelExistingControllers(cancelTypes[0]); + } + // Do not override any animations that the app started in the OnControllableInsetsChanged // listeners. int animatingTypes = invokeControllableInsetsChangedListeners(); @@ -2154,12 +2159,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation new InsetsSourceControl(ID_IME_CAPTION_BAR, captionBar(), null /* leash */, false /* initialVisible */, new Point(), Insets.NONE), - new int[1], new int[1]); + new int[1], new int[1], new int[1]); } else { mState.removeSource(ID_IME_CAPTION_BAR); InsetsSourceConsumer sourceConsumer = mSourceConsumers.get(ID_IME_CAPTION_BAR); if (sourceConsumer != null) { - sourceConsumer.setControl(null, new int[1], new int[1]); + sourceConsumer.setControl(null, new int[1], new int[1], new int[1]); } } mHost.notifyInsetsChanged(); diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index da788a78a95fca707c34c6158bbc979c25f8e28c..17f33c1af4ed11f27e37ca28f55432c78139eb1c 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -122,7 +122,7 @@ public class InsetsSourceConsumer { /** * Updates the control delivered from the server. - + * * @param showTypes An integer array with a single entry that determines which types a show * animation should be run after setting the control. * @param hideTypes An integer array with a single entry that determines which types a hide @@ -130,7 +130,7 @@ public class InsetsSourceConsumer { * @return Whether the control has changed from the server */ public boolean setControl(@Nullable InsetsSourceControl control, - @InsetsType int[] showTypes, @InsetsType int[] hideTypes) { + @InsetsType int[] showTypes, @InsetsType int[] hideTypes, int[] cancelTypes) { if (Objects.equals(mSourceControl, control)) { if (mSourceControl != null && mSourceControl != control) { mSourceControl.release(SurfaceControl::release); @@ -165,6 +165,12 @@ public class InsetsSourceConsumer { // Reset the applier to the default one which has the most lightweight implementation. setSurfaceParamsApplier(InsetsAnimationControlRunner.SurfaceParamsApplier.DEFAULT); } else { + if (lastControl != null && InsetsSource.getInsetSide(lastControl.getInsetsHint()) + != InsetsSource.getInsetSide(control.getInsetsHint())) { + // The source has been moved to a different side. The coordinates are stale. + // Canceling existing animation if there is any. + cancelTypes[0] |= mType; + } final boolean requestedVisible = isRequestedVisibleAwaitingControl(); final SurfaceControl oldLeash = lastControl != null ? lastControl.getLeash() : null; final SurfaceControl newLeash = control.getLeash(); diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 5c415165137eec100f77fa79a06b601dfcdf9db3..67050e01b5915a4bdda9fc7698146a2a4f551eb8 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -38,7 +38,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS; +import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 2748e3288d345adbdc558fbc3f0fd1fd79fdfdb5..19e244aa5981292372912818892aacf3da45fcc7 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -49,6 +49,7 @@ import android.gui.DropInputMode; import android.gui.StalledTransactionInfo; import android.gui.TrustedOverlay; import android.hardware.DataSpace; +import android.hardware.DisplayLuts; import android.hardware.HardwareBuffer; import android.hardware.OverlayProperties; import android.hardware.SyncFence; @@ -307,9 +308,9 @@ public final class SurfaceControl implements Parcelable { private static native StalledTransactionInfo nativeGetStalledTransactionInfo(int pid); private static native void nativeSetDesiredPresentTimeNanos(long transactionObj, long desiredPresentTimeNanos); - private static native void nativeSetFrameTimeline(long transactionObj, - long vsyncId); private static native void nativeNotifyShutdown(); + private static native void nativeSetLuts(long transactionObj, long nativeObject, + float[] buffers, int[] slots, int[] dimensions, int[] sizes, int[] samplingKeys); /** * Transforms that can be applied to buffers as they are displayed to a window. @@ -4399,6 +4400,17 @@ public final class SurfaceControl implements Parcelable { return this; } + /** @hide */ + public @NonNull Transaction setLuts(@NonNull SurfaceControl sc, + @NonNull DisplayLuts displayLuts) { + checkPreconditions(sc); + + nativeSetLuts(mNativeObject, sc.mNativeObject, displayLuts.getLutBuffers(), + displayLuts.getOffsets(), displayLuts.getLutDimensions(), + displayLuts.getLutSizes(), displayLuts.getLutSamplingKeys()); + return this; + } + /** * Sets the caching hint for the layer. By default, the caching hint is * {@link CACHING_ENABLED}. diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index f7745d14188e2b78c4a8b03ad44cc82bc442f92a..83b4971c86214e00b351bc89f75755c28c1421b1 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.flags.Flags.FLAG_SURFACE_VIEW_SET_COMPOSITION_ORDER; import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_OVERLAY_SUBLAYER; import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER; @@ -769,6 +770,36 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall return mCornerRadius; } + /** + * Controls the composition order of the SurfaceView. A non-negative composition order + * value indicates that the SurfaceView is composited on top of the parent window, while + * a negative composition order indicates that the SurfaceView is behind the parent + * window. A SurfaceView with a higher value appears above its peers with lower values. + * For SurfaceViews with the same composition order value, their relative order is + * undefined. + * + * @param compositionOrder the composition order of the surface view. + */ + @FlaggedApi(FLAG_SURFACE_VIEW_SET_COMPOSITION_ORDER) + public void setCompositionOrder(int compositionOrder) { + mRequestedSubLayer = compositionOrder; + if (mSubLayer != mRequestedSubLayer) { + updateSurface(); + } + } + + /** + * Returns the composition order of the SurfaceView. + * + * @return composition order of the SurfaceView. + * + * @see #setCompositionOrder(int) + */ + @FlaggedApi(FLAG_SURFACE_VIEW_SET_COMPOSITION_ORDER) + public int getCompositionOrder() { + return mRequestedSubLayer; + } + /** * Control whether the surface view's surface is placed on top of another * regular surface view in the window (but still behind the window itself). @@ -1257,7 +1288,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall final Transaction surfaceUpdateTransaction = new Transaction(); if (creating) { updateOpaqueFlag(); - final String name = "SurfaceView[" + viewRoot.getTitle().toString() + "]"; + final String name = Integer.toHexString(System.identityHashCode(this)) + + " SurfaceView[" + viewRoot.getTitle().toString() + "]"; createBlastSurfaceControls(viewRoot, name, surfaceUpdateTransaction); } else if (mSurfaceControl == null) { return; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 0ca442d66e6f8dc1194ca688b13862ab635d4738..b921213cc26ccd35121b305c83294ad939f5152d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -125,7 +125,7 @@ import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision; import static android.view.flags.Flags.toolkitSetFrameRateReadOnly; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER; -import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION; +import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme; @@ -284,6 +284,7 @@ import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.RootViewSurfaceTaker; import com.android.internal.view.SurfaceCallbackHelper; import com.android.modules.expresslog.Counter; +import com.android.os.coregraphics.HwuiStatsLog; import libcore.io.IoUtils; @@ -1228,6 +1229,8 @@ public final class ViewRootImpl implements ViewParent, // The latest input event from the gesture that was used to resolve the pointer icon. private MotionEvent mPointerIconEvent = null; + private @ActivityInfo.ColorMode int mCurrentColorMode = ActivityInfo.COLOR_MODE_DEFAULT; + private long mColorModeLastSetMillis = -1; public ViewRootImpl(Context context, Display display) { this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout()); @@ -2646,6 +2649,7 @@ public final class ViewRootImpl implements ViewParent, mFirstFramePresentedTimeNs = -1; } } + logColorMode(mCurrentColorMode, true); } @@ -6341,6 +6345,7 @@ public final class ViewRootImpl implements ViewParent, if (mAttachInfo.mThreadedRenderer == null) { return; } + boolean isHdr = colorMode == ActivityInfo.COLOR_MODE_HDR || colorMode == ActivityInfo.COLOR_MODE_HDR10; if (isHdr && !mDisplay.isHdrSdrRatioAvailable()) { @@ -6353,6 +6358,9 @@ public final class ViewRootImpl implements ViewParent, && !getConfiguration().isScreenWideColorGamut()) { colorMode = ActivityInfo.COLOR_MODE_DEFAULT; } + + logColorMode(colorMode, false); + float automaticRatio = mAttachInfo.mThreadedRenderer.setColorMode(colorMode); if (desiredRatio == 0 || desiredRatio > automaticRatio) { desiredRatio = automaticRatio; @@ -10005,6 +10013,7 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mThreadedRenderer = null; mAttachInfo.mHardwareAccelerated = false; + logColorMode(mCurrentColorMode, true); } } @@ -13346,4 +13355,27 @@ public final class ViewRootImpl implements ViewParent, mInfrequentUpdateCount = 0; } } + + private void logColorMode(@ActivityInfo.ColorMode int colorMode, boolean windowStopped) { + if (mColorModeLastSetMillis == -1 && windowStopped) { + Log.d(TAG, "Skipping stats log for color mode"); + return; + } + + long currentTimeMillis = System.currentTimeMillis(); + + if (windowStopped) { + HwuiStatsLog.write(HwuiStatsLog.HARDWARE_RENDERER_EVENT, Process.myUid(), + currentTimeMillis - mColorModeLastSetMillis, mCurrentColorMode); + mColorModeLastSetMillis = -1; + } else { + if (mColorModeLastSetMillis > 0) { + HwuiStatsLog.write(HwuiStatsLog.HARDWARE_RENDERER_EVENT, Process.myUid(), + currentTimeMillis - mColorModeLastSetMillis, mCurrentColorMode); + } + mColorModeLastSetMillis = currentTimeMillis; + } + + mCurrentColorMode = colorMode; + } } diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index a43906f30ff752476d777cd1073045dbf33460c5..dfac244fcc0dcfc3d03fe68d354b2466eab4bc49 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -16,6 +16,7 @@ package android.view.accessibility; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; @@ -784,6 +785,19 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par */ public static final int CONTENT_CHANGE_TYPE_ENABLED = 1 << 12; + /** + * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: + * The source node changed its checked state, which is returned by + * {@link AccessibilityNodeInfo#getChecked()}. + * The view changing its checked state should call + * {@link AccessibilityNodeInfo#setChecked(int)} and then send this event. + * + * @see AccessibilityNodeInfo#getChecked() + * @see AccessibilityNodeInfo#setChecked(int) + */ + @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED) + public static final int CONTENT_CHANGE_TYPE_CHECKED = 1 << 13; + // Speech state change types. /** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */ diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index fe6aafbd7e160b54fc4c76d57ee46d2c145ce6a4..c5ca059d1cea16237cce949df4b54d3242875539 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -860,6 +860,37 @@ public class AccessibilityNodeInfo implements Parcelable { public static final String EXTRA_DATA_REQUESTED_KEY = "android.view.accessibility.AccessibilityNodeInfo.extra_data_requested"; + // Tri-state checked states. + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "CHECKED_STATE" }, value = { + CHECKED_STATE_FALSE, + CHECKED_STATE_TRUE, + CHECKED_STATE_PARTIAL + }) + public @interface CheckedState {} + + /** + * This node is not checked. + */ + @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED) + public static final int CHECKED_STATE_FALSE = 0; + + /** + * This node is checked. + */ + @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED) + public static final int CHECKED_STATE_TRUE = 1; + + /** + * This node is partially checked. For example, + * when a checkbox owns a number of sub-options and they have + * different states, then the main checkbox is in a partially-checked state. + */ + @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED) + public static final int CHECKED_STATE_PARTIAL = 2; + // Boolean attributes. private static final int BOOLEAN_PROPERTY_CHECKABLE = 1 /* << 0 */; @@ -1038,6 +1069,10 @@ public class AccessibilityNodeInfo implements Parcelable { private IBinder mLeashedParent; private long mLeashedParentNodeId = UNDEFINED_NODE_ID; + // TODO(b/369951517) Initialize mChecked explicitly with + // the CHECKED_FALSE state when flagging is removed. + private int mChecked; + /** * Creates a new {@link AccessibilityNodeInfo}. */ @@ -2319,28 +2354,100 @@ public class AccessibilityNodeInfo implements Parcelable { } /** - * Gets whether this node is checked. + * Gets whether this node is checked. This is only meaningful + * when {@link #isCheckable()} returns {@code true}. + * + * @deprecated Use {@link #getChecked()} instead. * * @return True if the node is checked. */ + @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED) + @Deprecated public boolean isChecked() { return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED); } /** - * Sets whether this node is checked. + * Sets whether this node is checked. This is only meaningful + * when {@link #isCheckable()} returns {@code true}. *

        * Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *

        * + * @deprecated Use {@link #setChecked(int)} instead. + * * @param checked True if the node is checked. * * @throws IllegalStateException If called from an AccessibilityService. */ + @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED) + @Deprecated public void setChecked(boolean checked) { setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked); + if (Flags.triStateChecked()) { + mChecked = checked ? CHECKED_STATE_TRUE : CHECKED_STATE_FALSE; + } + } + + /** + * Gets the checked state of this node. This is only meaningful + * when {@link #isCheckable()} returns {@code true}. + * + * @see #setCheckable(boolean) + * @see #isCheckable() + * @see #setChecked(int) + * + * @return The checked state, one of: + *
          + *
        • {@link #CHECKED_STATE_FALSE} + *
        • {@link #CHECKED_STATE_TRUE} + *
        • {@link #CHECKED_STATE_PARTIAL} + *
        + */ + @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED) + public @CheckedState int getChecked() { + return mChecked; + } + + /** + * Sets the checked state of this node. This is only meaningful + * when {@link #isCheckable()} returns {@code true}. + *

        + * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + *

        + * + * @see #setCheckable(boolean) + * @see #isCheckable() + * @see #getChecked() + * + * @param checked The checked state. One of + *
          + *
        • {@link #CHECKED_STATE_FALSE} + *
        • {@link #CHECKED_STATE_TRUE} + *
        • {@link #CHECKED_STATE_PARTIAL} + *
        + * + * @throws IllegalStateException If called from an AccessibilityService. + * @throws IllegalArgumentException if checked is not one of {@link #CHECKED_STATE_FALSE}, + * {@link #CHECKED_STATE_TRUE}, or {@link #CHECKED_STATE_PARTIAL}. + */ + @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED) + public void setChecked(@CheckedState int checked) { + enforceNotSealed(); + switch (checked) { + case CHECKED_STATE_FALSE: + case CHECKED_STATE_TRUE: + case CHECKED_STATE_PARTIAL: + mChecked = checked; + break; + default: + throw new IllegalArgumentException("Unknown checked argument: " + checked); + } + setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked == CHECKED_STATE_TRUE); } /** @@ -4515,6 +4622,10 @@ public class AccessibilityNodeInfo implements Parcelable { if (mLeashedParentNodeId != DEFAULT.mLeashedParentNodeId) { nonDefaultFields |= bitAt(fieldIndex); } + fieldIndex++; + if (mChecked != DEFAULT.mChecked) { + nonDefaultFields |= bitAt(fieldIndex); + } int totalFields = fieldIndex; parcel.writeLong(nonDefaultFields); @@ -4683,6 +4794,9 @@ public class AccessibilityNodeInfo implements Parcelable { if (isBitSet(nonDefaultFields, fieldIndex++)) { parcel.writeLong(mLeashedParentNodeId); } + if (isBitSet(nonDefaultFields, fieldIndex++)) { + parcel.writeInt(mChecked); + } if (DEBUG) { fieldIndex--; @@ -4771,6 +4885,7 @@ public class AccessibilityNodeInfo implements Parcelable { mLeashedChild = other.mLeashedChild; mLeashedParent = other.mLeashedParent; mLeashedParentNodeId = other.mLeashedParentNodeId; + mChecked = other.mChecked; } private void initCopyInfos(AccessibilityNodeInfo other) { @@ -4960,6 +5075,9 @@ public class AccessibilityNodeInfo implements Parcelable { if (isBitSet(nonDefaultFields, fieldIndex++)) { mLeashedParentNodeId = parcel.readLong(); } + if (isBitSet(nonDefaultFields, fieldIndex++)) { + mChecked = parcel.readInt(); + } mSealed = sealed; } diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig index b9e97502cad7197dc4d0e76936e5738a8d03ab0b..8ffae845de1f11c9c988b21800cb28defe2284ad 100644 --- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -10,6 +10,13 @@ flag { bug: "362782536" } +flag { + name: "a11y_is_required_api" + namespace: "accessibility" + description: "Adds an API to indicate whether a form field (or similar element) is required." + bug: "362784403" +} + flag { name: "a11y_overlay_callbacks" is_exported: true @@ -210,3 +217,20 @@ flag { description: "Feature flag for declaring system pinch zoom opt-out apis" bug: "315089687" } + +flag { + name: "tri_state_checked" + namespace: "accessibility" + description: "Feature flag for adding tri-state checked api" + bug: "333784774" +} + +flag { + name: "warning_use_default_dialog_type" + namespace: "accessibility" + description: "Uses the default type for the A11yService warning dialog, instead of SYSTEM_ALERT_DIALOG" + bug: "336719951" + metadata { + purpose: PURPOSE_BUGFIX + } + } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 79ecfe1e9141b6846c5e73dcbea197fc1f3bff28..1a45939f65b626dd74bc527b7270f4d7c78138e7 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -117,6 +117,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import sun.misc.Cleaner; @@ -4914,7 +4916,22 @@ public final class AutofillManager { && (root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) == 0) { ViewNodeBuilder viewStructure = new ViewNodeBuilder(); viewStructure.setAutofillId(view.getAutofillId()); - view.onProvideAutofillStructure(viewStructure, /* flags= */ 0); + + // Post onProvideAutofillStructure to the UI thread + final CountDownLatch latch = new CountDownLatch(1); + afm.post( + () -> { + view.onProvideAutofillStructure(viewStructure, /* flags= */ 0); + latch.countDown(); + } + ); + try { + latch.await(5000, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return null; + } + // TODO(b/141703532): We don't call View#onProvideAutofillVirtualStructure for // efficiency reason. But this also means we will return null for virtual views // for now. We will add a new API to fetch the view node info of the virtual diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig index f570a9a50ebf2c7e8fd3604e7063fc39fa27ab95..1cf26ab64c09ed370769ce6f9ee5d0dd45d2f941 100644 --- a/core/java/android/view/flags/view_flags.aconfig +++ b/core/java/android/view/flags/view_flags.aconfig @@ -97,4 +97,23 @@ flag { description: "Disable Draw Wakelock starting U." bug: "331698645" is_fixed_read_only: true +} + +flag { + name: "calculate_bounds_in_parent_from_bounds_in_screen" + namespace: "accessibility" + description: "Calculate bounds in parent of each node in ViewStructure from its bounds set relative to screen and its parent's" + bug: "366131857" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "surface_view_set_composition_order" + namespace: "window_surfaces" + description: "Add a SurfaceView composition order control API." + bug: "341021569" + is_fixed_read_only: true } \ No newline at end of file diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 806a593346890e17d1d2aab14dca05c4f4b6029e..0a83bdc35b1f1e6d01291ed26f7c8c6c455ebaff 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -860,7 +860,7 @@ public final class InputMethodInfo implements Parcelable { *

        e.g.

        startActivity(createStylusHandwritingSettingsActivityIntent());
              * 

        * - * @attr ref R.styleable#InputMethod_stylusHandwritingSettingsActivity + * @attr ref android.R.styleable#InputMethod_stylusHandwritingSettingsActivity * @see #getSettingsActivity() * @see #supportsStylusHandwriting() */ @@ -888,8 +888,8 @@ public final class InputMethodInfo implements Parcelable { * the IME language settings activity.

        *

        e.g.

        startActivity(createImeLanguageSettingsActivityIntent());

        * - * @attr ref R.styleable#InputMethod_languageSettingsActivity - * @attr ref R.styleable#InputMethod_settingsActivity + * @attr ref android.R.styleable#InputMethod_languageSettingsActivity + * @attr ref android.R.styleable#InputMethod_settingsActivity */ @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API) @Nullable diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 7366b9a443c3b96ed57de25305aeb577dc99f62c..ab5969bb381c9aeee69164bf09cac0a7eac19715 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -16,10 +16,12 @@ package android.webkit; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -150,6 +152,24 @@ public abstract class WebSettings { @SystemApi public static final long ENABLE_SIMPLIFIED_DARK_MODE = 214741472L; + /** + * Enable User-Agent Reduction for webview. + * The OS, CPU, and Build information in the default User-Agent will be + * reduced to the static "Linux; Android 10; K" string. + * Minor/build/patch version information in the default User-Agent is + * reduced to "0.0.0". The rest of the default User-Agent remains unchanged. + * + * See https://developers.google.com/privacy-sandbox/protections/user-agent + * for details related to User-Agent Reduction. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) + @FlaggedApi(android.webkit.Flags.FLAG_USER_AGENT_REDUCTION) + @SystemApi + public static final long ENABLE_USER_AGENT_REDUCTION = 371034303L; + /** * Default cache usage mode. If the navigation type doesn't impose any * specific behavior, use cached resources when they are available diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig index b21a490cc506f353f9a1df496018725b2c10f528..d1013a92476f265a3f6ea0ba9f4e5db1c9a59273 100644 --- a/core/java/android/webkit/flags.aconfig +++ b/core/java/android/webkit/flags.aconfig @@ -18,3 +18,11 @@ flag { bug: "310653407" is_fixed_read_only: true } + +flag { + name: "user_agent_reduction" + is_exported: true + namespace: "webview" + description: "New feature reduce user-agent for webview" + bug: "371034303" +} diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 510a92d1b2ca85e6152a40c5ea97ba9ec55d7f96..3f611c7efbdd5ac39d0e9a1a1a644c24d927bb97 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -3636,7 +3636,7 @@ public class ListView extends AbsListView { * Returns the drawable that will be drawn between each item in the list. * * @return the current drawable drawn between list elements - * @attr ref R.styleable#ListView_divider + * @attr ref android.R.styleable#ListView_divider */ @InspectableProperty @Nullable @@ -3651,7 +3651,7 @@ public class ListView extends AbsListView { * height, you should also call {@link #setDividerHeight(int)}. * * @param divider the drawable to use - * @attr ref R.styleable#ListView_divider + * @attr ref android.R.styleable#ListView_divider */ public void setDivider(@Nullable Drawable divider) { if (divider != null) { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 06820cd4c2ce8c7c31acb21e1597064e4598e0e7..d7b5211ad9fd08263640d24f26ca9fa567ea8c86 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -46,6 +46,7 @@ import android.app.LoadedApk; import android.app.PendingIntent; import android.app.RemoteInput; import android.appwidget.AppWidgetHostView; +import android.appwidget.flags.Flags; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -4119,6 +4120,71 @@ public class RemoteViews implements Parcelable, Filter { public void visitUris(@NonNull Consumer visitor) { mNestedViews.visitUris(visitor); } + + @Override + public boolean canWriteToProto() { + return true; + } + + @Override + public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) { + if (!Flags.remoteViewsProto()) return; + final long token = out.start(RemoteViewsProto.Action.VIEW_GROUP_ADD_ACTION); + out.write(RemoteViewsProto.ViewGroupAddAction.VIEW_ID, + appResources.getResourceName(mViewId)); + out.write(RemoteViewsProto.ViewGroupAddAction.INDEX, mIndex); + out.write(RemoteViewsProto.ViewGroupAddAction.STABLE_ID, mStableId); + long rvToken = out.start(RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS); + mNestedViews.writePreviewToProto(context, out); + out.end(rvToken); + out.end(token); + } + } + + private PendingResources createViewGroupActionAddFromProto(ProtoInputStream in) + throws Exception { + final LongSparseArray values = new LongSparseArray<>(); + + final long token = in.start(RemoteViewsProto.Action.VIEW_GROUP_ADD_ACTION); + while (in.nextField() != NO_MORE_FIELDS) { + switch (in.getFieldNumber()) { + case (int) RemoteViewsProto.ViewGroupAddAction.VIEW_ID: + values.put(RemoteViewsProto.ViewGroupAddAction.VIEW_ID, + in.readString(RemoteViewsProto.ViewGroupAddAction.VIEW_ID)); + break; + case (int) RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS: + final long nvToken = in.start(RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS); + values.put(RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS, + createFromProto(in)); + in.end(nvToken); + break; + case (int) RemoteViewsProto.ViewGroupAddAction.INDEX: + values.put(RemoteViewsProto.ViewGroupAddAction.INDEX, + in.readInt(RemoteViewsProto.ViewGroupAddAction.INDEX)); + break; + case (int) RemoteViewsProto.ViewGroupAddAction.STABLE_ID: + values.put(RemoteViewsProto.ViewGroupAddAction.STABLE_ID, + in.readInt(RemoteViewsProto.ViewGroupAddAction.STABLE_ID)); + break; + default: + Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + + ProtoUtils.currentFieldToString(in)); + } + } + in.end(token); + + checkContainsKeys(values, new long[]{RemoteViewsProto.ViewGroupAddAction.VIEW_ID, + RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS}); + + return (context, resources, rootData, depth) -> { + int viewId = getAsIdentifier(resources, values, + RemoteViewsProto.ViewGroupAddAction.VIEW_ID); + return new ViewGroupActionAdd(viewId, ((PendingResources) values.get( + RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS)).create(context, resources, + rootData, depth), + (int) values.get(RemoteViewsProto.ViewGroupAddAction.INDEX, 0), + (int) values.get(RemoteViewsProto.ViewGroupAddAction.STABLE_ID, 0)); + }; } /** @@ -4241,6 +4307,60 @@ public class RemoteViews implements Parcelable, Filter { public int mergeBehavior() { return MERGE_APPEND; } + + @Override + public boolean canWriteToProto() { + return true; + } + + @Override + public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) { + final long token = out.start(RemoteViewsProto.Action.VIEW_GROUP_REMOVE_ACTION); + out.write(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID, + appResources.getResourceName(mViewId)); + if (mViewIdToKeep != REMOVE_ALL_VIEWS_ID) { + out.write(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP, + appResources.getResourceName(mViewIdToKeep)); + } + out.end(token); + } + + public static PendingResources createFromProto(ProtoInputStream in) + throws Exception { + final LongSparseArray values = new LongSparseArray<>(); + + final long token = in.start(RemoteViewsProto.Action.VIEW_GROUP_REMOVE_ACTION); + while (in.nextField() != NO_MORE_FIELDS) { + switch (in.getFieldNumber()) { + case (int) RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID: + values.put(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID, + in.readString(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID)); + break; + case (int) RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP: + values.put(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP, + in.readString( + RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP)); + break; + default: + Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + + ProtoUtils.currentFieldToString(in)); + } + } + in.end(token); + + checkContainsKeys(values, new long[]{RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID}); + + return (context, resources, rootData, depth) -> { + int viewId = getAsIdentifier(resources, values, + RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID); + int viewIdToKeep = (values.indexOfKey( + RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP) >= 0) + ? getAsIdentifier(resources, values, + RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP) + : REMOVE_ALL_VIEWS_ID; + return new ViewGroupActionRemove(viewId, viewIdToKeep); + }; + } } /** @@ -4497,6 +4617,200 @@ public class RemoteViews implements Parcelable, Filter { visitIconUri(mI4, visitor); } } + + @Override + public boolean canWriteToProto() { + return true; + } + + @Override + public void writeToProto(ProtoOutputStream out, Context context, + Resources appResources) { // rebase + final long token = out.start(RemoteViewsProto.Action.TEXT_VIEW_DRAWABLE_ACTION); + out.write(RemoteViewsProto.TextViewDrawableAction.VIEW_ID, + appResources.getResourceName(mViewId)); + out.write(RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE, mIsRelative); + if (mUseIcons) { + long iconsToken = out.start(RemoteViewsProto.TextViewDrawableAction.ICONS); + if (mI1 != null) { + writeIconToProto(out, appResources, mI1, + RemoteViewsProto.TextViewDrawableAction.Icons.ONE); + } + if (mI2 != null) { + writeIconToProto(out, appResources, mI2, + RemoteViewsProto.TextViewDrawableAction.Icons.TWO); + } + if (mI3 != null) { + writeIconToProto(out, appResources, mI3, + RemoteViewsProto.TextViewDrawableAction.Icons.THREE); + } + if (mI4 != null) { + writeIconToProto(out, appResources, mI4, + RemoteViewsProto.TextViewDrawableAction.Icons.FOUR); + } + out.end(iconsToken); + } else { + long resourcesToken = out.start(RemoteViewsProto.TextViewDrawableAction.RESOURCES); + if (mD1 != 0) { + out.write(RemoteViewsProto.TextViewDrawableAction.Resources.ONE, + appResources.getResourceName(mD1)); + } + if (mD2 != 0) { + out.write(RemoteViewsProto.TextViewDrawableAction.Resources.TWO, + appResources.getResourceName(mD2)); + } + if (mD3 != 0) { + out.write(RemoteViewsProto.TextViewDrawableAction.Resources.THREE, + appResources.getResourceName(mD3)); + } + if (mD4 != 0) { + out.write(RemoteViewsProto.TextViewDrawableAction.Resources.FOUR, + appResources.getResourceName(mD4)); + } + out.end(resourcesToken); + } + out.end(token); + } + + public static PendingResources createFromProto(ProtoInputStream in) + throws Exception { + final LongSparseArray values = new LongSparseArray<>(); + + values.put(RemoteViewsProto.TextViewDrawableAction.ICONS, + new SparseArray>()); + values.put(RemoteViewsProto.TextViewDrawableAction.RESOURCES, + new SparseArray()); + final long token = in.start(RemoteViewsProto.Action.TEXT_VIEW_DRAWABLE_ACTION); + while (in.nextField() != NO_MORE_FIELDS) { + switch (in.getFieldNumber()) { + case (int) RemoteViewsProto.TextViewDrawableAction.VIEW_ID: + values.put(RemoteViewsProto.TextViewDrawableAction.VIEW_ID, + in.readString(RemoteViewsProto.TextViewDrawableAction.VIEW_ID)); + break; + case (int) RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE: + values.put(RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE, + in.readBoolean( + RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE)); + break; + case (int) RemoteViewsProto.TextViewDrawableAction.RESOURCES: + final long resourcesToken = in.start( + RemoteViewsProto.TextViewDrawableAction.RESOURCES); + while (in.nextField() != NO_MORE_FIELDS) { + switch (in.getFieldNumber()) { + case (int) RemoteViewsProto.TextViewDrawableAction.Resources.ONE: + ((SparseArray) values.get( + RemoteViewsProto.TextViewDrawableAction.RESOURCES)).put( + 1, in.readString( + RemoteViewsProto + .TextViewDrawableAction.Resources.ONE)); + break; + case (int) RemoteViewsProto.TextViewDrawableAction.Resources.TWO: + ((SparseArray) values.get( + RemoteViewsProto.TextViewDrawableAction.RESOURCES)).put( + 2, in.readString( + RemoteViewsProto + .TextViewDrawableAction.Resources.TWO)); + break; + case (int) RemoteViewsProto.TextViewDrawableAction.Resources.THREE: + ((SparseArray) values.get( + RemoteViewsProto.TextViewDrawableAction.RESOURCES)).put( + 3, in.readString( + RemoteViewsProto + .TextViewDrawableAction + .Resources.THREE)); + break; + case (int) RemoteViewsProto.TextViewDrawableAction.Resources.FOUR: + ((SparseArray) values.get( + RemoteViewsProto.TextViewDrawableAction.RESOURCES)).put( + 4, in.readString( + RemoteViewsProto + .TextViewDrawableAction + .Resources.FOUR)); + break; + default: + Log.w(LOG_TAG, + "Unhandled field while reading RemoteViews proto!\n" + + ProtoUtils.currentFieldToString(in)); + } + } + in.end(resourcesToken); + break; + case (int) RemoteViewsProto.TextViewDrawableAction.ICONS: + final long iconsToken = in.start( + RemoteViewsProto.TextViewDrawableAction.ICONS); + while (in.nextField() != NO_MORE_FIELDS) { + switch (in.getFieldNumber()) { + case (int) RemoteViewsProto.TextViewDrawableAction.Icons.ONE: + ((SparseArray>) values.get( + RemoteViewsProto.TextViewDrawableAction.ICONS)).put(1, + createIconFromProto(in, + RemoteViewsProto + .TextViewDrawableAction.Icons.ONE)); + break; + case (int) RemoteViewsProto.TextViewDrawableAction.Icons.TWO: + ((SparseArray>) values.get( + RemoteViewsProto.TextViewDrawableAction.ICONS)).put(2, + createIconFromProto(in, + RemoteViewsProto + .TextViewDrawableAction.Icons.TWO)); + break; + case (int) RemoteViewsProto.TextViewDrawableAction.Icons.THREE: + ((SparseArray>) values.get( + RemoteViewsProto.TextViewDrawableAction.ICONS)).put(3, + createIconFromProto(in, + RemoteViewsProto + .TextViewDrawableAction.Icons.THREE)); + break; + case (int) RemoteViewsProto.TextViewDrawableAction.Icons.FOUR: + ((SparseArray>) values.get( + RemoteViewsProto.TextViewDrawableAction.ICONS)).put(4, + createIconFromProto(in, + RemoteViewsProto + .TextViewDrawableAction.Icons.FOUR)); + break; + default: + Log.w(LOG_TAG, + "Unhandled field while reading RemoteViews proto!\n" + + ProtoUtils.currentFieldToString(in)); + } + } + in.end(iconsToken); + break; + default: + Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + + ProtoUtils.currentFieldToString(in)); + } + } + in.end(token); + + checkContainsKeys(values, new long[]{RemoteViewsProto.TextViewDrawableAction.VIEW_ID}); + + return (context, resources, rootData, depth) -> { + int viewId = getAsIdentifier(resources, values, + RemoteViewsProto.TextViewDrawableAction.VIEW_ID); + SparseArray> icons = + (SparseArray>) values.get( + RemoteViewsProto.TextViewDrawableAction.ICONS); + SparseArray resArray = (SparseArray) values.get( + RemoteViewsProto.TextViewDrawableAction.RESOURCES); + boolean isRelative = (boolean) values.get( + RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE, false); + if (icons.size() > 0) { + return new TextViewDrawableAction(viewId, isRelative, + icons.get(1).create(context, resources, rootData, depth), + icons.get(2).create(context, resources, rootData, depth), + icons.get(3).create(context, resources, rootData, depth), + icons.get(4).create(context, resources, rootData, depth)); + } else { + int first = resArray.contains(1) ? getAsIdentifier(resources, resArray, 1) : 0; + int second = resArray.contains(2) ? getAsIdentifier(resources, resArray, 2) : 0; + int third = resArray.contains(3) ? getAsIdentifier(resources, resArray, 3) : 0; + int fourth = resArray.contains(4) ? getAsIdentifier(resources, resArray, 4) : 0; + return new TextViewDrawableAction(viewId, isRelative, first, second, third, + fourth); + } + }; + } } /** @@ -4535,6 +4849,58 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return TEXT_VIEW_SIZE_ACTION_TAG; } + + @Override + public boolean canWriteToProto() { + return true; + } + + @Override + public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) { + final long token = out.start(RemoteViewsProto.Action.TEXT_VIEW_SIZE_ACTION); + out.write(RemoteViewsProto.TextViewSizeAction.VIEW_ID, + appResources.getResourceName(mViewId)); + out.write(RemoteViewsProto.TextViewSizeAction.UNITS, mUnits); + out.write(RemoteViewsProto.TextViewSizeAction.SIZE, mSize); + out.end(token); + } + + public static PendingResources createFromProto(ProtoInputStream in) + throws Exception { + final LongSparseArray values = new LongSparseArray<>(); + + final long token = in.start(RemoteViewsProto.Action.TEXT_VIEW_SIZE_ACTION); + while (in.nextField() != NO_MORE_FIELDS) { + switch (in.getFieldNumber()) { + case (int) RemoteViewsProto.TextViewSizeAction.VIEW_ID: + values.put(RemoteViewsProto.TextViewSizeAction.VIEW_ID, + in.readString(RemoteViewsProto.TextViewSizeAction.VIEW_ID)); + break; + case (int) RemoteViewsProto.TextViewSizeAction.UNITS: + values.put(RemoteViewsProto.TextViewSizeAction.UNITS, + in.readInt(RemoteViewsProto.TextViewSizeAction.UNITS)); + break; + case (int) RemoteViewsProto.TextViewSizeAction.SIZE: + values.put(RemoteViewsProto.TextViewSizeAction.SIZE, + in.readFloat(RemoteViewsProto.TextViewSizeAction.SIZE)); + break; + default: + Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + + ProtoUtils.currentFieldToString(in)); + } + } + in.end(token); + + checkContainsKeys(values, new long[]{RemoteViewsProto.TextViewSizeAction.VIEW_ID}); + + return (context, resources, rootData, depth) -> { + int viewId = getAsIdentifier(resources, values, + RemoteViewsProto.TextViewSizeAction.VIEW_ID); + return new TextViewSizeAction(viewId, + (int) values.get(RemoteViewsProto.TextViewSizeAction.UNITS, 0), + (float) values.get(RemoteViewsProto.TextViewSizeAction.SIZE, 0)); + }; + } } /** @@ -4579,6 +4945,70 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return VIEW_PADDING_ACTION_TAG; } + + @Override + public boolean canWriteToProto() { + return true; + } + + @Override + public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) { + final long token = out.start(RemoteViewsProto.Action.VIEW_PADDING_ACTION); + out.write(RemoteViewsProto.ViewPaddingAction.VIEW_ID, + appResources.getResourceName(mViewId)); + out.write(RemoteViewsProto.ViewPaddingAction.LEFT, mLeft); + out.write(RemoteViewsProto.ViewPaddingAction.RIGHT, mRight); + out.write(RemoteViewsProto.ViewPaddingAction.TOP, mTop); + out.write(RemoteViewsProto.ViewPaddingAction.BOTTOM, mBottom); + out.end(token); + } + + public static PendingResources createFromProto(ProtoInputStream in) + throws Exception { + final LongSparseArray values = new LongSparseArray<>(); + + final long token = in.start(RemoteViewsProto.Action.VIEW_PADDING_ACTION); + while (in.nextField() != NO_MORE_FIELDS) { + switch (in.getFieldNumber()) { + case (int) RemoteViewsProto.ViewPaddingAction.VIEW_ID: + values.put(RemoteViewsProto.ViewPaddingAction.VIEW_ID, + in.readString(RemoteViewsProto.ViewPaddingAction.VIEW_ID)); + break; + case (int) RemoteViewsProto.ViewPaddingAction.LEFT: + values.put(RemoteViewsProto.ViewPaddingAction.LEFT, + in.readInt(RemoteViewsProto.ViewPaddingAction.LEFT)); + break; + case (int) RemoteViewsProto.ViewPaddingAction.RIGHT: + values.put(RemoteViewsProto.ViewPaddingAction.RIGHT, + in.readInt(RemoteViewsProto.ViewPaddingAction.RIGHT)); + break; + case (int) RemoteViewsProto.ViewPaddingAction.TOP: + values.put(RemoteViewsProto.ViewPaddingAction.TOP, + in.readInt(RemoteViewsProto.ViewPaddingAction.TOP)); + break; + case (int) RemoteViewsProto.ViewPaddingAction.BOTTOM: + values.put(RemoteViewsProto.ViewPaddingAction.BOTTOM, + in.readInt(RemoteViewsProto.ViewPaddingAction.BOTTOM)); + break; + default: + Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + + ProtoUtils.currentFieldToString(in)); + } + } + in.end(token); + + checkContainsKeys(values, new long[]{RemoteViewsProto.ViewPaddingAction.VIEW_ID}); + + return (context, resources, rootData, depth) -> { + int viewId = getAsIdentifier(resources, values, + RemoteViewsProto.ViewPaddingAction.VIEW_ID); + return new ViewPaddingAction(viewId, + (int) values.get(RemoteViewsProto.ViewPaddingAction.LEFT, 0), + (int) values.get(RemoteViewsProto.ViewPaddingAction.TOP, 0), + (int) values.get(RemoteViewsProto.ViewPaddingAction.RIGHT, 0), + (int) values.get(RemoteViewsProto.ViewPaddingAction.BOTTOM, 0)); + }; + } } /** @@ -5241,6 +5671,69 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_VIEW_OUTLINE_RADIUS_TAG; } + + @Override + public boolean canWriteToProto() { + return true; + } + + @Override + public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) { + final long token = out.start( + RemoteViewsProto.Action.SET_VIEW_OUTLINE_PREFERRED_RADIUS_ACTION); + out.write(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID, + appResources.getResourceName(mViewId)); + out.write(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE, mValueType); + out.write(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE, mValue); + out.end(token); + } + + public static PendingResources createFromProto(ProtoInputStream in) + throws Exception { + final LongSparseArray values = new LongSparseArray<>(); + + final long token = in.start( + RemoteViewsProto.Action.SET_VIEW_OUTLINE_PREFERRED_RADIUS_ACTION); + while (in.nextField() != NO_MORE_FIELDS) { + switch (in.getFieldNumber()) { + case (int) RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID: + values.put(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID, + in.readString( + RemoteViewsProto + .SetViewOutlinePreferredRadiusAction.VIEW_ID)); + break; + case (int) RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE: + values.put(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE, + in.readInt( + RemoteViewsProto + .SetViewOutlinePreferredRadiusAction.VALUE_TYPE)); + break; + case (int) RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE: + values.put(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE, + in.readInt( + RemoteViewsProto + .SetViewOutlinePreferredRadiusAction.VALUE)); + break; + default: + Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + + ProtoUtils.currentFieldToString(in)); + } + } + in.end(token); + + checkContainsKeys(values, + new long[]{RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID, + RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE}); + + return (context, resources, rootData, depth) -> { + int viewId = getAsIdentifier(resources, values, + RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID); + return new SetViewOutlinePreferredRadiusAction(viewId, + (int) values.get(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE, + 0), (int) values.get( + RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE)); + }; + } } /** @@ -5324,6 +5817,46 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_DRAW_INSTRUCTION_TAG; } + + @Override + public boolean canWriteToProto() { + return drawDataParcel(); + } + + @Override + public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) { + if (!drawDataParcel()) return; + final long token = out.start(RemoteViewsProto.Action.SET_DRAW_INSTRUCTION_ACTION); + if (mInstructions != null) { + for (byte[] bytes : mInstructions.mInstructions) { + out.write(RemoteViewsProto.SetDrawInstructionAction.INSTRUCTIONS, bytes); + } + } + out.end(token); + } + } + + @FlaggedApi(FLAG_DRAW_DATA_PARCEL) + private PendingResources createSetDrawInstructionActionFromProto(ProtoInputStream in) + throws Exception { + List instructions = new ArrayList(); + + final long token = in.start(RemoteViewsProto.Action.SET_DRAW_INSTRUCTION_ACTION); + while (in.nextField() != NO_MORE_FIELDS) { + switch (in.getFieldNumber()) { + case (int) RemoteViewsProto.SetDrawInstructionAction.INSTRUCTIONS: + instructions.add( + in.readBytes(RemoteViewsProto.SetDrawInstructionAction.INSTRUCTIONS)); + break; + default: + Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + + ProtoUtils.currentFieldToString(in)); + } + } + in.end(token); + + return (context, resources, rootData, depth) -> new SetDrawInstructionAction( + new DrawInstructions.Builder(instructions).build()); } /** @@ -9604,12 +10137,20 @@ public class RemoteViews implements Parcelable, Filter { } if (ref.mMode == MODE_NORMAL) { rv.setIdealSize(ref.mIdealSize); + boolean hasDrawInstructionAction = false; for (PendingResources pendingAction : ref.mActions) { Action action = pendingAction.create(appContext, appResources, rootData, depth); if (action != null) { + if (action instanceof SetDrawInstructionAction) { + hasDrawInstructionAction = true; + } rv.addAction(action); } } + if (rv.mHasDrawInstructions && !hasDrawInstructionAction) { + throw new InvalidProtoException( + "RemoteViews proto is missing DrawInstructions"); + } return rv; } else if (ref.mMode == MODE_HAS_SIZED_REMOTEVIEWS) { List sizedViews = new ArrayList<>(); @@ -9685,6 +10226,23 @@ public class RemoteViews implements Parcelable, Filter { return rv.createSetRemoteCollectionItemListAdapterActionFromProto(in); case (int) RemoteViewsProto.Action.SET_RIPPLE_DRAWABLE_COLOR_ACTION: return SetRippleDrawableColor.createFromProto(in); + case (int) RemoteViewsProto.Action.SET_VIEW_OUTLINE_PREFERRED_RADIUS_ACTION: + return SetViewOutlinePreferredRadiusAction.createFromProto(in); + case (int) RemoteViewsProto.Action.TEXT_VIEW_DRAWABLE_ACTION: + return TextViewDrawableAction.createFromProto(in); + case (int) RemoteViewsProto.Action.TEXT_VIEW_SIZE_ACTION: + return TextViewSizeAction.createFromProto(in); + case (int) RemoteViewsProto.Action.VIEW_GROUP_ADD_ACTION: + return rv.createViewGroupActionAddFromProto(in); + case (int) RemoteViewsProto.Action.VIEW_GROUP_REMOVE_ACTION: + return ViewGroupActionRemove.createFromProto(in); + case (int) RemoteViewsProto.Action.VIEW_PADDING_ACTION: + return ViewPaddingAction.createFromProto(in); + case (int) RemoteViewsProto.Action.SET_DRAW_INSTRUCTION_ACTION: + if (!drawDataParcel()) { + return null; + } + return rv.createSetDrawInstructionActionFromProto(in); default: throw new RuntimeException("Unhandled field while reading Action proto!\n" + ProtoUtils.currentFieldToString(in)); diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java index 7cda3a36da955cff8f4298a8c5fa5f24a4570e2d..8ac68abd8d8ea1e39949e721732d0a8fd8210ede 100644 --- a/core/java/android/window/BackMotionEvent.java +++ b/core/java/android/window/BackMotionEvent.java @@ -34,8 +34,6 @@ public final class BackMotionEvent implements Parcelable { private final float mTouchX; private final float mTouchY; private final float mProgress; - private final float mVelocityX; - private final float mVelocityY; private final boolean mTriggerBack; @BackEvent.SwipeEdge @@ -51,10 +49,6 @@ public final class BackMotionEvent implements Parcelable { * @param touchX Absolute X location of the touch point of this event. * @param touchY Absolute Y location of the touch point of this event. * @param progress Value between 0 and 1 on how far along the back gesture is. - * @param velocityX X velocity computed from the touch point of this event. - * Value in pixels/second. {@link Float#NaN} if was not computed. - * @param velocityY Y velocity computed from the touch point of this event. - * Value in pixels/second. {@link Float#NaN} if was not computed. * @param triggerBack Indicates whether the back arrow is in the triggered state or not * @param swipeEdge Indicates which edge the swipe starts from. * @param departingAnimationTarget The remote animation target of the departing @@ -64,16 +58,12 @@ public final class BackMotionEvent implements Parcelable { float touchX, float touchY, float progress, - float velocityX, - float velocityY, boolean triggerBack, @BackEvent.SwipeEdge int swipeEdge, @Nullable RemoteAnimationTarget departingAnimationTarget) { mTouchX = touchX; mTouchY = touchY; mProgress = progress; - mVelocityX = velocityX; - mVelocityY = velocityY; mTriggerBack = triggerBack; mSwipeEdge = swipeEdge; mDepartingAnimationTarget = departingAnimationTarget; @@ -83,8 +73,6 @@ public final class BackMotionEvent implements Parcelable { mTouchX = in.readFloat(); mTouchY = in.readFloat(); mProgress = in.readFloat(); - mVelocityX = in.readFloat(); - mVelocityY = in.readFloat(); mTriggerBack = in.readBoolean(); mSwipeEdge = in.readInt(); mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR); @@ -113,8 +101,6 @@ public final class BackMotionEvent implements Parcelable { dest.writeFloat(mTouchX); dest.writeFloat(mTouchY); dest.writeFloat(mProgress); - dest.writeFloat(mVelocityX); - dest.writeFloat(mVelocityY); dest.writeBoolean(mTriggerBack); dest.writeInt(mSwipeEdge); dest.writeTypedObject(mDepartingAnimationTarget, flags); @@ -144,24 +130,6 @@ public final class BackMotionEvent implements Parcelable { return mProgress; } - /** - * Returns the X velocity computed from the touch point. - * - * @return value in pixels/second or {@link Float#NaN} if was not computed. - */ - public float getVelocityX() { - return mVelocityX; - } - - /** - * Returns the Y velocity computed from the touch point. - * - * @return value in pixels/second or {@link Float#NaN} if was not computed. - */ - public float getVelocityY() { - return mVelocityY; - } - /** * Returns whether the back arrow is in the triggered state or not * @@ -195,8 +163,6 @@ public final class BackMotionEvent implements Parcelable { + "mTouchX=" + mTouchX + ", mTouchY=" + mTouchY + ", mProgress=" + mProgress - + ", mVelocityX=" + mVelocityX - + ", mVelocityY=" + mVelocityY + ", mTriggerBack=" + mTriggerBack + ", mSwipeEdge" + mSwipeEdge + ", mDepartingAnimationTarget" + mDepartingAnimationTarget diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java index eb23af27a24ddd3306dceed77f6cf544da2e2d25..290c494b1bffcdc251353137be87f25a3883868c 100644 --- a/core/java/android/window/BackTouchTracker.java +++ b/core/java/android/window/BackTouchTracker.java @@ -48,8 +48,6 @@ public class BackTouchTracker { */ private float mInitTouchX; private float mInitTouchY; - private float mLatestVelocityX; - private float mLatestVelocityY; private float mStartThresholdX; private int mSwipeEdge; private boolean mShouldUpdateStartLocation = false; @@ -58,7 +56,7 @@ public class BackTouchTracker { /** * Updates the tracker with a new motion event. */ - public void update(float touchX, float touchY, float velocityX, float velocityY) { + public void update(float touchX, float touchY) { /** * If back was previously cancelled but the user has started swiping in the forward * direction again, restart back. @@ -73,8 +71,6 @@ public class BackTouchTracker { } mLatestTouchX = touchX; mLatestTouchY = touchY; - mLatestVelocityX = velocityX; - mLatestVelocityY = velocityY; } /** Sets whether the back gesture is past the trigger threshold. */ @@ -156,8 +152,6 @@ public class BackTouchTracker { /* touchX = */ mInitTouchX, /* touchY = */ mInitTouchY, /* progress = */ 0, - /* velocityX = */ 0, - /* velocityY = */ 0, /* triggerBack = */ mTriggerBack, /* swipeEdge = */ mSwipeEdge, /* departingAnimationTarget = */ target); @@ -242,8 +236,6 @@ public class BackTouchTracker { /* touchX = */ mLatestTouchX, /* touchY = */ mLatestTouchY, /* progress = */ progress, - /* velocityX = */ mLatestVelocityX, - /* velocityY = */ mLatestVelocityY, /* triggerBack = */ mTriggerBack, /* swipeEdge = */ mSwipeEdge, /* departingAnimationTarget = */ null); diff --git a/core/java/android/window/flags/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java similarity index 97% rename from core/java/android/window/flags/DesktopModeFlags.java rename to core/java/android/window/DesktopModeFlags.java index 47af50dac930d84b820b1d00fa247166b5470a5e..8e35843e2193819ee4f651ab26ad20c84eea13d3 100644 --- a/core/java/android/window/flags/DesktopModeFlags.java +++ b/core/java/android/window/DesktopModeFlags.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.window.flags; +package android.window; import android.annotation.Nullable; import android.app.ActivityThread; @@ -65,7 +65,9 @@ public enum DesktopModeFlags { ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS( Flags::enableDesktopWindowingTaskbarRunningApps, true), ENABLE_DESKTOP_WINDOWING_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false), - ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false); + ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false), + ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS( + Flags::enableWindowingTransitionHandlersObservers, false); private static final String TAG = "DesktopModeFlagsUtil"; // Function called to obtain aconfig flag value. diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java index 771dc7a99c0e93a52bf9ce429cdb916edc8b73b7..66c35e2fe837f9da92cd2dfc5307e0bd7badf652 100644 --- a/core/java/android/window/ImeOnBackInvokedDispatcher.java +++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java @@ -237,7 +237,7 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc try { mIOnBackInvokedCallback.onBackStarted( new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), - backEvent.getProgress(), 0f, 0f, false, backEvent.getSwipeEdge(), + backEvent.getProgress(), false, backEvent.getSwipeEdge(), null)); } catch (RemoteException e) { Log.e(TAG, "Exception when invoking forwarded callback. e: ", e); @@ -249,7 +249,7 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc try { mIOnBackInvokedCallback.onBackProgressed( new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), - backEvent.getProgress(), 0f, 0f, false, backEvent.getSwipeEdge(), + backEvent.getProgress(), false, backEvent.getSwipeEdge(), null)); } catch (RemoteException e) { Log.e(TAG, "Exception when invoking forwarded callback. e: ", e); diff --git a/core/java/android/window/KeyguardState.aidl b/core/java/android/window/KeyguardState.aidl new file mode 100644 index 0000000000000000000000000000000000000000..9612d9590056443ee4ec616370647b1a7d7ac3bf --- /dev/null +++ b/core/java/android/window/KeyguardState.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +/** + * @hide + */ +parcelable KeyguardState; diff --git a/core/java/android/window/KeyguardState.java b/core/java/android/window/KeyguardState.java new file mode 100644 index 0000000000000000000000000000000000000000..6584d30cdaedefbe9c754a58a0267a9dd1308382 --- /dev/null +++ b/core/java/android/window/KeyguardState.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Data object of params for Keyguard related {@link WindowContainerTransaction} operation. + * + * @hide + */ +public final class KeyguardState implements Parcelable { + + private final int mDisplayId; + + private final boolean mKeyguardShowing; + + private final boolean mAodShowing; + + + private KeyguardState(int displayId, boolean keyguardShowing, boolean aodShowing) { + mDisplayId = displayId; + mKeyguardShowing = keyguardShowing; + mAodShowing = aodShowing; + } + + private KeyguardState(Parcel in) { + mDisplayId = in.readInt(); + mKeyguardShowing = in.readBoolean(); + mAodShowing = in.readBoolean(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mDisplayId); + dest.writeBoolean(mKeyguardShowing); + dest.writeBoolean(mAodShowing); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public KeyguardState createFromParcel(Parcel in) { + return new KeyguardState(in); + } + + @Override + public KeyguardState[] newArray(int size) { + return new KeyguardState[size]; + } + }; + + /** + * Gets the display id of this {@link KeyguardState}. + */ + public int getDisplayId() { + return mDisplayId; + } + + /** Returns the keyguard showing value. */ + public boolean getKeyguardShowing() { + return mKeyguardShowing; + } + + /** Returns the aod showing value. */ + public boolean getAodShowing() { + return mAodShowing; + } + + @Override + public String toString() { + return "KeyguardState{ displayId=" + mDisplayId + + ", keyguardShowing=" + mKeyguardShowing + + ", aodShowing=" + mAodShowing + + '}'; + } + + @Override + public int hashCode() { + return Objects.hash(mDisplayId, mKeyguardShowing, mAodShowing); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof KeyguardState other)) { + return false; + } + return mDisplayId == other.mDisplayId + && mKeyguardShowing == other.mKeyguardShowing + && mAodShowing == other.mAodShowing; + } + + @Override + public int describeContents() { + return 0; + } + + /** Builder to construct the {@link KeyguardState}. */ + public static final class Builder { + + private final int mDisplayId; + + private boolean mKeyguardShowing; + + private boolean mAodShowing; + + /** + * @param displayId the display of this {@link KeyguardState}. + */ + public Builder(int displayId) { + mDisplayId = displayId; + } + + /** + * Sets the boolean value for this operation. + */ + @NonNull + public Builder setKeyguardShowing(boolean keyguardShowing) { + mKeyguardShowing = keyguardShowing; + return this; + } + + /** + * Sets the boolean value for this operation. + */ + @NonNull + public Builder setAodShowing(boolean aodShowing) { + mAodShowing = aodShowing; + return this; + } + + /** + * Constructs the {@link KeyguardState}. + */ + @NonNull + public KeyguardState build() { + return new KeyguardState(mDisplayId, mKeyguardShowing, mAodShowing); + } + } +} diff --git a/core/java/android/window/OWNERS b/core/java/android/window/OWNERS index 2c61df96eb03cab8100da4c87b68fb164eae18ef..77c99b98cf4ad3628720e6c69b89876f05a88a9c 100644 --- a/core/java/android/window/OWNERS +++ b/core/java/android/window/OWNERS @@ -1,3 +1,5 @@ set noparent include /services/core/java/com/android/server/wm/OWNERS + +per-file DesktopModeFlags.java = file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 0dc9263e990da8e8d8b92f579c011b97c05ba62f..8e495ec1dc40c81448b255da868f892f7067b6cf 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -868,6 +868,24 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + /** + * Adds a {@link KeyguardState} to apply to the given displays. + * + * @hide + */ + @NonNull + public WindowContainerTransaction addKeyguardState( + @NonNull KeyguardState keyguardState) { + Objects.requireNonNull(keyguardState); + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder( + HierarchyOp.HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE) + .setKeyguardState(keyguardState) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + /** * Sets/removes the always on top flag for this {@code windowContainer}. See * {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}. @@ -1469,6 +1487,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE = 19; public static final int HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION = 20; public static final int HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES = 21; + public static final int HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE = 22; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -1515,6 +1534,9 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private TaskFragmentOperation mTaskFragmentOperation; + @Nullable + private KeyguardState mKeyguardState; + @Nullable private PendingIntent mPendingIntent; @@ -1666,6 +1688,7 @@ public final class WindowContainerTransaction implements Parcelable { mLaunchOptions = copy.mLaunchOptions; mActivityIntent = copy.mActivityIntent; mTaskFragmentOperation = copy.mTaskFragmentOperation; + mKeyguardState = copy.mKeyguardState; mPendingIntent = copy.mPendingIntent; mShortcutInfo = copy.mShortcutInfo; mAlwaysOnTop = copy.mAlwaysOnTop; @@ -1689,6 +1712,7 @@ public final class WindowContainerTransaction implements Parcelable { mLaunchOptions = in.readBundle(); mActivityIntent = in.readTypedObject(Intent.CREATOR); mTaskFragmentOperation = in.readTypedObject(TaskFragmentOperation.CREATOR); + mKeyguardState = in.readTypedObject(KeyguardState.CREATOR); mPendingIntent = in.readTypedObject(PendingIntent.CREATOR); mShortcutInfo = in.readTypedObject(ShortcutInfo.CREATOR); mAlwaysOnTop = in.readBoolean(); @@ -1769,6 +1793,11 @@ public final class WindowContainerTransaction implements Parcelable { return mTaskFragmentOperation; } + @Nullable + public KeyguardState getKeyguardState() { + return mKeyguardState; + } + @Nullable public PendingIntent getPendingIntent() { return mPendingIntent; @@ -1902,6 +1931,9 @@ public final class WindowContainerTransaction implements Parcelable { .append(" mExcludeInsetsTypes= ") .append(WindowInsets.Type.toString(mExcludeInsetsTypes)); break; + case HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE: + sb.append("KeyguardState= ").append(mKeyguardState); + break; case HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE: sb.append("container= ").append(mContainer) .append(" isTrimmable= ") @@ -1932,6 +1964,7 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeBundle(mLaunchOptions); dest.writeTypedObject(mActivityIntent, flags); dest.writeTypedObject(mTaskFragmentOperation, flags); + dest.writeTypedObject(mKeyguardState, flags); dest.writeTypedObject(mPendingIntent, flags); dest.writeTypedObject(mShortcutInfo, flags); dest.writeBoolean(mAlwaysOnTop); @@ -1992,6 +2025,9 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private TaskFragmentOperation mTaskFragmentOperation; + @Nullable + private KeyguardState mKeyguardState; + @Nullable private PendingIntent mPendingIntent; @@ -2081,6 +2117,12 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + Builder setKeyguardState( + @Nullable KeyguardState keyguardState) { + mKeyguardState = keyguardState; + return this; + } + Builder setReparentLeafTaskIfRelaunch(boolean reparentLeafTaskIfRelaunch) { mReparentLeafTaskIfRelaunch = reparentLeafTaskIfRelaunch; return this; @@ -2130,6 +2172,7 @@ public final class WindowContainerTransaction implements Parcelable { hierarchyOp.mPendingIntent = mPendingIntent; hierarchyOp.mAlwaysOnTop = mAlwaysOnTop; hierarchyOp.mTaskFragmentOperation = mTaskFragmentOperation; + hierarchyOp.mKeyguardState = mKeyguardState; hierarchyOp.mShortcutInfo = mShortcutInfo; hierarchyOp.mBounds = mBounds; hierarchyOp.mIncludingParents = mIncludingParents; diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 51bc7d5c571b8658f628baff8bb8b7ff2d124e3b..44a374fb7c20b4441391ee416d70f74eedda86c4 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -124,7 +124,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { if (!isBackGestureInProgress() || ev == null || ev.getAction() != MotionEvent.ACTION_MOVE) { return; } - mTouchTracker.update(ev.getX(), ev.getY(), Float.NaN, Float.NaN); + mTouchTracker.update(ev.getX(), ev.getY()); if (mTouchTracker.shouldUpdateStartLocation()) { // Reset the start location on the first event after starting back, so that // the beginning of the animation feels smooth. diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index b22aa222d1de04904c82a029a963a6f84f2bedaa..155494fb3b256b6ecd09857a4f56b08b9ccf4479 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -228,7 +228,7 @@ flag { name: "enable_desktop_windowing_app_handle_education" namespace: "lse_desktop_experience" description: "Enables desktop windowing app handle education" - bug: "348208342" + bug: "316006079" } flag { @@ -319,4 +319,11 @@ flag { namespace: "lse_desktop_experience" description: "Enables custom transitions for alt-tab app launches in Desktop Mode." bug: "370735595" -} \ No newline at end of file +} + +flag { + name: "enable_move_to_next_display_shortcut" + namespace: "lse_desktop_experience" + description: "Add new keyboard shortcut of moving a task into next display" + bug: "364154795" +} diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index c9b93c95e0c1f5636c97324c2f0c294e6f0b3335..622f8c817d9971eadc89d579ee10fe5b57020a58 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -172,6 +172,16 @@ flag { bug: "357141415" } +flag { + name: "filter_irrelevant_input_device_change" + namespace: "windowing_frontend" + description: "Recompute display configuration only for necessary input device changes" + bug: "368461853" + metadata { + purpose: PURPOSE_BUGFIX + } +} + flag { name: "respect_non_top_visible_fixed_orientation" namespace: "windowing_frontend" diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceWarning.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceWarning.java index 0f8ced27e8c16ea46ff2ef04ca08f5ca66158e5a..3557633f87c577e55f118a7374094e6c4609099b 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceWarning.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceWarning.java @@ -29,6 +29,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; +import android.view.accessibility.Flags; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; @@ -58,13 +59,15 @@ public class AccessibilityServiceWarning { @NonNull View.OnClickListener uninstallListener) { final AlertDialog ad = new AlertDialog.Builder(context) .setView(createAccessibilityServiceWarningDialogContentView( - context, info, allowListener, denyListener, uninstallListener)) + context, info, allowListener, denyListener, uninstallListener)) .setCancelable(true) .create(); Window window = ad.getWindow(); WindowManager.LayoutParams params = window.getAttributes(); params.privateFlags |= SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; - params.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; + if (!Flags.warningUseDefaultDialogType()) { + params.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; + } window.setAttributes(params); return ad; } diff --git a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java index 39aadfb24b0cfabd45596341a7b2672f30592c5b..8faaf9584e5457d81f3e2afe18de0ed56930d5c1 100644 --- a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java +++ b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java @@ -53,19 +53,18 @@ import java.util.Map; * @hide */ public class AconfigFlags { + private static final boolean DEBUG = false; private static final String LOG_TAG = "AconfigFlags"; - - public enum Permission { - READ_WRITE, - READ_ONLY - } + private static final String OVERRIDE_PREFIX = "device_config_overrides/"; + private static final String STAGED_PREFIX = "staged/"; private final ArrayMap mFlagValues = new ArrayMap<>(); - private final ArrayMap mFlagPermissions = new ArrayMap<>(); public AconfigFlags() { if (!Flags.manifestFlagging()) { - Slog.v(LOG_TAG, "Feature disabled, skipped all loading"); + if (DEBUG) { + Slog.v(LOG_TAG, "Feature disabled, skipped all loading"); + } return; } final var defaultFlagProtoFiles = @@ -130,19 +129,17 @@ public class AconfigFlags { if (!"false".equalsIgnoreCase(value) && !"true".equalsIgnoreCase(value)) { continue; } - final var overridePrefix = "device_config_overrides/"; - final var stagedPrefix = "staged/"; String separator = "/"; String prefix = "default"; int priority = 0; - if (name.startsWith(overridePrefix)) { - prefix = overridePrefix; - name = name.substring(overridePrefix.length()); + if (name.startsWith(OVERRIDE_PREFIX)) { + prefix = OVERRIDE_PREFIX; + name = name.substring(OVERRIDE_PREFIX.length()); separator = ":"; priority = 20; - } else if (name.startsWith(stagedPrefix)) { - prefix = stagedPrefix; - name = name.substring(stagedPrefix.length()); + } else if (name.startsWith(STAGED_PREFIX)) { + prefix = STAGED_PREFIX; + name = name.substring(STAGED_PREFIX.length()); separator = "*"; priority = 10; } @@ -155,12 +152,19 @@ public class AconfigFlags { if (!mFlagValues.containsKey(flagPackageAndName)) { continue; } - Slog.d(LOG_TAG, "Found " + prefix - + " Aconfig flag value for " + flagPackageAndName + " = " + value); + if (DEBUG) { + Slog.d(LOG_TAG, "Found " + prefix + + " Aconfig flag value in settings for " + flagPackageAndName + + " = " + value); + } final Integer currentPriority = flagPriority.get(flagPackageAndName); if (currentPriority != null && currentPriority >= priority) { - Slog.i(LOG_TAG, "Skipping " + prefix + " flag " + flagPackageAndName - + " because of the existing one with priority " + currentPriority); + if (DEBUG) { + Slog.d(LOG_TAG, "Skipping " + prefix + " flag " + + flagPackageAndName + + " in settings because of existing one with priority " + + currentPriority); + } continue; } flagPriority.put(flagPackageAndName, priority); @@ -185,15 +189,7 @@ public class AconfigFlags { for (parsed_flag flag : parsedFlags.parsedFlag) { String flagPackageAndName = flag.package_ + "." + flag.name; boolean flagValue = (flag.state == Aconfig.ENABLED); - Slog.v(LOG_TAG, "Read Aconfig default flag value " - + flagPackageAndName + " = " + flagValue); mFlagValues.put(flagPackageAndName, flagValue); - - Permission permission = flag.permission == Aconfig.READ_ONLY - ? Permission.READ_ONLY - : Permission.READ_WRITE; - - mFlagPermissions.put(flagPackageAndName, permission); } } @@ -203,23 +199,14 @@ public class AconfigFlags { * @return the current value of the given Aconfig flag, or null if there is no such flag */ @Nullable - public Boolean getFlagValue(@NonNull String flagPackageAndName) { + private Boolean getFlagValue(@NonNull String flagPackageAndName) { Boolean value = mFlagValues.get(flagPackageAndName); - Slog.d(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value); + if (DEBUG) { + Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value); + } return value; } - /** - * Get the flag permission, or null if the flag doesn't exist. - * @param flagPackageAndName Full flag name formatted as 'package.flag' - * @return the current permission of the given Aconfig flag, or null if there is no such flag - */ - @Nullable - public Permission getFlagPermission(@NonNull String flagPackageAndName) { - Permission permission = mFlagPermissions.get(flagPackageAndName); - return permission; - } - /** * Check if the element in {@code parser} should be skipped because of the feature flag. * @param parser XML parser object currently parsing an element @@ -247,7 +234,7 @@ public class AconfigFlags { } // Skip if flag==false && attr=="flag" OR flag==true && attr=="!flag" (negated) if (flagValue == negated) { - Slog.v(LOG_TAG, "Skipping element " + parser.getName() + Slog.i(LOG_TAG, "Skipping element " + parser.getName() + " behind feature flag " + featureFlag + " = " + flagValue); return true; } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 6faea17f24b2701127ad31390dadb93e1915830f..bd746d5ecf048dcb33f325058c2a006bc9c9460a 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -38,8 +38,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATIO import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.flags.Flags.customizableWindowHeaders; -import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION; -import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS; +import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION; +import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS; import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL; diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index f177e1473b6a5460068f29154f02f2b49af5a568..81b885aa626b008cd3aae265a023b3f0e49c26d2 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -78,8 +78,9 @@ oneway interface IPhoneStateListener { void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType); void onLinkCapacityEstimateChanged(in List linkCapacityEstimateList); void onMediaQualityStatusChanged(in MediaQualityStatus mediaQualityStatus); - void onCallBackModeStarted(int type); - void onCallBackModeStopped(int type, int reason); + void onCallbackModeStarted(int type, long durationMillis, int subId); + void onCallbackModeRestarted(int type, long durationMillis, int subId); + void onCallbackModeStopped(int type, int reason, int subId); void onSimultaneousCallingStateChanged(in int[] subIds); void onCarrierRoamingNtnModeChanged(in boolean active); void onCarrierRoamingNtnEligibleStateChanged(in boolean eligible); diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index e500a37abb530179bd25ab71e251250612284e43..f836cf2b9d8783b93f633c43daa09d5588dec8ff 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -118,7 +118,8 @@ interface ITelephonyRegistry { void removeCarrierConfigChangeListener(ICarrierConfigChangeListener listener, String pkg); void notifyCarrierConfigChanged(int phoneId, int subId, int carrierId, int specificCarrierId); - void notifyCallbackModeStarted(int phoneId, int subId, int type); + void notifyCallbackModeStarted(int phoneId, int subId, int type, long durationMillis); + void notifyCallbackModeRestarted(int phoneId, int subId, int type, long durationMillis); void notifyCallbackModeStopped(int phoneId, int subId, int type, int reason); void notifyCarrierRoamingNtnModeChanged(int subId, in boolean active); void notifyCarrierRoamingNtnEligibleStateChanged(int subId, in boolean eligible); diff --git a/core/java/com/android/internal/util/MemInfoReader.java b/core/java/com/android/internal/util/MemInfoReader.java index 0c5c8537e85aea12acd701e8240fba8c389b81fe..d34bca6315c1a8f35d1f3564a6800d2722cd9bf3 100644 --- a/core/java/com/android/internal/util/MemInfoReader.java +++ b/core/java/com/android/internal/util/MemInfoReader.java @@ -87,6 +87,13 @@ public final class MemInfoReader { return mInfos[Debug.MEMINFO_FREE]; } + /** + * Amount of RAM that used by shared memory (shmem) and tmpfs + */ + public long getShmemSizeKb() { + return mInfos[Debug.MEMINFO_SHMEM]; + } + /** * Amount of RAM that the kernel is being used for caches, not counting caches * that are mapped in to processes. diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java new file mode 100644 index 0000000000000000000000000000000000000000..3e597d73f217b198e5f4e7f22ea2644ecf6f621e --- /dev/null +++ b/core/java/com/android/internal/widget/NotificationProgressBar.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.Notification.ProgressStyle; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.RemotableViewMethod; +import android.widget.ProgressBar; +import android.widget.RemoteViews; + +import androidx.annotation.ColorInt; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; +import com.android.internal.widget.NotificationProgressDrawable.Part; +import com.android.internal.widget.NotificationProgressDrawable.Point; +import com.android.internal.widget.NotificationProgressDrawable.Segment; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * NotificationProgressBar extends the capabilities of ProgressBar by adding functionalities to + * represent Notification ProgressStyle progress, such as for ridesharing and navigation. + */ +@RemoteViews.RemoteView +public class NotificationProgressBar extends ProgressBar { + private NotificationProgressModel mProgressModel; + @Nullable + private Drawable mProgressTrackerDrawable = null; + + public NotificationProgressBar(Context context) { + this(context, null); + } + + public NotificationProgressBar(Context context, AttributeSet attrs) { + this(context, attrs, com.android.internal.R.attr.progressBarStyle); + } + + public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + /** + * Setter for the notification progress model. + * + * @see NotificationProgressModel#fromBundle + * @see #setProgressModelAsync + */ + @RemotableViewMethod(asyncImpl = "setProgressModelAsync") + public void setProgressModel(@Nullable Bundle bundle) { + Preconditions.checkArgument(bundle != null, + "Bundle shouldn't be null"); + + mProgressModel = NotificationProgressModel.fromBundle(bundle); + } + + private void setProgressModel(@NonNull NotificationProgressModel model) { + mProgressModel = model; + } + + /** + * Setter for the progress tracker icon. + * + * @see #setProgressTrackerIconAsync + */ + @RemotableViewMethod(asyncImpl = "setProgressTrackerIconAsync") + public void setProgressTrackerIcon(@Nullable Icon icon) { + } + + + /** + * Async version of {@link #setProgressTrackerIcon} + */ + public Runnable setProgressTrackerIconAsync(@Nullable Icon icon) { + final Drawable progressTrackerDrawable; + if (icon != null) { + progressTrackerDrawable = icon.loadDrawable(getContext()); + } else { + progressTrackerDrawable = null; + } + return () -> { + setProgressTrackerDrawable(progressTrackerDrawable); + }; + } + + private void setProgressTrackerDrawable(@Nullable Drawable drawable) { + mProgressTrackerDrawable = drawable; + } + + /** + * Processes the ProgressStyle data and convert to list of {@code + * NotificationProgressDrawable.Part}. + */ + @VisibleForTesting + public static List processAndConvertToDrawableParts( + List segments, + List points, + int progress, + boolean isStyledByProgress + ) { + if (segments.isEmpty()) { + throw new IllegalArgumentException("List of segments shouldn't be empty"); + } + + for (ProgressStyle.Segment segment : segments) { + final int length = segment.getLength(); + if (length <= 0) { + throw new IllegalArgumentException("Invalid segment length : " + length); + } + } + + final int progressMax = segments.stream().mapToInt(ProgressStyle.Segment::getLength).sum(); + + if (progress < 0 || progress > progressMax) { + throw new IllegalArgumentException("Invalid progress : " + progress); + } + for (ProgressStyle.Point point : points) { + final int pos = point.getPosition(); + if (pos <= 0 || pos >= progressMax) { + throw new IllegalArgumentException("Invalid Point position : " + pos); + } + } + + final Map startToSegmentMap = generateStartToSegmentMap( + segments); + final Map positionToPointMap = generatePositionToPointMap( + points); + final SortedSet sortedPos = generateSortedPositionSet(startToSegmentMap, + positionToPointMap, progress, isStyledByProgress); + + final Map startToSplitSegmentMap = + splitSegmentsByPointsAndProgress( + startToSegmentMap, sortedPos, progressMax); + + return convertToDrawableParts(startToSplitSegmentMap, positionToPointMap, sortedPos, + progress, progressMax, + isStyledByProgress); + } + + + // Any segment with a point on it gets split by the point. + // If isStyledByProgress is true, also split the segment with the progress value in its range. + private static Map splitSegmentsByPointsAndProgress( + Map startToSegmentMap, + SortedSet sortedPos, + int progressMax) { + int prevSegStart = 0; + for (Integer pos : sortedPos) { + if (pos == 0 || pos == progressMax) continue; + if (startToSegmentMap.containsKey(pos)) { + prevSegStart = pos; + continue; + } + + final ProgressStyle.Segment prevSeg = startToSegmentMap.get(prevSegStart); + final ProgressStyle.Segment leftSeg = new ProgressStyle.Segment( + pos - prevSegStart).setColor( + prevSeg.getColor()); + final ProgressStyle.Segment rightSeg = new ProgressStyle.Segment( + prevSegStart + prevSeg.getLength() - pos).setColor(prevSeg.getColor()); + + startToSegmentMap.put(prevSegStart, leftSeg); + startToSegmentMap.put(pos, rightSeg); + + prevSegStart = pos; + } + + return startToSegmentMap; + } + + private static List convertToDrawableParts( + Map startToSegmentMap, + Map positionToPointMap, + SortedSet sortedPos, + int progress, + int progressMax, + boolean isStyledByProgress + ) { + List parts = new ArrayList<>(); + boolean styleRemainingParts = false; + for (Integer pos : sortedPos) { + if (positionToPointMap.containsKey(pos)) { + final ProgressStyle.Point point = positionToPointMap.get(pos); + final int color = maybeGetFadedColor(point.getColor(), styleRemainingParts); + parts.add(new Point(null, color, styleRemainingParts)); + } + // We want the Point at the current progress to be filled (not faded), but a Segment + // starting at this progress to be faded. + if (isStyledByProgress && !styleRemainingParts && pos == progress) { + styleRemainingParts = true; + } + if (startToSegmentMap.containsKey(pos)) { + final ProgressStyle.Segment seg = startToSegmentMap.get(pos); + final int color = maybeGetFadedColor(seg.getColor(), styleRemainingParts); + parts.add(new Segment( + (float) seg.getLength() / progressMax, color, styleRemainingParts)); + } + } + + return parts; + } + + @ColorInt + private static int maybeGetFadedColor(@ColorInt int color, boolean fade) { + if (!fade) return color; + + return NotificationProgressDrawable.getFadedColor(color); + } + + private static Map generateStartToSegmentMap( + List segments) { + final Map startToSegmentMap = new HashMap<>(); + + int currentStart = 0; // Initial start position is 0 + + for (ProgressStyle.Segment segment : segments) { + // Use the current start position as the key, and the segment as the value + startToSegmentMap.put(currentStart, segment); + + // Update the start position for the next segment + currentStart += segment.getLength(); + } + + return startToSegmentMap; + } + + private static Map generatePositionToPointMap( + List points) { + final Map positionToPointMap = new HashMap<>(); + + for (ProgressStyle.Point point : points) { + positionToPointMap.put(point.getPosition(), point); + } + + return positionToPointMap; + } + + private static SortedSet generateSortedPositionSet( + Map startToSegmentMap, + Map positionToPointMap, int progress, + boolean isStyledByProgress) { + final SortedSet sortedPos = new TreeSet<>(startToSegmentMap.keySet()); + sortedPos.addAll(positionToPointMap.keySet()); + if (isStyledByProgress) { + sortedPos.add(progress); + } + + return sortedPos; + } +} diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java new file mode 100644 index 0000000000000000000000000000000000000000..89ef8759a169566d9bd19d0fc756de9057fae12b --- /dev/null +++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java @@ -0,0 +1,801 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.content.pm.ActivityInfo.Config; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.DashPathEffect; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.Log; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Objects; + +/** + * This is used by NotificationProgressBar for displaying a custom background. It composes of + * segments, which have non-zero length, and points, which have zero length. + * + * @see Segment + * @see Point + */ +public final class NotificationProgressDrawable extends Drawable { + private static final String TAG = "NotifProgressDrawable"; + + private State mState; + private boolean mMutated; + + private final ArrayList mParts = new ArrayList<>(); + + private final Rect mPointRect = new Rect(); + private final RectF mPointRectF = new RectF(); + + private final Paint mStrokePaint = new Paint(); + private final Paint mDashedStrokePaint = new Paint(); + private final Paint mFillPaint = new Paint(); + + { + mStrokePaint.setStyle(Paint.Style.STROKE); + mStrokePaint.setStrokeCap(Paint.Cap.ROUND); + + mDashedStrokePaint.setStyle(Paint.Style.STROKE); + + mFillPaint.setStyle(Paint.Style.FILL); + } + + private int mAlpha; + + public NotificationProgressDrawable() { + this(new State(), null); + } + + /** + *

        Set the stroke width and default color for the drawable.

        + *

        Note: changing this property will affect all instances of a drawable loaded from a + * resource. It is recommended to invoke + * {@link #mutate()} before changing this property.

        + * + * @param width The width in pixels of the stroke + * @param color The color of the stroke + * @see #mutate() + * @see #setStroke(int, int, float, float) + */ + public void setStroke(int width, @ColorInt int color) { + setStroke(width, color, 0, 0); + } + + /** + *

        Set the stroke width and default color for the drawable. This method can also be used + * to dash the stroke for the dashed segments.

        + *

        Note: changing this property will affect all instances of a drawable loaded from a + * resource. It is recommended to invoke {@link #mutate()} before changing this property.

        + * + * @param width The width in pixels of the stroke + * @param color The color of the stroke + * @param dashWidth The length in pixels of the dashes, set to 0 to disable dashes + * @param dashGap The gap in pixels between dashes + * @see #mutate() + * @see #setStroke(int, int) + */ + public void setStroke(int width, @ColorInt int color, float dashWidth, float dashGap) { + mState.setStroke(width, color, dashWidth, dashGap); + setStrokeInternal(width, dashWidth, dashGap); + } + + /** + *

        Set the stroke default color for the drawable.

        + *

        Note: changing this property will affect all instances of a drawable loaded from a + * resource. It is recommended to invoke {@link #mutate()} before changing this property.

        + * + * @param color The color of the stroke + * @see #mutate() + * @see #setStroke(int, int, float, float) + */ + public void setStrokeDefaultColor(@ColorInt int color) { + mState.setStrokeColor(color); + } + + /** + *

        Set the point rect default color for the drawable.

        + *

        Note: changing this property will affect all instances of a drawable loaded from a + * resource. It is recommended to invoke {@link #mutate()} before changing this property.

        + * + * @param color The color of the point rect + * @see #mutate() + */ + public void setPointRectDefaultColor(@ColorInt int color) { + mState.setPointRectColor(color); + } + + private void setStrokeInternal(int width, float dashWidth, float dashGap) { + mStrokePaint.setStrokeWidth(width); + + mDashedStrokePaint.setStrokeWidth(width); + DashPathEffect e = null; + if (dashWidth > 0) { + e = new DashPathEffect(new float[] { dashWidth, dashGap }, 0); + } + mDashedStrokePaint.setPathEffect(e); + + invalidateSelf(); + } + + /** + * + */ + public void setParts(@NonNull Part... parts) { + mParts.clear(); + mParts.addAll(Arrays.asList(parts)); + } + + @Override + public void draw(@NonNull Canvas canvas) { + final float pointRadius = + mState.mPointRadius; // how big the point icon will be, halved + + // generally, we will start drawing at (x, y) and end at (x+w, y) + float x = (float) getBounds().left; + final float centerY = (float) getBounds().centerY(); + final float totalWidth = (float) getBounds().width(); + + final int numParts = mParts.size(); + for (int iPart = 0; iPart < numParts; iPart++) { + final Part part = mParts.get(iPart); + final Part prevPart = iPart == 0 ? null : mParts.get(iPart - 1); + final Part nextPart = iPart + 1 == numParts ? null : mParts.get(iPart + 1); + if (part instanceof Segment segment) { + final float segWidth = segment.mFraction * totalWidth; + // Advance the start position to account for a point immediately prior. + final float startOffset = getSegStartOffset(prevPart, pointRadius, + mState.mSegPointGap); + final float start = x + startOffset; + // Retract the end position to account for the padding and a point immediately + // after. + final float endOffset = getSegEndOffset(nextPart, pointRadius, mState.mSegPointGap, + mState.mSegSegGap); + final float end = x + segWidth - endOffset; + + // Transparent is not allowed (and also is the default in the data), so use that + // as a sentinel to be replaced by default + mStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor + : mState.mStrokeColor); + mDashedStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor + : mState.mFadedStrokeColor); + + // Leave space for the rounded line cap which extends beyond start/end. + final float capWidth = mStrokePaint.getStrokeWidth() / 2F; + + canvas.drawLine(start + capWidth, centerY, end - capWidth, centerY, + segment.mDashed ? mDashedStrokePaint : mStrokePaint); + + // Advance the current position to account for the segment's fraction of the total + // width (ignoring offset and padding) + x += segWidth; + } else if (part instanceof Point point) { + mPointRect.set((int) (x - pointRadius), (int) (centerY - pointRadius), + (int) (x + pointRadius), (int) (centerY + pointRadius)); + if (point.mIcon != null) { + point.mIcon.setBounds(mPointRect); + point.mIcon.draw(canvas); + } else { + // TODO: b/367804171 - actually use a vector asset for the default point + // rather than drawing it as a box? + mPointRectF.set(mPointRect); + final float inset = mState.mPointRectInset; + final float cornerRadius = mState.mPointRectCornerRadius; + mPointRectF.inset(inset, inset); + + mFillPaint.setColor(point.mColor != Color.TRANSPARENT ? point.mColor + : (point.mFaded ? mState.mFadedPointRectColor + : mState.mPointRectColor)); + + canvas.drawRoundRect(mPointRectF, cornerRadius, cornerRadius, mFillPaint); + } + } + } + } + + private static float getSegStartOffset(Part prevPart, float pointRadius, float segPointGap) { + return (prevPart instanceof Point) ? pointRadius + segPointGap : 0F; + } + + private static float getSegEndOffset(Part nextPart, float pointRadius, float segPointGap, + float segSegGap) { + if (nextPart == null) return 0F; + return (nextPart instanceof Point) ? segPointGap + pointRadius : segSegGap; + } + + @Override + public @Config int getChangingConfigurations() { + return super.getChangingConfigurations() | mState.getChangingConfigurations(); + } + + @Override + public void setAlpha(int alpha) { + if (mAlpha != alpha) { + mAlpha = alpha; + invalidateSelf(); + } + } + + @Override + public int getAlpha() { + return mAlpha; + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + // NO-OP + } + + @Override + public int getOpacity() { + // This method is deprecated. Hence we return UNKNOWN. + return PixelFormat.UNKNOWN; + } + + @Override + public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser, + @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) + throws XmlPullParserException, IOException { + super.inflate(r, parser, attrs, theme); + + mState.setDensity(resolveDensity(r, 0)); + + final TypedArray a = obtainAttributes(r, theme, attrs, + R.styleable.NotificationProgressDrawable); + updateStateFromTypedArray(a); + a.recycle(); + + inflateChildElements(r, parser, attrs, theme); + + updateLocalState(); + } + + @Override + public void applyTheme(@NonNull Theme t) { + super.applyTheme(t); + + final State state = mState; + if (state == null) { + return; + } + + state.setDensity(resolveDensity(t.getResources(), 0)); + + if (state.mThemeAttrs != null) { + final TypedArray a = t.resolveAttributes( + state.mThemeAttrs, R.styleable.NotificationProgressDrawable); + updateStateFromTypedArray(a); + a.recycle(); + } + + applyThemeChildElements(t); + + updateLocalState(); + } + + @Override + public boolean canApplyTheme() { + return (mState.canApplyTheme()) || super.canApplyTheme(); + } + + private void updateStateFromTypedArray(TypedArray a) { + final State state = mState; + + // Account for any configuration changes. + state.mChangingConfigurations |= a.getChangingConfigurations(); + + // Extract the theme attributes, if any. + state.mThemeAttrs = a.extractThemeAttrs(); + + state.mSegSegGap = a.getDimension(R.styleable.NotificationProgressDrawable_segSegGap, + state.mSegSegGap); + state.mSegPointGap = a.getDimension(R.styleable.NotificationProgressDrawable_segPointGap, + state.mSegPointGap); + } + + private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs, + Theme theme) throws XmlPullParserException, IOException { + TypedArray a; + int type; + + final int innerDepth = parser.getDepth() + 1; + int depth; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth = parser.getDepth()) >= innerDepth + || type != XmlPullParser.END_TAG)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + if (depth > innerDepth) { + continue; + } + + String name = parser.getName(); + + if (name.equals("segments")) { + a = obtainAttributes(r, theme, attrs, + R.styleable.NotificationProgressDrawableSegments); + updateSegmentsFromTypedArray(a); + a.recycle(); + } else if (name.equals("points")) { + a = obtainAttributes(r, theme, attrs, + R.styleable.NotificationProgressDrawablePoints); + updatePointsFromTypedArray(a); + a.recycle(); + } else { + Log.w(TAG, "Bad element under NotificationProgressDrawable: " + name); + } + } + } + + private void applyThemeChildElements(Theme t) { + final State state = mState; + + if (state.mThemeAttrsSegments != null) { + final TypedArray a = t.resolveAttributes( + state.mThemeAttrsSegments, R.styleable.NotificationProgressDrawableSegments); + updateSegmentsFromTypedArray(a); + a.recycle(); + } + + if (state.mThemeAttrsPoints != null) { + final TypedArray a = t.resolveAttributes( + state.mThemeAttrsPoints, R.styleable.NotificationProgressDrawablePoints); + updateSegmentsFromTypedArray(a); + a.recycle(); + } + } + + private void updateSegmentsFromTypedArray(TypedArray a) { + final State state = mState; + + // Account for any configuration changes. + state.mChangingConfigurations |= a.getChangingConfigurations(); + + // Extract the theme attributes, if any. + state.mThemeAttrsSegments = a.extractThemeAttrs(); + + final int width = a.getDimensionPixelSize( + R.styleable.NotificationProgressDrawableSegments_width, state.mStrokeWidth); + final float dashWidth = a.getDimension( + R.styleable.NotificationProgressDrawableSegments_dashWidth, state.mStrokeDashWidth); + + final int color = a.getColor(R.styleable.NotificationProgressDrawableSegments_color, + state.mStrokeColor); + + if (dashWidth != 0.0f) { + final float dashGap = a.getDimension( + R.styleable.NotificationProgressDrawableSegments_dashGap, state.mStrokeDashGap); + setStroke(width, color, dashWidth, dashGap); + } else { + setStroke(width, color); + } + } + + private void updatePointsFromTypedArray(TypedArray a) { + final State state = mState; + + // Account for any configuration changes. + state.mChangingConfigurations |= a.getChangingConfigurations(); + + // Extract the theme attributes, if any. + state.mThemeAttrsPoints = a.extractThemeAttrs(); + + state.mPointRadius = a.getDimension(R.styleable.NotificationProgressDrawablePoints_radius, + state.mPointRadius); + state.mPointRectInset = a.getDimension(R.styleable.NotificationProgressDrawablePoints_inset, + state.mPointRectInset); + state.mPointRectCornerRadius = a.getDimension( + R.styleable.NotificationProgressDrawablePoints_cornerRadius, + state.mPointRectCornerRadius); + final int color = a.getColor(R.styleable.NotificationProgressDrawablePoints_color, + state.mPointRectColor); + setPointRectDefaultColor(color); + } + + static int resolveDensity(@Nullable Resources r, int parentDensity) { + final int densityDpi = r == null ? parentDensity : r.getDisplayMetrics().densityDpi; + return densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi; + } + + /** + * Scales a floating-point pixel value from the source density to the + * target density. + */ + private static float scaleFromDensity(float pixels, int sourceDensity, int targetDensity) { + return pixels * targetDensity / sourceDensity; + } + + /** + * Scales a pixel value from the source density to the target density. + *

        + * Optionally, when {@code isSize} is true, handles the resulting pixel value as a size, + * which is rounded to the closest positive integer. + *

        + * Note: Iteratively applying density changes could result in drift of the pixel values due + * to rounding, especially for paddings which are truncated. Therefore it should be avoided. + * This isn't an issue for the notifications because the inflation pipeline reinflates + * notification views on density change. + */ + private static int scaleFromDensity( + int pixels, int sourceDensity, int targetDensity, boolean isSize) { + if (pixels == 0 || sourceDensity == targetDensity) { + return pixels; + } + + final float result = pixels * targetDensity / (float) sourceDensity; + if (!isSize) { + return (int) result; + } + + final int rounded = Math.round(result); + if (rounded != 0) { + return rounded; + } else if (pixels > 0) { + return 1; + } else { + return -1; + } + } + + /** + * A part of the progress bar, which is either a S{@link Segment} with non-zero length, or a + * {@link Point} with zero length. + */ + public interface Part { + } + + /** + * A segment is a part of the progress bar with non-zero length. For example, it can + * represent a portion in a navigation journey with certain traffic condition. + */ + public static final class Segment implements Part { + private final float mFraction; + @ColorInt private final int mColor; + private final boolean mDashed; + + public Segment(float fraction) { + this(fraction, Color.TRANSPARENT); + } + + public Segment(float fraction, @ColorInt int color) { + this(fraction, color, false); + } + + public Segment(float fraction, @ColorInt int color, boolean dashed) { + mFraction = fraction; + mColor = color; + mDashed = dashed; + } + + public float getFraction() { + return this.mFraction; + } + + public int getColor() { + return this.mColor; + } + + public boolean getDashed() { + return this.mDashed; + } + + @Override + public String toString() { + return "Segment(fraction=" + this.mFraction + ", color=" + this.mColor + ", dashed=" + + this.mDashed + ')'; + } + + // Needed for unit tests + @Override + public boolean equals(@Nullable Object other) { + if (this == other) return true; + + if (other == null || getClass() != other.getClass()) return false; + + Segment that = (Segment) other; + if (Float.compare(this.mFraction, that.mFraction) != 0) return false; + if (this.mColor != that.mColor) return false; + return this.mDashed == that.mDashed; + } + + @Override + public int hashCode() { + return Objects.hash(mFraction, mColor, mDashed); + } + } + + /** + * A point is a part of the progress bar with zero length. Points are designated points within a + * progressbar to visualize distinct stages or milestones. For example, a stop in a multi-stop + * ride-share journey. + */ + public static final class Point implements Part { + @Nullable + private final Drawable mIcon; + @ColorInt private final int mColor; + private final boolean mFaded; + + public Point(@Nullable Drawable icon) { + this(icon, Color.TRANSPARENT, false); + } + + public Point(@Nullable Drawable icon, @ColorInt int color) { + this(icon, color, false); + + } + + public Point(@Nullable Drawable icon, @ColorInt int color, boolean faded) { + mIcon = icon; + mColor = color; + mFaded = faded; + } + + @Nullable + public Drawable getIcon() { + return this.mIcon; + } + + public int getColor() { + return this.mColor; + } + + public boolean getFaded() { + return this.mFaded; + } + + @Override + public String toString() { + return "Point(icon=" + this.mIcon + ", color=" + this.mColor + ", faded=" + this.mFaded + + ")"; + } + + // Needed for unit tests. + @Override + public boolean equals(@Nullable Object other) { + if (this == other) return true; + + if (other == null || getClass() != other.getClass()) return false; + + Point that = (Point) other; + + if (!Objects.equals(this.mIcon, that.mIcon)) return false; + if (this.mColor != that.mColor) return false; + return this.mFaded == that.mFaded; + } + + @Override + public int hashCode() { + return Objects.hash(mIcon, mColor, mFaded); + } + } + + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mState = new State(mState, null); + updateLocalState(); + mMutated = true; + } + return this; + } + + @Override + public void clearMutated() { + super.clearMutated(); + mMutated = false; + } + + static final class State extends ConstantState { + @Config + int mChangingConfigurations; + float mSegSegGap = 0.0f; + float mSegPointGap = 0.0f; + int mStrokeWidth = 0; + int mStrokeColor; + int mFadedStrokeColor; + float mStrokeDashWidth = 0.0f; + float mStrokeDashGap = 0.0f; + float mPointRadius; + float mPointRectInset; + float mPointRectCornerRadius; + int mPointRectColor; + int mFadedPointRectColor; + + int[] mThemeAttrs; + int[] mThemeAttrsSegments; + int[] mThemeAttrsPoints; + + int mDensity = DisplayMetrics.DENSITY_DEFAULT; + + State() { + } + + State(@NonNull State orig, @Nullable Resources res) { + mChangingConfigurations = orig.mChangingConfigurations; + mStrokeColor = orig.mStrokeColor; + mFadedStrokeColor = orig.mFadedStrokeColor; + mStrokeWidth = orig.mStrokeWidth; + mStrokeDashWidth = orig.mStrokeDashWidth; + mStrokeDashGap = orig.mStrokeDashGap; + mPointRadius = orig.mPointRadius; + mPointRectInset = orig.mPointRectInset; + mPointRectCornerRadius = orig.mPointRectCornerRadius; + mPointRectColor = orig.mPointRectColor; + mFadedPointRectColor = orig.mFadedPointRectColor; + + mThemeAttrs = orig.mThemeAttrs; + mThemeAttrsSegments = orig.mThemeAttrsSegments; + mThemeAttrsPoints = orig.mThemeAttrsPoints; + + mDensity = resolveDensity(res, orig.mDensity); + if (orig.mDensity != mDensity) { + applyDensityScaling(orig.mDensity, mDensity); + } + } + + private void applyDensityScaling(int sourceDensity, int targetDensity) { + if (mStrokeWidth > 0) { + mStrokeWidth = scaleFromDensity( + mStrokeWidth, sourceDensity, targetDensity, true); + } + if (mStrokeDashWidth > 0) { + mStrokeDashWidth = scaleFromDensity( + mStrokeDashWidth, sourceDensity, targetDensity); + } + if (mStrokeDashGap > 0) { + mStrokeDashGap = scaleFromDensity( + mStrokeDashGap, sourceDensity, targetDensity); + } + if (mPointRadius > 0) { + mPointRadius = scaleFromDensity( + mPointRadius, sourceDensity, targetDensity); + } + if (mPointRectInset > 0) { + mPointRectInset = scaleFromDensity( + mPointRectInset, sourceDensity, targetDensity); + } + if (mPointRectCornerRadius > 0) { + mPointRectCornerRadius = scaleFromDensity( + mPointRectCornerRadius, sourceDensity, targetDensity); + } + } + + @NonNull + @Override + public Drawable newDrawable() { + return new NotificationProgressDrawable(this, null); + } + + @Override + public Drawable newDrawable(@Nullable Resources res) { + // If this drawable is being created for a different density, + // just create a new constant state and call it a day. + final State state; + final int density = resolveDensity(res, mDensity); + if (density != mDensity) { + state = new State(this, res); + } else { + state = this; + } + + return new NotificationProgressDrawable(state, res); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + @Override + public boolean canApplyTheme() { + return mThemeAttrs != null || mThemeAttrsSegments != null || mThemeAttrsPoints != null + || super.canApplyTheme(); + } + + public void setDensity(int targetDensity) { + if (mDensity != targetDensity) { + final int sourceDensity = mDensity; + mDensity = targetDensity; + + applyDensityScaling(sourceDensity, targetDensity); + } + } + + public void setStroke(int width, int color, float dashWidth, float dashGap) { + mStrokeWidth = width; + mStrokeDashWidth = dashWidth; + mStrokeDashGap = dashGap; + + setStrokeColor(color); + } + + public void setStrokeColor(int color) { + mStrokeColor = color; + mFadedStrokeColor = getFadedColor(color); + } + + public void setPointRectColor(int color) { + mPointRectColor = color; + mFadedPointRectColor = getFadedColor(color); + } + } + + /** + * Get a color with an opacity that's 50% of the input color. + */ + @ColorInt + static int getFadedColor(@ColorInt int color) { + return Color.argb(Color.alpha(color) / 2, Color.red(color), Color.green(color), + Color.blue(color)); + } + + @Override + public ConstantState getConstantState() { + mState.mChangingConfigurations = getChangingConfigurations(); + return mState; + } + + /** + * Creates a new themed NotificationProgressDrawable based on the specified constant state. + *

        + * The resulting drawable is guaranteed to have a new constant state. + * + * @param state Constant state from which the drawable inherits + */ + private NotificationProgressDrawable(@NonNull State state, @Nullable Resources res) { + mState = state; + + updateLocalState(); + } + + private void updateLocalState() { + final State state = mState; + + mStrokePaint.setStrokeWidth(state.mStrokeWidth); + + if (state.mStrokeDashWidth != 0.0f) { + final DashPathEffect e = new DashPathEffect( + new float[] { state.mStrokeDashWidth, state.mStrokeDashGap }, 0); + mDashedStrokePaint.setPathEffect(e); + } + } +} diff --git a/core/java/com/android/internal/widget/NotificationProgressModel.java b/core/java/com/android/internal/widget/NotificationProgressModel.java new file mode 100644 index 0000000000000000000000000000000000000000..e51ea99ac4f5ab9557a68d8af1f7d8339ed8f257 --- /dev/null +++ b/core/java/com/android/internal/widget/NotificationProgressModel.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + + +import android.annotation.ColorInt; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.app.Flags; +import android.app.Notification; +import android.app.Notification.ProgressStyle.Point; +import android.app.Notification.ProgressStyle.Segment; +import android.graphics.Color; +import android.os.Bundle; + +import com.android.internal.util.Preconditions; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Data model for {@link NotificationProgressBar}. + * + * This class holds the necessary data to render the notification progressbar. + * It is used to bind the progress style progress data to {@link NotificationProgressBar}. + * + * @hide + * @see NotificationProgressModel#toBundle + * @see NotificationProgressModel#fromBundle + */ +@FlaggedApi(Flags.FLAG_API_RICH_ONGOING) +public final class NotificationProgressModel { + private static final int INVALID_INDETERMINATE_COLOR = Color.TRANSPARENT; + private static final String KEY_SEGMENTS = "segments"; + private static final String KEY_POINTS = "points"; + private static final String KEY_PROGRESS = "progress"; + private static final String KEY_IS_STYLED_BY_PROGRESS = "isStyledByProgress"; + private static final String KEY_INDETERMINATE_COLOR = "indeterminateColor"; + private final List mSegments; + private final List mPoints; + private final int mProgress; + private final boolean mIsStyledByProgress; + @ColorInt + private final int mIndeterminateColor; + + public NotificationProgressModel( + @NonNull List segments, + @NonNull List points, + int progress, + boolean isStyledByProgress + ) { + Preconditions.checkArgument(progress >= 0); + Preconditions.checkArgument(!segments.isEmpty()); + mSegments = segments; + mPoints = points; + mProgress = progress; + mIsStyledByProgress = isStyledByProgress; + mIndeterminateColor = INVALID_INDETERMINATE_COLOR; + } + + public NotificationProgressModel( + @ColorInt int indeterminateColor + ) { + Preconditions.checkArgument(indeterminateColor != INVALID_INDETERMINATE_COLOR); + mSegments = Collections.emptyList(); + mPoints = Collections.emptyList(); + mProgress = 0; + mIsStyledByProgress = false; + mIndeterminateColor = indeterminateColor; + } + + public List getSegments() { + return mSegments; + } + + public List getPoints() { + return mPoints; + } + + public int getProgress() { + return mProgress; + } + + public boolean isStyledByProgress() { + return mIsStyledByProgress; + } + + @ColorInt + public int getIndeterminateColor() { + return mIndeterminateColor; + } + + public boolean isIndeterminate() { + return mIndeterminateColor != INVALID_INDETERMINATE_COLOR; + } + + /** + * Returns a {@link Bundle} representation of this {@link NotificationProgressModel}. + */ + @NonNull + public Bundle toBundle() { + final Bundle bundle = new Bundle(); + if (mIndeterminateColor != INVALID_INDETERMINATE_COLOR) { + bundle.putInt(KEY_INDETERMINATE_COLOR, mIndeterminateColor); + } else { + bundle.putParcelableList(KEY_SEGMENTS, + Notification.ProgressStyle.getProgressSegmentsAsBundleList(mSegments)); + bundle.putParcelableList(KEY_POINTS, + Notification.ProgressStyle.getProgressPointsAsBundleList(mPoints)); + bundle.putInt(KEY_PROGRESS, mProgress); + bundle.putBoolean(KEY_IS_STYLED_BY_PROGRESS, mIsStyledByProgress); + } + return bundle; + } + + /** + * Creates a {@link NotificationProgressModel} from a {@link Bundle}. + */ + @NonNull + public static NotificationProgressModel fromBundle(@NonNull Bundle bundle) { + final int indeterminateColor = bundle.getInt(KEY_INDETERMINATE_COLOR, + INVALID_INDETERMINATE_COLOR); + if (indeterminateColor != INVALID_INDETERMINATE_COLOR) { + return new NotificationProgressModel(indeterminateColor); + } else { + final List segments = + Notification.ProgressStyle.getProgressSegmentsFromBundleList( + bundle.getParcelableArrayList(KEY_SEGMENTS, Bundle.class)); + final List points = + Notification.ProgressStyle.getProgressPointsFromBundleList( + bundle.getParcelableArrayList(KEY_POINTS, Bundle.class)); + final int progress = bundle.getInt(KEY_PROGRESS); + final boolean isStyledByProgress = bundle.getBoolean(KEY_IS_STYLED_BY_PROGRESS); + return new NotificationProgressModel(segments, points, progress, isStyledByProgress); + } + } + + @Override + public String toString() { + return "NotificationProgressModel{" + + "mSegments=" + mSegments + + ", mPoints=" + mPoints + + ", mProgress=" + mProgress + + ", mIsStyledByProgress=" + mIsStyledByProgress + + ", mIndeterminateColor=" + mIndeterminateColor + "}"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final NotificationProgressModel that = (NotificationProgressModel) o; + return mProgress == that.mProgress + && mIsStyledByProgress == that.mIsStyledByProgress + && mIndeterminateColor == that.mIndeterminateColor + && Objects.equals(mSegments, that.mSegments) + && Objects.equals(mPoints, that.mPoints); + } + + @Override + public int hashCode() { + return Objects.hash(mSegments, + mPoints, + mProgress, + mIsStyledByProgress, + mIndeterminateColor); + } +} diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index c0a7383c9f06a26f1262d0ce9802ddd2a4ca4f19..46855c64d6ae5094631e31b5ead988bcfd092665 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -45,6 +45,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.RoundedCorner; +import android.view.Surface; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; @@ -84,7 +85,7 @@ public class PointerLocationView extends View implements InputDeviceListener, private boolean mCurDown; // Most recent coordinates. - private PointerCoords mCoords = new PointerCoords(); + private final PointerCoords mCoords = new PointerCoords(); private int mToolType; // Most recent velocity. @@ -104,7 +105,7 @@ public class PointerLocationView extends View implements InputDeviceListener, public PointerState() { } - public void addTrace(float x, float y, boolean isHistorical) { + void addTrace(float x, float y, boolean isHistorical) { if (Float.isNaN(mFirstX)) { mFirstX = x; } @@ -148,8 +149,9 @@ public class PointerLocationView extends View implements InputDeviceListener, // Draw the trace of all pointers in the current gesture in a separate layer // that is not cleared on every frame so that we don't have to re-draw the - // entire trace on each frame. - private final Bitmap mTraceBitmap; + // entire trace on each frame. The trace bitmap is in the coordinate space of the unrotated + // display. + private Bitmap mTraceBitmap; private final Canvas mTraceCanvas; private final Region mSystemGestureExclusion = new Region(); @@ -200,9 +202,8 @@ public class PointerLocationView extends View implements InputDeviceListener, mPathPaint.setARGB(255, 0, 96, 255); mPathPaint.setStyle(Paint.Style.STROKE); - mTraceBitmap = Bitmap.createBitmap(getResources().getDisplayMetrics().widthPixels, - getResources().getDisplayMetrics().heightPixels, Bitmap.Config.ARGB_8888); - mTraceCanvas = new Canvas(mTraceBitmap); + mTraceCanvas = new Canvas(); + configureTraceBitmap(); configureDensityDependentFactors(); @@ -275,7 +276,7 @@ public class PointerLocationView extends View implements InputDeviceListener, // Draw an oval. When angle is 0 radians, orients the major axis vertically, // angles less than or greater than 0 radians rotate the major axis left or right. - private RectF mReusableOvalRect = new RectF(); + private final RectF mReusableOvalRect = new RectF(); private void drawOval(Canvas canvas, float x, float y, float major, float minor, float angle, Paint paint) { @@ -293,7 +294,11 @@ public class PointerLocationView extends View implements InputDeviceListener, protected void onDraw(Canvas canvas) { final int NP = mPointers.size(); + // Pointer trace. + canvas.save(); + rotateCanvasToUnrotatedDisplay(canvas); canvas.drawBitmap(mTraceBitmap, 0, 0, null); + canvas.restore(); if (!mSystemGestureExclusion.isEmpty()) { mSystemGestureExclusionPath.reset(); @@ -310,7 +315,9 @@ public class PointerLocationView extends View implements InputDeviceListener, // Labels drawLabels(canvas); - // Pointer trace. + // Current pointer states. + canvas.save(); + rotateCanvasToUnrotatedDisplay(canvas); for (int p = 0; p < NP; p++) { final PointerState ps = mPointers.valueAt(p); float lastX = ps.mCurrentX, lastY = ps.mCurrentY; @@ -393,6 +400,7 @@ public class PointerLocationView extends View implements InputDeviceListener, } } } + canvas.restore(); } private void drawLabels(Canvas canvas) { @@ -572,6 +580,11 @@ public class PointerLocationView extends View implements InputDeviceListener, @Override public void onPointerEvent(MotionEvent event) { + // PointerLocationView stores and draws events in the unrotated display space, so undo the + // event's rotation to bring it back to the unrotated display space. + event.transform(MotionEvent.createRotateMatrix(inverseRotation(event.getSurfaceRotation()), + mTraceBitmap.getWidth(), mTraceBitmap.getHeight())); + final int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN @@ -633,7 +646,7 @@ public class PointerLocationView extends View implements InputDeviceListener, logCoords("Pointer", action, i, coords, id, event); } if (ps != null) { - ps.addTrace(coords.x, coords.y, true); + ps.addTrace(coords.x, coords.y, /*isHistorical*/ true); updateDrawTrace(ps); } } @@ -647,7 +660,7 @@ public class PointerLocationView extends View implements InputDeviceListener, logCoords("Pointer", action, i, coords, id, event); } if (ps != null) { - ps.addTrace(coords.x, coords.y, false); + ps.addTrace(coords.x, coords.y, /*isHistorical*/ false); updateDrawTrace(ps); ps.mXVelocity = mVelocity.getXVelocity(id); ps.mYVelocity = mVelocity.getYVelocity(id); @@ -704,11 +717,12 @@ public class PointerLocationView extends View implements InputDeviceListener, float y = ps.mCurrentY; float lastX = ps.mPreviousX; float lastY = ps.mPreviousY; - if (!Float.isNaN(x) && !Float.isNaN(y) && !Float.isNaN(lastX) && !Float.isNaN(lastY)) { - mTraceCanvas.drawLine(lastX, lastY, x, y, mPathPaint); - Paint paint = ps.mPreviousPointIsHistorical ? mPaint : mCurrentPointPaint; - mTraceCanvas.drawPoint(lastX, lastY, paint); + if (Float.isNaN(x) || Float.isNaN(y) || Float.isNaN(lastX) || Float.isNaN(lastY)) { + return; } + mTraceCanvas.drawLine(lastX, lastY, x, y, mPathPaint); + Paint paint = ps.mPreviousPointIsHistorical ? mPaint : mCurrentPointPaint; + mTraceCanvas.drawPoint(lastX, lastY, paint); } @Override @@ -973,7 +987,7 @@ public class PointerLocationView extends View implements InputDeviceListener, } } - private ISystemGestureExclusionListener mSystemGestureExclusionListener = + private final ISystemGestureExclusionListener mSystemGestureExclusionListener = new ISystemGestureExclusionListener.Stub() { @Override public void onSystemGestureExclusionChanged(int displayId, @@ -1000,6 +1014,7 @@ public class PointerLocationView extends View implements InputDeviceListener, @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); + configureTraceBitmap(); configureDensityDependentFactors(); } @@ -1011,4 +1026,58 @@ public class PointerLocationView extends View implements InputDeviceListener, mCurrentPointPaint.setStrokeWidth(1 * mDensity); mPathPaint.setStrokeWidth(1 * mDensity); } + + private void configureTraceBitmap() { + final var display = mContext.getDisplay(); + final boolean rotated = display.getRotation() == Surface.ROTATION_90 + || display.getRotation() == Surface.ROTATION_270; + int unrotatedWidth = rotated ? display.getHeight() : display.getWidth(); + int unrotatedHeight = rotated ? display.getWidth() : display.getHeight(); + + if (mTraceBitmap != null && mTraceBitmap.getWidth() == unrotatedWidth + && mTraceBitmap.getHeight() == unrotatedHeight) { + return; + } + if (unrotatedWidth <= 0 || unrotatedHeight <= 0) { + Slog.w(TAG, "Ignoring configuration: invalid display size: " + unrotatedWidth + "x" + + unrotatedHeight); + // Initialize the bitmap to an arbitrary size. It should be reconfigured with a valid + // size in the future. + unrotatedWidth = 100; + unrotatedHeight = 100; + } + mTraceBitmap = Bitmap.createBitmap(unrotatedWidth, unrotatedHeight, + Bitmap.Config.ARGB_8888); + mTraceCanvas.setBitmap(mTraceBitmap); + } + + private static int inverseRotation(@Surface.Rotation int rotation) { + return switch(rotation) { + case Surface.ROTATION_0 -> Surface.ROTATION_0; + case Surface.ROTATION_90 -> Surface.ROTATION_270; + case Surface.ROTATION_180 -> Surface.ROTATION_180; + case Surface.ROTATION_270 -> Surface.ROTATION_90; + default -> { + Slog.e(TAG, "Received unexpected surface rotation: " + rotation); + yield Surface.ROTATION_0; + } + }; + } + + private void rotateCanvasToUnrotatedDisplay(Canvas c) { + switch (inverseRotation(mContext.getDisplay().getRotation())) { + case Surface.ROTATION_90 -> { + c.rotate(90); + c.translate(0, -mTraceBitmap.getHeight()); + } + case Surface.ROTATION_180 -> { + c.rotate(180); + c.translate(-mTraceBitmap.getWidth(), -mTraceBitmap.getHeight()); + } + case Surface.ROTATION_270 -> { + c.rotate(270); + c.translate(-mTraceBitmap.getWidth(), 0); + } + } + } } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 4d2195deebaeb738368d2d506c72c13729c8a1be..2bc32657bd4a83fbf07727dfb8b479364461e270 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -308,6 +308,7 @@ cc_library_shared_for_libandroid_runtime { "spatializer-aidl-cpp", "av-types-aidl-cpp", "android.hardware.camera.device@3.2", + "camera_platform_flags_c_lib", "libandroid_net", "libbattery", "libnetdutils", diff --git a/core/jni/OWNERS b/core/jni/OWNERS index af106235bd7722fde87471f888f78321857b659e..af393fdc5ad460b3a0e23bd3327cd7805c148396 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -119,3 +119,4 @@ per-file android_tracing_Perfetto* = file:platform/development:/tools/winscope/O # ApplicationSharedMemory per-file *ApplicationSharedMemory* = file:/PERFORMANCE_OWNERS +per-file *PropertyInvalidatedCache* = file:/PERFORMANCE_OWNERS diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 3f74fac35bb7b65a0a1f578d8540ab0fc88de786..10e49efee92e46ef11d68e24e78d116c1a6aeec9 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ #include "jni.h" using namespace android; +namespace flags = com::android::internal::camera::flags; enum { // Keep up to date with Camera.java @@ -527,14 +529,19 @@ void JNICameraContext::clearCallbackBuffers_l(JNIEnv *env, Vector *b } static bool attributionSourceStateForJavaParcel(JNIEnv *env, jobject jClientAttributionParcel, + bool useContextAttributionSource, AttributionSourceState &clientAttribution) { const Parcel *clientAttributionParcel = parcelForJavaObject(env, jClientAttributionParcel); if (clientAttribution.readFromParcel(clientAttributionParcel) != ::android::OK) { jniThrowRuntimeException(env, "Fail to unparcel AttributionSourceState"); return false; } - clientAttribution.uid = Camera::USE_CALLING_UID; - clientAttribution.pid = Camera::USE_CALLING_PID; + + if (!(useContextAttributionSource && flags::use_context_attribution_source())) { + clientAttribution.uid = Camera::USE_CALLING_UID; + clientAttribution.pid = Camera::USE_CALLING_PID; + } + return true; } @@ -542,7 +549,9 @@ static jint android_hardware_Camera_getNumberOfCameras(JNIEnv *env, jobject thiz jobject jClientAttributionParcel, jint devicePolicy) { AttributionSourceState clientAttribution; - if (!attributionSourceStateForJavaParcel(env, jClientAttributionParcel, clientAttribution)) { + if (!attributionSourceStateForJavaParcel(env, jClientAttributionParcel, + /* useContextAttributionSource= */ false, + clientAttribution)) { return 0; } return Camera::getNumberOfCameras(clientAttribution, devicePolicy); @@ -553,7 +562,9 @@ static void android_hardware_Camera_getCameraInfo(JNIEnv *env, jobject thiz, jin jobject jClientAttributionParcel, jint devicePolicy, jobject info_obj) { AttributionSourceState clientAttribution; - if (!attributionSourceStateForJavaParcel(env, jClientAttributionParcel, clientAttribution)) { + if (!attributionSourceStateForJavaParcel(env, jClientAttributionParcel, + /* useContextAttributionSource= */ false, + clientAttribution)) { return; } @@ -587,7 +598,9 @@ static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj jobject jClientAttributionParcel, jint devicePolicy) { AttributionSourceState clientAttribution; - if (!attributionSourceStateForJavaParcel(env, jClientAttributionParcel, clientAttribution)) { + if (!attributionSourceStateForJavaParcel(env, jClientAttributionParcel, + /* useContextAttributionSource= */ true, + clientAttribution)) { return -EACCES; } diff --git a/core/jni/android_hardware_OverlayProperties.cpp b/core/jni/android_hardware_OverlayProperties.cpp index 96494b16cc2156b13f6a81777ae4935a4d6267e8..63de1950f2a524f97e2ba676bfde349acb5c2fbc 100644 --- a/core/jni/android_hardware_OverlayProperties.cpp +++ b/core/jni/android_hardware_OverlayProperties.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "OverlayProperties" // #define LOG_NDEBUG 0 +#include #include #include #include @@ -35,6 +36,12 @@ static struct { jclass clazz; jmethodID ctor; } gOverlayPropertiesClassInfo; + +static struct { + jclass clazz; + jmethodID ctor; +} gLutPropertiesClassInfo; + // ---------------------------------------------------------------------------- // OverlayProperties lifecycle // ---------------------------------------------------------------------------- @@ -95,6 +102,36 @@ static jlong android_hardware_OverlayProperties_createDefault(JNIEnv* env, jobje return reinterpret_cast(overlayProperties); } +static jobjectArray android_hardware_OverlayProperties_getLutProperties(JNIEnv* env, jobject thiz, + jlong nativeObject) { + gui::OverlayProperties* overlayProperties = + reinterpret_cast(nativeObject); + if (overlayProperties->lutProperties.has_value()) { + return NULL; + } + auto& lutProperties = overlayProperties->lutProperties.value(); + if (lutProperties.empty()) { + return NULL; + } + int32_t size = static_cast(lutProperties.size()); + jobjectArray nativeLutProperties = + env->NewObjectArray(size, gLutPropertiesClassInfo.clazz, NULL); + if (nativeLutProperties == NULL) { + return NULL; + } + for (int32_t i = 0; i < size; i++) { + if (lutProperties[i].has_value()) { + auto& item = lutProperties[i].value(); + jobject properties = + env->NewObject(gLutPropertiesClassInfo.clazz, gLutPropertiesClassInfo.ctor, + static_cast(item.dimension), item.size, + item.samplingKeys.data()); + env->SetObjectArrayElement(nativeLutProperties, i, properties); + } + } + return nativeLutProperties; +} + // ---------------------------------------------------------------------------- // Serialization // ---------------------------------------------------------------------------- @@ -161,6 +198,8 @@ static const JNINativeMethod gMethods[] = { { "nReadOverlayPropertiesFromParcel", "(Landroid/os/Parcel;)J", (void*) android_hardware_OverlayProperties_read }, {"nCreateDefault", "()J", (void*) android_hardware_OverlayProperties_createDefault }, + {"nGetLutProperties", "(J)[Landroid/hardware/LutProperties;", + (void*) android_hardware_OverlayProperties_getLutProperties }, }; // clang-format on @@ -171,5 +210,9 @@ int register_android_hardware_OverlayProperties(JNIEnv* env) { gOverlayPropertiesClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); gOverlayPropertiesClassInfo.ctor = GetMethodIDOrDie(env, gOverlayPropertiesClassInfo.clazz, "", "(J)V"); + clazz = FindClassOrDie(env, "android/hardware/LutProperties"); + gLutPropertiesClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); + gLutPropertiesClassInfo.ctor = + GetMethodIDOrDie(env, gLutPropertiesClassInfo.clazz, "", "(IJ[I)V"); return err; } diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index a9b19062b7641d411f45a64b8640cb5e4b336f98..704aef3cd131381e67b7d6ad201690944e951fb3 100644 --- a/core/jni/android_media_AudioFormat.h +++ b/core/jni/android_media_AudioFormat.h @@ -50,6 +50,7 @@ #define ENCODING_DTS_HD_MA 29 #define ENCODING_DTS_UHD_P2 30 #define ENCODING_DSD 31 +#define ENCODING_AC4_L4 32 #define ENCODING_INVALID 0 #define ENCODING_DEFAULT 1 @@ -95,6 +96,8 @@ static inline audio_format_t audioFormatToNative(int audioFormat) return AUDIO_FORMAT_AAC_XHE; case ENCODING_AC4: return AUDIO_FORMAT_AC4; + case ENCODING_AC4_L4: + return AUDIO_FORMAT_AC4_L4; case ENCODING_E_AC3_JOC: return AUDIO_FORMAT_E_AC3_JOC; case ENCODING_DEFAULT: @@ -177,6 +180,8 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat) return ENCODING_AAC_XHE; case AUDIO_FORMAT_AC4: return ENCODING_AC4; + case AUDIO_FORMAT_AC4_L4: + return ENCODING_AC4_L4; case AUDIO_FORMAT_E_AC3_JOC: return ENCODING_E_AC3_JOC; case AUDIO_FORMAT_MAT: diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 71ba2147d984731392476bf1a9ab3422f732aae6..a939d9274956bcc1f557c246d6478bbb1885a77f 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -33,11 +33,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -736,6 +738,65 @@ static void nativeSetDesiredHdrHeadroom(JNIEnv* env, jclass clazz, jlong transac transaction->setDesiredHdrHeadroom(ctrl, desiredRatio); } +static void nativeSetLuts(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, + jfloatArray jbufferArray, jintArray joffsetArray, + jintArray jdimensionArray, jintArray jsizeArray, + jintArray jsamplingKeyArray) { + auto transaction = reinterpret_cast(transactionObj); + SurfaceControl* const ctrl = reinterpret_cast(nativeObject); + + ScopedIntArrayRW joffsets(env, joffsetArray); + if (joffsets.get() == nullptr) { + jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from joffsetArray"); + return; + } + ScopedIntArrayRW jdimensions(env, jdimensionArray); + if (jdimensions.get() == nullptr) { + jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jdimensionArray"); + return; + } + ScopedIntArrayRW jsizes(env, jsizeArray); + if (jsizes.get() == nullptr) { + jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsizeArray"); + return; + } + ScopedIntArrayRW jsamplingKeys(env, jsamplingKeyArray); + if (jsamplingKeys.get() == nullptr) { + jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsamplingKeyArray"); + return; + } + + jsize numLuts = env->GetArrayLength(jdimensionArray); + std::vector offsets(joffsets.get(), joffsets.get() + numLuts); + std::vector dimensions(jdimensions.get(), jdimensions.get() + numLuts); + std::vector sizes(jsizes.get(), jsizes.get() + numLuts); + std::vector samplingKeys(jsamplingKeys.get(), jsamplingKeys.get() + numLuts); + + ScopedFloatArrayRW jbuffers(env, jbufferArray); + if (jbuffers.get() == nullptr) { + jniThrowRuntimeException(env, "Failed to get ScopedFloatArrayRW from jbufferArray"); + return; + } + + // create the shared memory and copy jbuffers + size_t bufferSize = jbuffers.size() * sizeof(float); + int32_t fd = ashmem_create_region("lut_shread_mem", bufferSize); + if (fd < 0) { + jniThrowRuntimeException(env, "ashmem_create_region() failed"); + return; + } + void* ptr = mmap(nullptr, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + jniThrowRuntimeException(env, "Failed to map the shared memory"); + return; + } + memcpy(ptr, jbuffers.get(), bufferSize); + // unmap + munmap(ptr, bufferSize); + + transaction->setLuts(ctrl, base::unique_fd(fd), offsets, dimensions, sizes, samplingKeys); +} + static void nativeSetCachingHint(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jint cachingHint) { auto transaction = reinterpret_cast(transactionObj); @@ -2541,6 +2602,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*) nativeSetDesiredPresentTimeNanos }, {"nativeNotifyShutdown", "()V", (void*)nativeNotifyShutdown }, + {"nativeSetLuts", "(JJ[F[I[I[I[I)V", (void*)nativeSetLuts }, // clang-format on }; diff --git a/core/jni/android_view_WindowManagerGlobal.cpp b/core/jni/android_view_WindowManagerGlobal.cpp index abc621d8dc90b0d511c40a901db726f0a75d6aca..4202de39adb0fdce2f3c9293914c7b1679c02f01 100644 --- a/core/jni/android_view_WindowManagerGlobal.cpp +++ b/core/jni/android_view_WindowManagerGlobal.cpp @@ -69,8 +69,8 @@ void removeInputChannel(const sp& clientToken) { JNIEnv* env = AndroidRuntime::getJNIEnv(); ScopedLocalRef clientTokenObj(env, javaObjectForIBinder(env, clientToken)); - env->CallStaticObjectMethod(gWindowManagerGlobal.clazz, gWindowManagerGlobal.removeInputChannel, - clientTokenObj.get()); + env->CallStaticVoidMethod(gWindowManagerGlobal.clazz, gWindowManagerGlobal.removeInputChannel, + clientTokenObj.get()); } int register_android_view_WindowManagerGlobal(JNIEnv* env) { @@ -88,4 +88,4 @@ int register_android_view_WindowManagerGlobal(JNIEnv* env) { return NO_ERROR; } -} // namespace android \ No newline at end of file +} // namespace android diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp index 88b3e1c1ed9db73cc00cb24527f43693698d9940..06621c9e6ab37871ad01ed21e25be0caba61ed1e 100644 --- a/core/jni/platform/host/HostRuntime.cpp +++ b/core/jni/platform/host/HostRuntime.cpp @@ -20,7 +20,9 @@ #include #include #include +#include #include +#include #include #include @@ -64,8 +66,8 @@ static JNINativeMethod gMethods[] = { }; int register_libcore_util_NativeAllocationRegistry(JNIEnv* env) { - return jniRegisterNativeMethods(env, "libcore/util/NativeAllocationRegistry", gMethods, - NELEM(gMethods)); + return android::RegisterMethodsOrDie(env, "libcore/util/NativeAllocationRegistry", gMethods, + NELEM(gMethods)); } namespace android { @@ -115,9 +117,9 @@ static const std::unordered_map gRegJNIMap = { #ifdef __linux__ {"android.content.res.ApkAssets", REG_JNI(register_android_content_res_ApkAssets)}, {"android.content.res.AssetManager", REG_JNI(register_android_content_AssetManager)}, +#endif {"android.content.res.StringBlock", REG_JNI(register_android_content_StringBlock)}, {"android.content.res.XmlBlock", REG_JNI(register_android_content_XmlBlock)}, -#endif {"android.database.CursorWindow", REG_JNI(register_android_database_CursorWindow)}, {"android.database.sqlite.SQLiteConnection", REG_JNI(register_android_database_SQLiteConnection)}, @@ -259,35 +261,67 @@ static void* mmapFile(const char* dataFilePath) { #endif } -// Loads the ICU data file from the location specified in the system property ro.icu.data.path +// returns result from java.lang.System.getProperty +static string getJavaProperty(JNIEnv* env, const char* property_name) { + jclass system = FindClassOrDie(env, "java/lang/System"); + jmethodID getPropertyMethod = + GetStaticMethodIDOrDie(env, system, "getProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + + auto jString = (jstring)env->CallStaticObjectMethod(system, getPropertyMethod, + env->NewStringUTF(property_name), + env->NewStringUTF("")); + ScopedUtfChars chars(env, jString); + return string(chars.c_str()); +} + +static void loadIcuData(string icuPath) { + void* addr = mmapFile(icuPath.c_str()); + UErrorCode err = U_ZERO_ERROR; + udata_setCommonData(addr, &err); + if (err != U_ZERO_ERROR) { + ALOGE("Unable to load ICU data\n"); + } +} + +// Loads the ICU data file from the location specified in properties. +// First try specified in the system property ro.icu.data.path, +// then fallback to java property icu.data.path static void loadIcuData() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); string icuPath = base::GetProperty("ro.icu.data.path", ""); if (!icuPath.empty()) { - // Set the location of ICU data - void* addr = mmapFile(icuPath.c_str()); - UErrorCode err = U_ZERO_ERROR; - udata_setCommonData(addr, &err); - if (err != U_ZERO_ERROR) { - ALOGE("Unable to load ICU data\n"); + loadIcuData(icuPath); + } else { + // fallback to read from java.lang.System.getProperty + string icuPathFromJava = getJavaProperty(env, "icu.data.path"); + if (!icuPathFromJava.empty()) { + loadIcuData(icuPathFromJava); + } + } + + // Check for the ICU default locale property. In Libcore, the default ICU + // locale is set when ICU.setDefaultLocale is called, which is called by + // Libcore's implemenentation of Java's Locale.setDefault. The default + // locale is used in cases such as when ucol_open(NULL, ...) is called, for + // example in SQLite's 'COLLATE UNICODE'. + string icuLocaleDefault = getJavaProperty(env, "icu.locale.default"); + if (!icuLocaleDefault.empty()) { + UErrorCode status = U_ZERO_ERROR; + icu::Locale locale = icu::Locale::forLanguageTag(icuLocaleDefault.c_str(), status); + if (U_SUCCESS(status)) { + icu::Locale::setDefault(locale, status); + } + if (U_FAILURE(status)) { + fprintf(stderr, "Failed to set the ICU default locale to '%s' (error code %d)\n", + icuLocaleDefault.c_str(), status); } } } static int register_android_core_classes(JNIEnv* env) { - jclass system = FindClassOrDie(env, "java/lang/System"); - jmethodID getPropertyMethod = - GetStaticMethodIDOrDie(env, system, "getProperty", - "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); - - // Get the names of classes that need to register their native methods - auto nativesClassesJString = - (jstring)env->CallStaticObjectMethod(system, getPropertyMethod, - env->NewStringUTF("core_native_classes"), - env->NewStringUTF("")); - const char* nativesClassesArray = env->GetStringUTFChars(nativesClassesJString, nullptr); - string nativesClassesString(nativesClassesArray); + string nativesClassesString = getJavaProperty(env, "core_native_classes"); vector classesToRegister = parseCsv(nativesClassesString); - env->ReleaseStringUTFChars(nativesClassesJString, nativesClassesArray); if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) { return JNI_ERR; @@ -359,6 +393,11 @@ void AndroidRuntime::onStarted() { void AndroidRuntime::start(const char* className, const Vector& options, bool zygote) { JNIEnv* env = AndroidRuntime::getJNIEnv(); + + auto method_binding_format = getJavaProperty(env, "method_binding_format"); + + setJniMethodFormat(method_binding_format); + // Register native functions. if (startReg(env) < 0) { ALOGE("Unable to register all android native methods\n"); diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto index e795e809664134b79e7d96dd9972efa59450e7c2..9779dc0e00b811a2a0861378664f70d645040487 100644 --- a/core/proto/android/providers/settings/system.proto +++ b/core/proto/android/providers/settings/system.proto @@ -220,6 +220,15 @@ message SystemSettingsProto { } optional Touchpad touchpad = 36; + message Mouse { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + optional SettingProto reverse_vertical_scrolling = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto swap_primary_button = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + } + + optional Mouse mouse = 38; + optional SettingProto tty_mode = 31 [ (android.privacy).dest = DEST_AUTOMATIC ]; message Vibrate { @@ -277,5 +286,5 @@ message SystemSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 38; + // Next tag = 39; } diff --git a/core/proto/android/widget/remoteviews.proto b/core/proto/android/widget/remoteviews.proto index f477d32cd91501b345128c4d1e7435b29ca92e6f..6a987a475711e0feb3b55861a4f9ea4160627c8e 100644 --- a/core/proto/android/widget/remoteviews.proto +++ b/core/proto/android/widget/remoteviews.proto @@ -32,6 +32,8 @@ import "frameworks/base/core/proto/android/content/res/color_state_list.proto"; * * Do not change the tag number or type of any fields in order to maintain compatibility with * previous versions. If a field is deleted, use `reserved` to mark its tag number. + * + * Next tag: 17 */ message RemoteViewsProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; @@ -290,6 +292,7 @@ message RemoteViewsProto { } } + // Next tag: 23 message Action { oneof action { AttributeReflectionAction attribute_reflection_action = 1; @@ -307,6 +310,13 @@ message RemoteViewsProto { SetRadioGroupCheckedAction set_radio_group_checked_action = 13; SetRemoteCollectionItemListAdapterAction set_remote_collection_item_list_adapter_action = 14; SetRippleDrawableColorAction set_ripple_drawable_color_action = 15; + SetViewOutlinePreferredRadiusAction set_view_outline_preferred_radius_action = 16; + TextViewDrawableAction text_view_drawable_action = 17; + TextViewSizeAction text_view_size_action = 18; + ViewGroupAddAction view_group_add_action = 19; + ViewGroupRemoveAction view_group_remove_action = 20; + ViewPaddingAction view_padding_action = 21; + SetDrawInstructionAction set_draw_instruction_action = 22; } } @@ -428,6 +438,65 @@ message RemoteViewsProto { optional string view_id = 1; optional android.content.res.ColorStateListProto color_state_list = 2; } + + message SetViewOutlinePreferredRadiusAction { + optional string view_id = 1; + optional int32 value_type = 2; + optional int32 value = 3; + } + + message TextViewDrawableAction { + optional string view_id = 1; + optional bool is_relative = 2; + oneof drawables { + Resources resources = 3; + Icons icons = 4; + }; + + message Resources { + optional string one = 1; + optional string two = 2; + optional string three = 3; + optional string four = 4; + } + + message Icons { + optional Icon one = 1; + optional Icon two = 2; + optional Icon three = 3; + optional Icon four = 4; + } + } + + message TextViewSizeAction { + optional string view_id = 1; + optional int32 units = 2; + optional float size = 3; + } + + message ViewGroupAddAction { + optional string view_id = 1; + optional RemoteViewsProto nested_views = 2; + optional int32 index = 3; + optional int32 stableId = 4; + } + + message ViewGroupRemoveAction { + optional string view_id = 1; + optional string view_id_to_keep = 2; + } + + message ViewPaddingAction { + optional string view_id = 1; + optional int32 left = 2; + optional int32 right = 3; + optional int32 top = 4; + optional int32 bottom = 5; + } + + message SetDrawInstructionAction { + repeated bytes instructions = 1; + } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index f067b5184bdb3984e0c7385364366ee66d2bec0e..5693d666adc2cd381ffe6d64504afa843740b865 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -155,6 +155,7 @@ + @@ -239,6 +240,8 @@ android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" /> + + @@ -845,6 +849,8 @@ + + diff --git a/core/res/OWNERS b/core/res/OWNERS index 5293131180ae453c05533937b0a4bde0e83e7349..d109cee5d910de6e47279c0c2d1e2027c2f6f34e 100644 --- a/core/res/OWNERS +++ b/core/res/OWNERS @@ -60,6 +60,7 @@ per-file res/values/config_display.xml = file:/services/core/java/com/android/se # Wear per-file res/*-watch/* = file:/WEAR_OWNERS +per-file res/*-watch-v*/* = file:/WEAR_OWNERS # Performance per-file res/values/config.xml = file:/PERFORMANCE_OWNERS diff --git a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml new file mode 100644 index 0000000000000000000000000000000000000000..8b2afa86986c58ac710ba1a58e05481fc706b2dd --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml new file mode 100644 index 0000000000000000000000000000000000000000..cefc9121b7a448abfaffd67289f008dd4b1dc7b3 --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml new file mode 100644 index 0000000000000000000000000000000000000000..eaf9e7d50bbd083cf932ed28cfc7ad40b8fad824 --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml new file mode 100644 index 0000000000000000000000000000000000000000..94e50fbe253308edd7641b3363f9ff8c63a40b2f --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml new file mode 100644 index 0000000000000000000000000000000000000000..0029de14e34a97f2d8043a91dd043cc4369400cd --- /dev/null +++ b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml new file mode 100644 index 0000000000000000000000000000000000000000..105f077cd8418c6ddd7f7439918b090dfeddcfd2 --- /dev/null +++ b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/notification_progress_icon_background.xml b/core/res/res/drawable/notification_progress_icon_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..8e843bea3d6a3f6b15a31c53eccdc90fad45ed76 --- /dev/null +++ b/core/res/res/drawable/notification_progress_icon_background.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/core/res/res/layout/input_method_switch_dialog_new.xml b/core/res/res/layout/input_method_switch_dialog_new.xml index 058fe3fe30761fcf6ae08426920433235bab23da..118f93b89237fb0064a7894d5097c920d0fb8df8 100644 --- a/core/res/res/layout/input_method_switch_dialog_new.xml +++ b/core/res/res/layout/input_method_switch_dialog_new.xml @@ -39,7 +39,7 @@ android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingVertical="8dp" + android:paddingTop="8dp" android:clipToPadding="false" android:layoutManager="com.android.internal.widget.LinearLayoutManager"/> @@ -74,8 +74,7 @@ android:text="@string/input_method_switcher_settings_button" android:fontFamily="google-sans-text" android:textAppearance="?attr/textAppearance" - android:contentDescription="@string/input_method_language_settings" - android:visibility="gone"/> + android:contentDescription="@string/input_method_language_settings"/> diff --git a/core/res/res/layout/input_method_switch_item_divider.xml b/core/res/res/layout/input_method_switch_item_divider.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f8c963ff8cf1bc3913761ea2042444fd2de06d0 --- /dev/null +++ b/core/res/res/layout/input_method_switch_item_divider.xml @@ -0,0 +1,34 @@ + + + + + + + + diff --git a/core/res/res/layout/input_method_switch_item_header.xml b/core/res/res/layout/input_method_switch_item_header.xml new file mode 100644 index 0000000000000000000000000000000000000000..f0080a6300253e80e6b2666bc37b46ac1847ae08 --- /dev/null +++ b/core/res/res/layout/input_method_switch_item_header.xml @@ -0,0 +1,38 @@ + + + + + + + + diff --git a/core/res/res/layout/input_method_switch_item_new.xml b/core/res/res/layout/input_method_switch_item_new.xml index 10d938c71ea4d1d1dccc85d61bd55f4d03539f27..f8710cc45358cadb7b70ce207f3cc6f8705a4344 100644 --- a/core/res/res/layout/input_method_switch_item_new.xml +++ b/core/res/res/layout/input_method_switch_item_new.xml @@ -16,76 +16,45 @@ --> - - - - + android:layout_height="72dp" + android:background="@drawable/input_method_switch_item_background" + android:gravity="center_vertical" + android:orientation="horizontal" + android:layout_marginHorizontal="16dp" + android:layout_marginBottom="8dp" + android:paddingStart="20dp" + android:paddingEnd="24dp"> - - - - - - - - + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:gravity="start|center_vertical" + android:orientation="vertical"> + + + + diff --git a/core/res/res/layout/notification_template_material_progress.xml b/core/res/res/layout/notification_template_material_progress.xml new file mode 100644 index 0000000000000000000000000000000000000000..fdcefccdbf1658ca07c53bf6169970815f18f0b2 --- /dev/null +++ b/core/res/res/layout/notification_template_material_progress.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 1cd21504193cce1b962db86de4f950f8c8807b86..cdae26541737e6bf28abdb9344b5aedd8d90b92c 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -2129,11 +2129,6 @@ "Oproepe en kennisgewings sal vibreer" "Oproepe en kennisgewings sal gedemp wees" "Stelselveranderinge" - "Moenie Steur Nie" - "Nuut: Moenie Steur Nie versteek tans kennisgewings" - "Tik om meer te wete te kom en te verander." - "Moenie Steur Nie het verander" - "Tik om te kyk wat geblokkeer word." "Gaan kennisgewinginstellings na" "Vanaf Android 13 het programme wat jy installeer jou toestemming nodig om kennisgewings te stuur. Tik om hierdie toestemming vir bestaande programme te verander." "Herinner my later" diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 484afc3c344e9c28e1637712e83d31bef6537196..cc8ab3e03f6ee0d65d7b514cd7e082f81769853e 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -2129,11 +2129,6 @@ "ጥሪዎች እና ማሳወቂያዎች ይነዝራሉ" "ጥሪዎች እና ማሳወቂያዎች ድምፀ-ከል ይሆናሉ" "የሥርዓት ለውጦች" - "አትረብሽ" - "አዲስ፦ አትረብሽ ማሳወቂያዎችን እየደበቀ ነው" - "የበለጠ ለመረዳት እና ለመለወጥ መታ ያድርጉ።" - "አትረብሽ ተቀይሯል" - "ምን እንደታገደ ለመፈተሽ መታ ያድርጉ።" "የማሳወቂያ ቅንብሮችን ይገምግሙ" "ከAndroid 13 ጀምረው የሚጭኗቸው መተግበሪያዎች ማሳወቂያዎችን ለመላክ የእርስዎ ፈቃድ ያስፈልጋቸዋል። ይህን ፈቃድ ለነባር መተግበሪያዎች ለመቀየር መታ ያድርጉ።" "በኋላ አስታውሰኝ" diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 75e93d1aa315a7bc6da2adfa8ea77d58e3b93662..e81ec89ebe5184da3bf1a8ee1f575061be7e66ca 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -2133,11 +2133,6 @@ "سيهتز الهاتف عند تلقّي المكالمات والإشعارات." "سيتم كتم صوت الهاتف عند تلقي المكالمات والإشعارات." "تغييرات النظام" - "عدم الإزعاج" - "جديد: يؤدي تفعيل ميزة \"عدم الإزعاج\" إلى إخفاء الإشعارات." - "انقر لمعرفة مزيد من المعلومات وإجراء التغيير." - "تم تغيير ميزة \"عدم الإزعاج\"" - "انقر للاطّلاع على ما تم حظره." "مراجعة إعدادات الإشعارات" "‏بدءً من نظام التشغيل Android 13، يجب أن تحصل التطبيقات التي تُثبِّتها على إذن لإرسال الإشعارات. انقر لتغيير هذا الإذن للتطبيقات الحالية." "تذكيري لاحقًا" diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index e64c85e058d5cc9b8ec8862ffb3ab5e1d6e5f6f4..f36a659dd84d2069e6e6efeda57d81c2811b1a19 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -2129,11 +2129,6 @@ "কল আৰু জাননীসমূহে কম্পন কৰিব" "কল আৰু জাননীসমূহ মিউট কৰা হ\'ব" "ছিষ্টেমৰ সালসলনি" - "অসুবিধা নিদিব" - "নতুন: অসুবিধা নিদিব ম\'ডে জাননীসমূহ লুকাই ৰাখিছে" - "অধিক জানিবলৈ আৰু সলনি কৰিবলৈ টিপক।" - "অসুবিধা নিদিব সলনি হৈছে" - "কি কি অৱৰোধ কৰা হৈছে জানিবলৈ টিপক।" "জাননীৰ ছেটিং পৰ্যালোচনা কৰক" "Android 13ৰ পৰা, আপুনি ইনষ্টল কৰা এপক জাননী পঠিয়াবলৈ আপোনাৰ অনুমতিৰ প্ৰয়োজন। আগৰে পৰা থকা এপৰ বাবে এই অনুমতিটো সলনি কৰিবলৈ টিপক।" "পাছত মনত পেলাই দিব" diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 25dc42904995f436c6666c8e4b982d5559cdb136..2a77df23934c61298bf9eeae85a4cc1503d6e180 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -2129,11 +2129,6 @@ "Zəng və bildirişlər vibrasiya verəcək" "Zəng və bildirişlər səssiz ediləcək" "Sistem dəyişiklikləri" - "Narahat Etməyin" - "Yenilik: \"Narahat etməyin\" rejimi bildirişləri gizlədir" - "Ətraflı məıumat əldə edərək dəyişmək üçün klikləyin." - "\"Narahat Etməyin\" rejimi dəyişdirildi" - "Nəyin blok edildiyini yoxlamaq üçün klikləyin." "Bildiriş ayarlarını nəzərdən keçirin" "Android 13-dən başlayaraq quraşdırdığınız tətbiqlər bildiriş göndərmək üçün icazənizi tələb edir. Mövcud tətbiqlər üçün bu icazəni dəyişmək üçün toxunun." "Sonra xatırladın" diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 7413703e2adab3ebd2322992528a713e8409fd33..a51aad24b06fd7eec718b79ba484376cdd02f732 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -2130,11 +2130,6 @@ "Vibracija za pozive i obaveštenja je uključena" "Melodija zvona za pozive i obaveštenje je isključena" "Sistemske promene" - "Ne uznemiravaj" - "Novo: Režim Ne uznemiravaj krije obaveštenja" - "Dodirnite da biste saznali više i promenili podešavanje." - "Režim Ne uznemiravaj je promenjen" - "Dodirnite da biste proverili šta je blokirano." "Pregledajte podešavanja obaveštenja" "Od Android-a 13 aplikacije koje instalirate moraju da imaju dozvolu za slanje obaveštenja. Dodirnite da biste promenili ovu dozvolu za postojeće aplikacije." "Podseti me kasnije" diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index a5af4b9fbdfe0972f85106090ef1d88664fae806..26f11df6259c71903b39db0c547db85c42bcfd31 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -2131,11 +2131,6 @@ "Для выклікаў і апавяшчэнняў уключаны вібрасігнал" "Для выклікаў і апавяшчэнняў гук выключаны" "Сістэмныя змены" - "Не турбаваць" - "Новае: у рэжыме \"Не турбаваць\" апавяшчэнні не паказваюцца" - "Дакраніцеся, каб даведацца больш і змяніць." - "Зменены налады рэжыму \"Не турбаваць\"" - "Націсніце, каб паглядзець заблакіраванае." "Праверце налады апавяшчэнняў" "Пачынаючы з версіі Android 13 усталяваным вамі праграмам неабходна даваць дазвол на адпраўку апавяшчэнняў. Націсніце, каб змяніць дазвол для існуючых праграм." "Нагадаць пазней" diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 9b5a825b9e3838ae8bcb415634143dd449199729..38c9d0b7cd94019ca45db2c9425d6a34a804f878 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -2129,11 +2129,6 @@ "При обаждания и известия устройството ще вибрира" "Обажданията и известията ще бъдат заглушени" "Промени в системата" - "Не безпокойте" - "Ново: Режимът „Не безпокойте“ скрива известията" - "Докоснете, за да научите повече и да извършите промени." - "Настройките за „Не безпокойте“ са променени" - "Докоснете, за да проверите какво е блокирано." "Преглед на настройките за известия" "От Android 13 инсталираните от вас приложения трябва да получат разрешението ви, за да изпращат известия. Докоснете, за да промените това разрешение за съществуващите приложения." "Напомняне по-късно" diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 0871bfd244fe0ce9c9cd8a965eee10f930ebed37..8a833c88e44abdfe5749c448d2376325968c6017 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -2129,11 +2129,6 @@ "কল এবং বিজ্ঞপ্তি আসলে ভাইব্রেট হবে" "কল এবং বিজ্ঞপ্তিগুলি মিউট করা হবে" "সিস্টেমে হয়ে থাকা পরিবর্তন" - "বিরক্ত করবে না" - "নতুন: \'বিরক্ত করবে না\' মোড চালু আছে, তাই বিজ্ঞপ্তি লুকিয়ে ফেলা হচ্ছে" - "আরও জানতে এবং পরিবর্তন করতে ট্যাপ করুন।" - "\'বিরক্ত করবে না\' মোডের সেটিং বদলে গেছে" - "কী কী ব্লক করা আছে তা দেখতে ট্যাপ করুন।" "বিজ্ঞপ্তির সেটিংস পর্যালোচনা করুন" "Android 13 থেকে শুরু করে, বিজ্ঞপ্তি পাঠানোর জন্য আপনার ইনস্টল করা অ্যাপকে অনুমতি নিতে হবে। বর্তমান অ্যাপের জন্য এই অনুমতি পরিবর্তন করতে ট্যাপ করুন।" "পরে মনে করিয়ে দিও" diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 9d5ecdcc1933cffdd4c46f27e2801dfb5edb14b2..9e29b7f7795fccee8c96769e4089403b23c320a9 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -2130,11 +2130,6 @@ "Pozivi i obavještenja će vibrirati" "Pozivi i obavještenja će se isključiti" "Sistemske promjene" - "Ne ometaj" - "Novo: Način rada Ne ometaj sakriva obavještenja" - "Dodirnite da saznate više i izvršite promjene." - "Način rada Ne ometaj je promijenjen" - "Dodirnite da provjerite šta je blokirano." "Pregledajte postavke obavještenja" "Počevši od Androida 13, aplikacije koje instalirate trebaju odobrenje da šalju obavještenja. Dodirnite da promijenite ovo odobrenje za postojeće aplikacije." "Podsjeti me kasnije" diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 5728aed8551a823c0a6409a27caf8ad2592caeab..0dd1bca868ade3b0cb75172a3713ed87dc040847 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -2130,11 +2130,6 @@ "Les trucades i les notificacions vibraran" "Les trucades i les notificacions se silenciaran" "Canvis del sistema" - "No molestis" - "Novetat: el mode No molestis està amagant notificacions" - "Toca per obtenir més informació i canviar la configuració." - "S\'ha canviat el mode No molestis" - "Toca per consultar què s\'ha bloquejat." "Consulta la configuració de notificacions" "A partir de la versió Android 13, les aplicacions que instal·les necessiten el teu permís per enviar notificacions. Toca per canviar aquest permís per a les aplicacions existents." "Recorda-m\'ho més tard" @@ -2394,12 +2389,12 @@ "Utilitza un altre cable i torna-ho a provar" "El dispositiu està massa calent i no pot duplicar a la pantalla fins que es refredi" "Pantalla dual" - "La pantalla dual està activada" + "Pantalla dual està activada" "%1$s està utilitzant les dues pantalles per mostrar contingut" "El dispositiu està massa calent" - "La pantalla dual no està disponible perquè el telèfon està massa calent" - "Dual Screen no està disponible" - "Dual Screen no està disponible perquè la funció Estalvi de bateria està activada. Pots desactivar aquesta opció a Configuració." + "Pantalla dual no està disponible perquè el telèfon està massa calent" + "Pantalla dual no està disponible" + "Pantalla dual no està disponible perquè la funció Estalvi de bateria està activada. Pots desactivar aquesta opció a Configuració." "Ves a Configuració" "Desactiva" "%s configurat" diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 2fea7fba45dc0991b533b9e055fa5db8cf492f1f..35f269ab48e077ff26849675a1e3525494ebe03a 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -2131,11 +2131,6 @@ "Volání a oznámení budou vibrovat" "Volání a oznámení budou ztlumena" "Změny nastavení systému" - "Nerušit" - "Novinka: Režim Nerušit skrývá oznámení" - "Klepnutím zobrazíte další informace a provedete změny." - "Nastavení režimu Nerušit se změnilo" - "Klepnutím zkontrolujete, co je blokováno." "Zkontrolujte nastavení oznámení" "Počínaje systémem Android 13 od vás nainstalované aplikace potřebují oprávnění k odesílání oznámení. Klepnutím toto oprávnění změníte pro stávající aplikace." "Připomenout později" diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index a9626a650cf9a995003986b4aaed2cff1b06d4ec..833b0c5f29b109d7548926dc0e9525466c1ea602 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -2129,11 +2129,6 @@ "Telefonen vibrerer ved opkald og notifikationer" "Der afspilles ikke lyd ved opkald og notifikationer" "Systemændringer" - "Forstyr ikke" - "Nyhed! Forstyr ikke skjuler notifikationer" - "Tryk for at få flere oplysninger og foretage ændringer." - "Tilstanden Forstyr ikke blev ændret" - "Tryk for at se, hvad der er blokeret." "Gennemgå indstillinger for notifikationer" "Fra og med Android 13 skal de apps, som du installerer, have din tilladelse til at sende notifikationer. Tryk for at ændre denne indstilling for eksisterende apps." "Påmind mig senere" diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index da0e9bd2c83583820a7a91b0fab34148291b89b7..1318f64178f860d9fae0ffb2620105714f2ebbd3 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -2129,11 +2129,6 @@ "Gerät vibriert bei Anrufen und Benachrichtigungen" "Anrufe und Benachrichtigungen stummgeschaltet" "Systemänderungen" - "Bitte nicht stören" - "Neu: Durch „Bitte nicht stören“ werden Benachrichtigungen nicht mehr angezeigt" - "Für weitere Informationen und zum Ändern tippen." - "„Bitte nicht stören“ wurde geändert" - "Tippe, um zu überprüfen, welche Inhalte blockiert werden." "Benachrichtigungseinstellungen überprüfen" "Ab Android 13 benötigen Apps, die du installierst, die Berechtigung zum Senden von Benachrichtigungen. Wenn du diese Berechtigung für bereits installierte Apps ändern möchtest, tippe hier." "Später erinnern" diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 0556a0820dea62988138f44b5e2af5711aef3e70..bccea8f8bc0b25a10aa01d030639fd9e16bd97b4 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -507,7 +507,7 @@ "Επιτρέπει στην εφαρμογή την τροποποίηση καθολικών ρυθμίσεων ήχου, όπως η ένταση και ποιο ηχείο χρησιμοποιείται για έξοδο." "εγγράφει ήχο" "Αυτή η εφαρμογή μπορεί να εγγράφει ήχο μέσω του μικροφώνου, όταν τη χρησιμοποιείτε." - "εγγραφή ήχου στο παρασκήνιο" + "ηχογράφηση στο παρασκήνιο" "Αυτή η εφαρμογή μπορεί να εγγράφει ήχο μέσω του μικροφώνου, ανά πάσα στιγμή." "ανίχνευση καταγραφών οθόνης που περιέχουν τα παράθυρα της εφαρμογής" "Η εφαρμογή θα λάβει ειδοποίηση όταν ληφθεί ένα στιγμιότυπο οθόνης ενώ βρίσκεται σε χρήση." @@ -541,7 +541,7 @@ "προβολή και έλεγχος κλήσεων μέσω του συστήματος." "Επιτρέπει στην εφαρμογή να βλέπει και να ελέγχει τις εισερχόμενες κλήσεις στη συσκευή. Αυτό περιλαμβάνει πληροφορίες όπως τους αριθμούς κλήσεων για τις κλήσεις και την κατάσταση των κλήσεων." "εξαίρεση από περιορισμούς εγγραφής ήχου" - "Εξαιρέστε την εφαρμογή από περιορισμούς για την εγγραφή ήχου." + "Εξαιρέστε την εφαρμογή από περιορισμούς για την ηχογράφηση." "συνέχιση κλήσης από άλλη συσκευή" "Επιτρέπει στην εφαρμογή να συνεχίσει μια κλήση η οποία ξεκίνησε σε άλλη εφαρμογή." "ανάγνωση αριθμών τηλεφώνου" @@ -2129,11 +2129,6 @@ "Θα υπάρχει δόνηση για κλήσεις και ειδοποιήσεις" "Οι κλήσεις και οι ειδοποιήσεις θα τεθούν σε παύση" "Αλλαγές στο σύστημα" - "Μην ενοχλείτε" - "Νέο: Η λειτουργία \"Μην ενοχλείτε\" αποκρύπτει ειδοποιήσεις" - "Πατήστε για να μάθετε περισσότερα και να κάνετε αλλαγές." - "Η λειτουργία \"Μην ενοχλείτε\" άλλαξε" - "Πατήστε για να ελέγξετε το περιεχόμενο που έχει αποκλειστεί." "Έλεγχος ρυθμίσεων ειδοποιήσεων" "Από το Android 13 και έπειτα, οι εφαρμογές που εγκαθιστάτε θα χρειάζονται την άδειά σας για την αποστολή ειδοποιήσεων. Πατήστε για να αλλάξετε αυτή την άδεια για υπάρχουσες εφαρμογές." "Υπενθύμιση αργότερα" diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 086835c1cd5abc0359bc19ca306dc57795e80597..e9239e2330921fa5e5750de738451175436b2ce0 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -2129,11 +2129,6 @@ "Calls and notifications will vibrate" "Calls and notifications will be muted" "System changes" - "Do not disturb" - "New: Do Not Disturb is hiding notifications" - "Tap to find out more and change." - "Do Not Disturb has changed" - "Tap to check what\'s blocked." "Review notification settings" "Starting in Android 13, apps that you install need your permission to send notifications. Tap to change this permission for existing apps." "Remind me later" diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 88a83b595520552a1f06b0a717c79b51adfb395e..c624e2a724a529d066a651b8783ebeb02db4550a 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -2129,11 +2129,6 @@ "Calls and notifications will vibrate" "Calls and notifications will be muted" "System changes" - "Do Not Disturb" - "New: Do Not Disturb is hiding notifications" - "Tap to learn more and change." - "Do Not Disturb has changed" - "Tap to check what\'s blocked." "Review notification settings" "Starting in Android 13, apps that you install need your permission to send notifications. Tap to change this permission for existing apps." "Remind me later" diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index ef399b715631a475865cc6173d3537cdc30d3ef3..9ffaa5d31e959de1ea1c13b7904a4ed0b4b12610 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -2129,11 +2129,6 @@ "Calls and notifications will vibrate" "Calls and notifications will be muted" "System changes" - "Do not disturb" - "New: Do Not Disturb is hiding notifications" - "Tap to find out more and change." - "Do Not Disturb has changed" - "Tap to check what\'s blocked." "Review notification settings" "Starting in Android 13, apps that you install need your permission to send notifications. Tap to change this permission for existing apps." "Remind me later" diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 94ddc434926f188ae8c08ac3f81af71b0cdd095c..23441bbb0e4d49d0b8166dfe76174c6892557382 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -2129,11 +2129,6 @@ "Calls and notifications will vibrate" "Calls and notifications will be muted" "System changes" - "Do not disturb" - "New: Do Not Disturb is hiding notifications" - "Tap to find out more and change." - "Do Not Disturb has changed" - "Tap to check what\'s blocked." "Review notification settings" "Starting in Android 13, apps that you install need your permission to send notifications. Tap to change this permission for existing apps." "Remind me later" diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index a0a891eefac2f5e21511a1e35691f5a8f2cd531b..b1ba09c406c9229067452491503df29ef8544f5b 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -2129,11 +2129,6 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‎‎‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‏‏‎‎‏‏‏‎Calls and notifications will vibrate‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‏‏‎Calls and notifications will be muted‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‎‎System changes‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‏‎Do Not Disturb‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‎New: Do Not Disturb is hiding notifications‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎Tap to learn more and change.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‎‏‎‏‎‏‏‎‎‎‏‎‎‏‏‏‎‏‎Do Not Disturb has changed‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎Tap to check what\'s blocked.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‎‎‎‏‎‎‎Review notification settings‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎Starting in Android 13, apps that you install need your permission to send notifications. Tap to change this permission for existing apps.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‎‎‏‏‎‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‏‎‏‏‎Remind me later‎‏‎‎‏‎" diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index cbb30fd707689fb0a0198483788c5448bfb42e3c..89b22b975ef2c31401e59cbfaaa8514e1b7cc7a1 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -2130,11 +2130,6 @@ "Vibrarán las llamadas y notificaciones" "Se silenciarán las llamadas y notificaciones" "Cambios del sistema" - "No interrumpir" - "Nuevo: No interrumpir oculta las notificaciones" - "Presiona para obtener más información y realizar cambios." - "Se modificó la opción No interrumpir" - "Presiona para consultar lo que está bloqueado." "Revisa la configuración de notificaciones" "A partir de Android 13, las apps que instales necesitarán tu permiso a fin de enviar notificaciones. Presiona para cambiar este permiso para las apps existentes." "Recordarme más tarde" diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 23024f097dec3fe15ed0607f225796791b39bc15..0082adecddce11903fc745c18d6ca3cdceadbac4 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1407,7 +1407,7 @@ "Se ha detectado un accesorio de audio analógico" "El dispositivo adjunto no es compatible con este teléfono. Toca para obtener más información." "Depuración por USB activa" - "Toca para desactivar la depuración por USB" + "Toca para desactivar depuración USB" "Seleccionar para inhabilitar la depuración por USB" "Depuración inalámbrica conectada" "Toca para desactivar la depuración inalámbrica" @@ -2130,11 +2130,6 @@ "Las llamadas y las notificaciones vibrarán" "Las llamadas y las notificaciones se silenciarán" "Cambios del sistema" - "No molestar" - "Novedad: El modo No molestar oculta las notificaciones" - "Toca para obtener más información y hacer cambios." - "Ha cambiado el modo No molestar" - "Toca para consultar lo que se está bloqueando." "Consulta los ajustes de notificaciones" "A partir de Android 13, las aplicaciones que instalas necesitan tu permiso para enviar notificaciones. Toca para cambiar este permiso en las aplicaciones que ya tengas." "Recordar más tarde" diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 326b3db247c8811a917f8015afd0f20cd678f578..44f1055444541f5cc5102e1ec75a46460654cb5f 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -2129,11 +2129,6 @@ "Kõnede ja märguannete puhul seade vibreerib" "Kõned ja märguanded on vaigistatud" "Süsteemi muudatused" - "Mitte segada" - "Uus: režiim Mitte segada peidab märguandeid" - "Puudutage lisateabe vaatamiseks ja muutmiseks." - "Režiimi Mitte segada muudeti" - "Puudutage, et kontrollida, mis on blokeeritud." "Vaadake üle märguandeseaded" "Alates operatsioonisüsteemist Android 13 vajavad installitavad rakendused märguannete saatmiseks teie luba. Puudutage, et muuta seda luba olemasolevate rakenduste jaoks." "Tuleta hiljem meelde" diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index d092f46ab6c249ec3bf1737ed997a8a2c1b27c92..d017a05a57c7eec408887493d32ba87c4ab53fc6 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -745,7 +745,7 @@ "Saiakera gehiegi egin dira. Horren ordez, erabili pantailaren blokeoa." "Ezin da egiaztatu aurpegia. Saiatu berriro." "Ez duzu konfiguratu aurpegi bidez desblokeatzeko eginbidea" - "Aurpegi bidez desblokeatzeko eginbidea ez da bateragarria gailu honekin" + "Aurpegi bidez desblokeatzeko eginbidea ez da onartzen gailu honetan" "Sentsorea aldi baterako desgaitu da." "%d aurpegia" "Erabili aurpegi bidez desblokeatzeko eginbidea" @@ -2129,11 +2129,6 @@ "Dar-dar egingo du deiak eta jakinarazpenak jasotzean" "Ez da joko tonurik deiak eta jakinarazpenak jasotzean" "Sistema-aldaketak" - "Ez molestatzeko modua" - "Berria: Ez molestatzeko modua jakinarazpenak ezkutatzen ari da" - "Sakatu informazio gehiago lortzeko eta portaera aldatzeko." - "Ez molestatzeko modua aldatu da" - "Sakatu zer dagoen blokeatuta ikusteko." "Berrikusi jakinarazpen-ezarpenak" "Android 13 ezkero, jakinarazpenak bidaltzeko baimena eman behar diezu instalatzen dituzun aplikazioei. Sakatu hau lehendik dauden aplikazioen baimenak aldatzeko." "Gogorarazi geroago" diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 6933a16dd97ed5e8ee1344dae8f4934d81a56fc2..ad8f056a8609326e8138ea7815bc17cfe6e31d37 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -458,7 +458,7 @@ "اجرای سرویس پیش‌نما از نوع «استفاده ویژه»" "به برنامه اجازه می‌دهد از سرویس‌های پیش‌نما از نوع «استفاده ویژه» استفاده کند" "اندازه‌گیری اندازه فضای ذخیره‌سازی برنامه" - "‏به برنامه اجازه می‎دهد تا کدها، داده‎ها و اندازه‎های حافظهٔ پنهان خود را بازیابی کند" + "‏به برنامه اجازه می‎دهد تا کدها، داده‎ها و اندازه‎های حافظه نهان خود را بازیابی کند" "تغییر تنظیمات سیستم" "‏به برنامه اجازه می‎دهد تا داده‎های تنظیم سیستم را تغییر دهد. برنامه‌های مخرب می‎توانند پیکربندی سیستم شما را خراب کنند." "اجرا شدن در هنگام راه‌اندازی" @@ -2129,11 +2129,6 @@ "دستگاهتان برای تماس‌ها و اعلان‌ها می‌لرزد" "دستگاهتان برای تماس‌ها و اعلان‌ها بی‌صدا خواهد شد" "تغییرات سیستم" - "مزاحم نشوید" - "جدید: «مزاحم نشوید» اعلان‌ها را پنهان می‌کند" - "برای اطلاعات بیشتر و تغییر دادن، تک‌ضرب بزنید." - "«مزاحم نشوید» تغییر کرده است" - "برای بررسی موارد مسدودشده تک‌ضرب بزنید." "مرور تنظیمات اعلان" "‏از Android نسخه ۱۳ به بعد، برنامه‌هایی که نصب می‌کنید برای ارسال اعلان به اجازه شما نیاز دارند. برای تغییر دادن این اجازه در برنامه‌های موجود، تک‌ضرب بزنید." "بعداً یادآوری شود" diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 18c7902511a094359e6206ad1d9fd34cae13ecea..196db127fb0e73db0c72b22c70386b81ef84af83 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -2129,11 +2129,6 @@ "Puhelut ja ilmoitukset värisevät" "Puhelut ja ilmoitukset mykistetään" "Järjestelmän muutokset" - "Älä häiritse" - "Uutta: Älä häiritse ‑tila piilottaa ilmoitukset" - "Napauta, jos haluat lukea lisää ja tehdä muutoksia." - "Älä häiritse ‑tila muuttui" - "Napauta niin näet, mitä on estetty." "Tarkista ilmoitusasetukset" "Asentamasi sovellukset tarvitsevat sinulta luvan ilmoitusten lähettämiseen Android 13 ‑käyttöjärjestelmästä alkaen. Napauta muuttaaksesi nykyisten sovellusten lupia." "Muistuta myöhemmin" diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index bc11e547bb821eabed0c6004388e32a372c1f428..ac87d3bac69dd5f34b9f59139ae685284501402c 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -2130,11 +2130,6 @@ "Les appels et les notifications vibreront" "Les appels et les notifications seront silencieux" "Changements système" - "Ne pas déranger" - "Nouveau : Le mode Ne pas déranger masque les notifications" - "Touchez ici pour en savoir plus et changer les paramètres" - "Les paramètres du mode Ne pas déranger ont changé" - "Touchez l\'écran pour vérifier ce qui est bloqué." "Examiner les paramètres de notification" "À partir d\'Android 13, les applis que vous installez ont besoin de votre autorisation pour envoyer des notifications. Touchez pour modifier cette autorisation pour les applis existantes." "Me rappeler plus tard" diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 03cf4634bad2520d7e2135c5a164d3e86573e803..2368c885ccf9547deb87169a1e00f05a68cda928 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -1407,7 +1407,7 @@ "Accessoire audio analogique détecté" "L\'appareil connecté n\'est pas compatible avec ce téléphone. Appuyez ici pour en savoir plus." "Débogage USB activé" - "Appuyez pour désactiver le débogage USB" + "Appuyez pour le désactiver" "Sélectionnez cette option pour désactiver le débogage USB." "Débogage sans fil connecté" "Appuyez pour désactiver le débogage sans fil" @@ -2130,11 +2130,6 @@ "Les appels et les notifications vibreront" "Les appels et les notifications seront silencieux" "Modifications du système" - "Ne pas déranger" - "Nouveau : Le mode Ne pas déranger masque les notifications" - "Appuyez pour en savoir plus et pour modifier les paramètres." - "Le mode Ne pas déranger a été modifié" - "Appuyez pour vérifier les contenus bloqués." "Vérifiez les paramètres de notification" "À partir d\'Android 13, les applications que vous installez ont besoin de votre autorisation pour envoyer des notifications. Appuyez pour modifier cette autorisation pour les applications déjà installées." "Plus tard" diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 430d64918470629f020c92b62c4d217dcdd3ea56..2aba79031b694c506bd62ba546bf1ffe8c8e8c66 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -2129,11 +2129,6 @@ "As chamadas e as notificacións vibrarán" "As chamadas e as notificacións estarán silenciadas" "Cambios no sistema" - "Non molestar" - "Novidade! O modo Non molestar oculta as notificacións" - "Toca para obter máis información e facer cambios." - "O modo Non molestar cambiou" - "Toca para comprobar o contido bloqueado." "Consulta a configuración de notificacións" "Desde Android 13, as aplicacións que instales necesitan o teu permiso para enviar notificacións. Toca para cambiar este permiso nas aplicacións que xa teñas." "Lembrarmo máis tarde" diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index f40fb93ca6a90192afeb3a1b745f421e2ed08be6..8d90e3b82c925f02107a5e63db2b56241b624e5e 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -2129,11 +2129,6 @@ "કૉલ અને નોટિફિકેશન માટે ઉપકરણ વાઇબ્રેટ થશે" "કૉલ અને નોટિફિકેશન મ્યૂટ કરવામાં આવશે" "સિસ્ટમના ફેરફારો" - "ખલેલ પાડશો નહીં" - "નવું: ખલેલ પાડશો નહીં હવે નોટિફિકેશન છુપાવી શકે છે" - "વધુ જાણવા અને બદલવા માટે ટૅપ કરો." - "ખલેલ પાડશો નહીંમાં ફેરફાર થયો છે" - "શું બ્લૉક કરેલ છે તે તપાસવા માટે ટૅપ કરો." "નોટિફિકેશનના સેટિંગ રિવ્યૂ કરો" "Android 13થી શરૂઆત કરીને, તમે જે પણ ઍપ ઇન્સ્ટૉલ કરશો, તેને નોટિફિકેશન મોકલવા માટે તમારી પરવાનગીની જરૂર રહેશે. હાલની બધી ઍપ માટે આ પરવાનગીમાં ફેરફાર કરવા માટે ટૅપ કરો." "મને પછી યાદ અપાવજો" diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 0386387fc134a5fbe5479216bdcd720c8c7899aa..12ffe8774fe85030f58caf54d9f5a7d34ee02e55 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -2129,11 +2129,6 @@ "कॉल और सूचनाओं आने पर डिवाइस वाइब्रेट हाेगा" "कॉल और सूचनाओं के लिए डिवाइस म्यूट रहेगा" "सिस्टम में हुए बदलाव" - "परेशान न करें" - "नई सुविधा: परेशान न करें सुविधा चालू होने की वजह से सूचनाएं नहीं दिखाई जा रही हैं" - "ज़्यादा जानने और बदलाव करने के लिए टैप करें." - "परेशान न करें की सुविधा बदल गई है" - "टैप करके देखें कि किन चीज़ों पर रोक लगाई गई है." "सूचना सेटिंग देखें" "Android 13 में जो ऐप्लिकेशन इंस्टॉल किए जाएंगे, उन्हें आपको सूचनाएं भेजने के लिए अनुमति लेनी होगी. पहले से इंस्टॉल किए गए ऐप्लिकेशन को दी गई अनुमति बदलने के लिए टैप करें." "बाद में याद दिलाएं" diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 54dc55de7991286398de2d85dd792b2459f77e93..895c3fa420ec67e14a54366b2e51c34c17df14c3 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -2130,11 +2130,6 @@ "Uređaj će vibrirati za pozive i obavijesti" "Zvučni signal poziva i obavijesti bit će isključen" "Promjene sustava" - "Ne uznemiravaj" - "Novo: način Ne uznemiravaj sakriva obavijesti" - "Dodirnite da biste saznali više i promijenili postavke." - "Promijenjena je postavka Ne uznemiravaj" - "Dodirnite da biste provjerili što je blokirano." "Pregledajte postavke obavijesti" "Od Androida 13 aplikacije koje instalirate trebaju vaše dopuštenje za slanje obavijesti. Dodirnite da biste promijenili to dopuštenje za postojeće aplikacije." "Podsjeti me kasnije" diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 179de2009dc53b1156235e893a73a73cd961c109..3bbaef2b18aca7e61227de5c8e134cd88d379f2c 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -2129,11 +2129,6 @@ "A hívások és az értesítések rezegnek" "A hívások és az értesítések némák" "Rendszermódosítások" - "Ne zavarjanak" - "Újdonság: A Ne zavarjanak mód elrejti az értesítéseket" - "Koppintással további információhoz juthat, és elvégezheti a módosítást." - "Módosultak a Ne zavarjanak mód beállításai" - "Koppintson a letiltott elemek megtekintéséhez." "Értesítési beállítások áttekintése" "Az Android 13-as rendszertől kezdődően a telepített alkalmazásoknak engedélyre van szükségük értesítések küldéséhez. Koppintással módosíthatja ezt az engedélyt a meglévő alkalmazások esetében." "Emlékeztessen később" diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 312a105285e174ca63353062881cd3bfe722f685..d341adafd871f667406cb011277145b2b98263e5 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -2129,11 +2129,6 @@ "Զանգերի և ծանուցումների համար թրթռոցը միացված է" "Զանգերի և ծանուցումների համար ձայնն անջատված է" "Համակարգի փոփոխություններ" - "Չանհանգստացնել" - "Այժմ «Չանհանգստացնել» ռեժիմում ծանուցումները թաքցվում են" - "Հպեք՝ ավելին իմանալու և կարգավորումները փոխելու համար:" - "«Չանհանգստացնել» ռեժիմի կարգավորումները փոխվել են" - "Հպեք՝ տեսնելու, թե ինչ է արգելափակվել:" "Ստուգեք ծանուցումների կարգավորումները" "Հավելվածներին, որոնք տեղադրում եք Android 13 և ավելի նոր տարբերակներով սարքերում, անհրաժեշտ է տրամադրել ծանուցումներ ուղարկելու թույլտվություն։ Հպեք և փոխեք այս թույլտվությունն արդեն տեղադրված հավելվածների համար։" "Հիշեցնել ավելի ուշ" diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index e830d6d10e0e3d8eab2feda69d1afb66261db42d..957e835bec6b3e4cbafc1d35c1571e5a6d19d427 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1904,7 +1904,7 @@ "Upaya ke-2 %1$s" "Upaya ke-3 %1$s" "Clone %1$s" - "%1$s Pribadi" + "%1$s Privasi" "Meminta PIN sebelum melepas sematan" "Meminta pola pembukaan kunci sebelum melepas sematan" "Meminta sandi sebelum melepas sematan" @@ -2129,11 +2129,6 @@ "Panggilan dan notifikasi akan bergetar" "Suara panggilan dan notifikasi akan dinonaktifkan" "Perubahan sistem" - "Jangan Ganggu" - "Baru: Mode Jangan Ganggu menyembunyikan notifikasi" - "Ketuk untuk mempelajari lebih lanjut dan mengubah." - "Jangan Ganggu telah berubah" - "Ketuk untuk memeriksa item yang diblokir." "Tinjau setelan notifikasi" "Mulai Android 13, aplikasi yang Anda instal memerlukan izin untuk mengirim notifikasi. Ketuk guna mengubah izin ini untuk aplikasi yang sudah ada." "Ingatkan saya nanti" diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 307fb529b903d879201a4df1cb7cf93629de4384..b6d1c12549dff8abdac590fa5850dcf1b8b290e4 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -2129,11 +2129,6 @@ "Titringur er virkur fyrir símtöl og tilkynningar" "Slökkt verður á hljóði símtala og tilkynninga" "Breytingar á kerfi" - "Ónáðið ekki" - "Nýtt: „Ónáðið ekki“ er að fela tilkynningar" - "Ýttu til að fá frekari upplýsingar og breyta." - "„Ónáðið ekki“ var breytt" - "Ýttu til að skoða hvað lokað hefur verið á." "Yfirfara tilkynningastillingar" "Frá og með Android 13 þurfa forrit sem þú setur upp heimild frá þér til að senda tilkynningar. Ýttu til að breyta þessari heimild fyrir forrit sem fyrir eru." "Minna mig á seinna" diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 08ea1f1adee459aedc77184917430c01c72331b6..66e5f72fcabb2ba86a498f9eb80fcbbdd30d8539 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -2130,11 +2130,6 @@ "La vibrazione sarà attiva per chiamate e notifiche" "L\'audio di chiamate e notifiche sarà disattivato" "Modifiche al sistema" - "Non disturbare" - "Novità: la modalità Non disturbare nasconde le notifiche" - "Tocca per avere ulteriori informazioni e modificare." - "L\'impostazione Non disturbare è cambiata" - "Tocca per controllare le notifiche bloccate." "Controlla le impostazioni di notifica" "A partire da Android 13, le app che installi devono avere la tua autorizzazione per poter inviare notifiche. Tocca per cambiare questa autorizzazione per le app esistenti." "Ricordamelo dopo" diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 1c1918cac58b732ff45b47309b193be38fe02576..8f4fbcd76939f6e1847e4b31660f1615781632fc 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -2130,11 +2130,6 @@ "שיחות והודעות ירטטו" "שיחות והתראות יושתקו" "שינויים במערכת" - "נא לא להפריע" - "חדש: מצב \'נא לא להפריע\' מסתיר התראות" - "אפשר להקיש כדי לקבל מידע נוסף ולבצע שינויים." - "ההגדרה \'נא לא להפריע\' השתנתה" - "יש להקיש כדי לבדוק מה חסום." "בדיקת הגדרת ההתראות" "‏החל מגרסת Android 13, אפליקציות שיותקנו יוכלו לשלוח התראות רק אם יקבלו ממך הרשאה. אפשר להקיש כדי לשנות את ההרשאה הזו באפליקציות קיימות." "תזכירו לי מאוחר יותר" diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 36d956c3bdd05b6d7f0f473fb16546f484711193..cd5527c80cba4559171d778d2a8a905179e2ccdb 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -2129,11 +2129,6 @@ "着信や通知をバイブレーションで知らせます" "着信音と通知音が鳴りません" "システムの変更" - "サイレント モード" - "新機能: サイレント モードでは通知が非表示になります" - "タップすると、詳細を確認して設定を変更できます。" - "サイレント モードが変わりました" - "タップしてブロック対象をご確認ください。" "通知設定の確認" "Android 13 以降では、インストールするアプリに、通知を送信する権限を付与する必要があります。既存のアプリのこの権限を変更するには、タップしてください。" "後で" diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 9a88e545b0c6a8b963ebf0bb11bc1eda917cac67..9b1c07fb2af661f97692825646857674e59843ee 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -2129,11 +2129,6 @@ "ზარების და შეტყობინებების მიღებისას ვიბრაცია ჩაირთვება" "ზარები და შეტყობინებები დადუმებული იქნება" "სისტემის ცვლილებები" - "არ შემაწუხოთ" - "ახალი: „არ შემაწუხოთ“ რეჟიმი მალავს შეტყობინებებს" - "შეეხეთ მეტის გასაგებად და შესაცვლელად." - "„არ შემაწუხოთ“ რეჟიმი შეცვლილია" - "შეეხეთ იმის სანახავად, თუ რა არის დაბლოკილი." "შეტყობინების პარამეტრების შემოწმება" "Android 13-ზე შეტყობინებების გასაგზავნად საჭიროა თქვენ მიერ დაინსტალირებული აპებისთვის ნებართვის მინიჭება. არსებული აპებისთვის ამ ნებართვის შესაცვლელად შეეხეთ." "შემახსენე მოგვიან." diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 595b9baaa25464e196307ae4e0e54f1f8e8a1a76..ebf627f763badc6b5c3364a2d2951e2bb0318801 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -2129,11 +2129,6 @@ "Қоңыраулар мен хабарландырулардың дірілі болады." "Қоңыраулар мен хабарландырулардың дыбыстық сигналы өшіріледі" "Жүйе өзгерістері" - "Мазаламау режимі" - "Жаңа: Мазаламау режимі хабарландыруларды жасыруда" - "Толығырақ ақпарат алу және өзгерту үшін түртіңіз." - "Мазаламау режимі өзгерді" - "Түймені түртіп, неге тыйым салынатынын көріңіз." "Хабарландыру параметрлерін қарау" "Android 13 нұсқасынан бастап орнатылатын қолданбалар үшін хабарландыру жіберу рұқсаты керек. Бұрынғы қолданбаларда осы рұқсатты өзгерту үшін түртіңіз." "Кейінірек еске салу" diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 696bd79509ac6dd47886b4d98142546dc067c487..df7006ace657b790a240b3a3e4a20d63695f9170 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -2129,11 +2129,6 @@ "ការហៅ​ទូរសព្ទ និងការជូន​ដំណឹងនឹងញ័រ" "ការហៅ​ទូរសព្ទ និងការជូន​ដំណឹងនឹង​បិទសំឡេង" "ការផ្លាស់ប្ដូរ​ប្រព័ន្ធ" - "កុំ​រំខាន" - "ថ្មី៖ មុខងារ​កុំរំខាន​កំពុងលាក់​ការជូនដំណឹង" - "ចុចដើម្បីស្វែងយល់បន្ថែម និងផ្លាស់ប្ដូរ។" - "មុខងារ​កុំ​រំខាន​ត្រូវ​បាន​ប្ដូរ" - "សូមចុច​ដើម្បី​មើល​ថា​​បានទប់ស្កាត់អ្វីខ្លះ។" "ពិនិត្យមើលការកំណត់ការជូនដំណឹង" "ចាប់ពី Android 13 ឡើងទៅ កម្មវិធីដែលអ្នកដំឡើងត្រូវការ​ការអនុញ្ញាតរបស់អ្នក ដើម្បីផ្ញើការជូនដំណឹង។ សូមចុចដើម្បីផ្លាស់ប្ដូរការអនុញ្ញាតនេះសម្រាប់កម្មវិធីដែលមានស្រាប់។" "រំលឹក​ខ្ញុំ​ពេលក្រោយ" diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 419dd17d233cce4c0694bdd71d7a5b4f693fc725..5ddb586b541e4459aeb13e4d627363707b410d6e 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -2129,11 +2129,6 @@ "ಕರೆಗಳು ಮತ್ತು ಅಧಿಸೂಚನೆಗಳು ವೈಬ್ರೇಟ್‌ ಆಗುತ್ತವೆ" "ಕರೆಗಳು ಮತ್ತು ಅಧಿಸೂಚನೆಗಳನ್ನು ಮ್ಯೂಟ್ ಮಾಡಲಾಗುತ್ತದೆ" "ಸಿಸ್ಟಂ ಬದಲಾವಣೆಗಳು" - "ಅಡಚಣೆ ಮಾಡಬೇಡಿ" - "ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಮೋಡ್ ಅಧಿಸೂಚನೆಗಳನ್ನು ಮರೆಮಾಡುತ್ತಿದೆ" - "ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಮತ್ತು ಬದಲಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ." - "ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಬದಲಾಗಿದೆ" - "ಏನನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ ಎಂಬುದನ್ನು ಪರೀಕ್ಷಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ." "ನೋಟಿಫಿಕೇಶನ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಪರಿಶೀಲಿಸಿ" "Android 13 ನಿಂದ ಪ್ರಾರಂಭಿಸಿ, ನೀವು ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡುವ ಆ್ಯಪ್‌ಗಳಿಗೆ, ಅಧಿಸೂಚನೆಗಳನ್ನು ಕಳುಹಿಸಲು ನಿಮ್ಮ ಅನುಮತಿಯ ಅಗತ್ಯವಿದೆ. ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಆ್ಯಪ್‌ಗಳಿಗಾಗಿ ಈ ಅನುಮತಿಯನ್ನು ಬದಲಾಯಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ." "ನಂತರ ರಿಮೈಂಡ್ ಮಾಡಿ" diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 5f0e1f9760e06345bfe389efce6247a79677e46f..92ca1d52956333d3800c9734ce6969ff72d93bcf 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -2129,11 +2129,6 @@ "전화 및 알림이 오면 진동이 사용됩니다." "전화 및 알림 소리가 음소거됩니다." "시스템 변경사항" - "방해 금지 모드" - "새로운 기능: 방해 금지 모드로 알림 숨기기" - "자세히 알아보고 변경하려면 탭하세요." - "방해 금지 모드 변경" - "차단된 항목을 확인하려면 탭하세요." "알림 설정 검토" "Android 13부터 설치된 앱에는 알림을 전송하기 위한 권한이 필요합니다. 기존 앱의 알림 전송 권한을 변경하려면 탭하세요." "나중에 알림" diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 14e1064a9aec1c705c4d8629ca898e56d4c2d72b..a4380253cd738fc72060abbb55e2624f2b77086e 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -2129,11 +2129,6 @@ "Чалуулар менен билдирмелер дирилдөө режиминде иштейт" "Чалуулар менен эскертмелердин үнү өчүрүлөт" "Система өзгөрүүлөрү" - "Тынчымды алба" - "Жаңы: \"Тынчымды алба\" режими билдирмелерди жашырууда" - "Көбүрөөк маалымат алып, өзгөртүү үчүн таптаңыз." - "\"Тынчымды алба\" режими өзгөрдү" - "Бөгөттөлгөн нерселерди көрүү үчүн таптаңыз." "Билдирмелердин параметрлерин карап чыгуу" "Android 13 версиясынан баштап билдирмелерди жөнөтүү үчүн орноткон колдонмолоруңузга уруксат берүү керек. Учурдагы колдонмолор үчүн бул уруксатты өзгөртүү үчүн таптап коюңуз." "Кийинчерээк эскертүү" diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index fb677cf1e44feccd6a1fa61e7355339c1561674b..be09c1081f2886a2e9032c885fb46ac356d7699c 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -2129,11 +2129,6 @@ "ການໂທ ແລະ ການແຈ້ງເຕືອນຈະສັ່ນ" "ການໂທ ແລະ ການແຈ້ງເຕືອນຈະບໍ່ມີສຽງ" "ການປ່ຽນແປງລະບົບ" - "ຫ້າມລົບກວນ" - "ໃໝ່: ໂໝດຫ້າມລົບກວນຈະເຊື່ອງການແຈ້ງເຕືອນໄວ້" - "ແຕະເພື່ອສຶກສາເພີ່ມເຕີມ ແລະ ປ່ຽນແປງ." - "ປ່ຽນໂໝດຫ້າມລົບກວນແລ້ວ" - "ແຕະເພື່ອກວດສອບວ່າມີຫຍັງຖືກບລັອກໄວ້ແດ່." "ກວດສອບ​ການ​ຕັ້ງ​ຄ່າ​ການ​ແຈ້ງ​ເຕືອນ" "ເລີ່ມຕົ້ນໃນ Android 13, ແອັບຕ່າງໆທີ່ທ່ານຕິດຕັ້ງຈະຕ້ອງໃຊ້ການອະນຸຍາດຂອງທ່ານເພື່ອສົ່ງການແຈ້ງເຕືອນ. ແຕະເພື່ອປ່ຽນການອະນຸຍາດນີ້ສຳລັບແອັບທີ່ມີຢູ່ກ່ອນແລ້ວ." "ແຈ້ງເຕືອນຂ້ອຍພາຍຫຼັງ" diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index e617a4cfbfc623e4978e1cfe7a602646ecd0fd30..45998ee3878721f5b50cdada67de24bf1b3dc2eb 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -2131,11 +2131,6 @@ "Skambučiai ir pranešimai vibruos" "Skambučiai ir pranešimai bus nutildyti" "Sistemos pakeitimai" - "Netrukdymo režimas" - "Naujiena: naudojant netrukdymo režimą pranešimai slepiami" - "Palieskite, kad sužinotumėte daugiau ir pakeistumėte." - "Netrukdymo režimas pakeistas" - "Palieskite, kad patikrintumėte, kas blokuojama." "Peržiūrėkite pranešimų nustatymus" "Tryliktos ir naujesnių versijų „Android” jūsų įdiegtoms programoms reikia suteikti leidimą siųsti pranešimus. Palieskite, kad pakeistumėte šį leidimą esamoms programoms." "Priminti vėliau" diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index b04dd4521a3179dd019685fc170bcbfad105877e..973d8e2c0568394a9529282a7d903c3fe9d8dc51 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -2130,11 +2130,6 @@ "Zvaniem un paziņojumiem tiks aktivizēta vibrācija." "Zvanu un paziņojumu signāla skaņa būs izslēgta." "Sistēmas izmaiņas" - "Netraucēt" - "Jaunums: režīmā “Netraucēt” paziņojumi tiek paslēpti" - "Pieskarieties, lai uzzinātu vairāk un veiktu izmaiņas." - "Režīms “Netraucēt” ir mainīts" - "Pieskarieties, lai uzzinātu, kas tiek bloķēts." "Pārskatīt paziņojumu iestatījumus" "Operētājsistēmā Android 13 un jaunākās versijās jūsu instalētajām lietotnēm ir nepieciešama atļauja sūtīt paziņojumus. Pieskarieties, lai mainītu šo atļauju esošajām lietotnēm." "Atgādināt vēlāk" diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index b0d6351f5667cb78894c77667f5b95983a425686..3943dc359bd98e0e62d4561a745a62d7dd933495 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -2129,11 +2129,6 @@ "Повиците и известувањата ќе вибрираат" "Повиците и известувањата нема да имаат звук" "Системски промени" - "Не вознемирувај" - "Ново: режимот „Не вознемирувај“ ги крие известувањата" - "Допрете за да дознаете повеќе и да ги промените поставките." - "Поставките за „Не вознемирувај“ се изменија" - "Допрете за да проверите што е блокирано." "Прегледајте ги поставките за известувања" "Почнувајќи од Android 13, на апликациите што ги инсталирате им е потребна ваша дозвола за испраќање известувања. Допрете за да ја промените оваа дозвола за постојни апликации." "Потсети ме подоцна" diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index cc545e29ac4f233b48a1c123b92ee727589305df..242e9679baf682867d671e08cef5268475af7a15 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -2129,11 +2129,6 @@ "കോളുകളും അറിയിപ്പുകളും വൈബ്രേറ്റ് ചെയ്യും" "കോളുകളും അറിയിപ്പുകളും മ്യൂട്ട് ചെയ്യപ്പെടും" "സിസ്‌റ്റത്തിലെ മാറ്റങ്ങൾ" - "ശല്യപ്പെടുത്തരുത്" - "പുതിയത്: അറിയിപ്പുകളെ \'ശല്യപ്പെടുത്തരുത്\' അദൃശ്യമാക്കുന്നു" - "കൂടുതലറിയാനും മാറ്റാനും ടാപ്പ് ചെയ്യുക." - "\'ശല്യപ്പെടുത്തരുത്\' മാറ്റി" - "എന്തിനെയാണ് ബ്ലോക്ക് ചെയ്‌തതെന്ന് പരിശോധിക്കാൻ ടാപ്പ് ചെയ്യുക." "അറിയിപ്പ് ക്രമീകരണം അവലോകനം ചെയ്യുക" "Android 13 മുതൽ, നിങ്ങൾ ഇൻസ്‌റ്റാൾ ചെയ്യുന്ന ആപ്പുകൾക്ക് അറിയിപ്പുകൾ അയയ്‌ക്കാൻ നിങ്ങളുടെ അനുമതി വേണം. നിലവിലുള്ള ആപ്പുകൾക്ക് ഈ അനുമതി മാറ്റാൻ ടാപ്പ് ചെയ്യുക." "പിന്നീട് ഓർമ്മിപ്പിക്കൂ" diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 87ce94ba6939d77660e2089a8dcc748be1691cee..c8f2eaaf0b5cce64191c94a5d342234dfb615a58 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -2129,11 +2129,6 @@ "Дуудлага болон мэдэгдэл чичирнэ" "Дуудлага болон мэдэгдлийн дууг хаана" "Системийн өөрчлөлт" - "Бүү саад бол" - "Шинэ: Бүү саад бол горим мэдэгдлийг нууж байна" - "Илүү ихийг мэдэж, өөрчлөхийн тулд товшино уу." - "Бүү саад бол горимыг өөрчилсөн" - "Блоклосон зүйлийг шалгахын тулд товшино уу." "Мэдэгдлийн тохиргоог шалгах" "Android 13-аас эхлэн таны суулгасан аппууд мэдэгдэл илгээхийн тулд танаас зөвшөөрөл авах шаардлагатай. Одоо байгаа аппуудын уг зөвшөөрлийг өөрчлөхийн тулд товшино уу." "Надад дараа сануул" diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 03cabc31f573b1255d8ca4b6f98dd5756891fcaa..9980d9f5e4c8a594713b8b9a423bac5f513789e4 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -2129,11 +2129,6 @@ "कॉल आणि सूचनांवर व्हायब्रेट होईल" "कॉल आणि सूचना म्यूट केल्या जातील" "सिस्टम बदल" - "व्यत्यय आणू नका" - "व्यत्यय आणू नका सूचना लपवत आहे" - "अधिक जाणून घेण्‍यासाठी आणि बदलण्‍यासाठी टॅप करा." - "व्यत्यय आणू नका बदलले आहे" - "काय ब्लॉक केले आहे हे तपासण्यासाठी टॅप करा." "सूचना सेटिंग्जचे पुनरावलोकन करा" "Android 13 पासून, तुम्ही त्यामध्ये इंस्टॉल केलेल्या अ‍ॅप्सना सूचना पाठवण्यासाठी तुमच्या परवानगीची आवश्यकता आहे. सध्याच्या अ‍ॅप्ससाठी ही परवानगी बदलण्याकरिता टॅप करा." "मला आठवण करून द्या" diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 57f1472b6f34858e48f3dd1a23a6dcdb51050464..e8116355477e352e4c296af1718f7942d54f1cd7 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -2129,11 +2129,6 @@ "Panggilan dan pemberitahuan akan bergetar" "Panggilan dan pemberitahuan akan diredamkan" "Perubahan sistem" - "Jangan Ganggu" - "Baharu: Jangan Ganggu menyembunyikan pemberitahuan" - "Ketik untuk mengetahui lebih lanjut dan menukar tetapan." - "Jangan Ganggu telah berubah" - "Ketik untuk menyemak item yang disekat." "Semak tetapan pemberitahuan" "Bermula dengan Android 13, apl yang anda pasang memerlukan kebenaran anda untuk menghantar pemberitahuan. Ketik untuk menukar kebenaran ini bagi apl sedia ada." "Ingatkan saya nanti" diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index f568d7112cc01ccd29ccbbf23ae6ae0d0461e47d..7088a15797f3e5b9222704afc792af0ef1417dd8 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -322,7 +322,7 @@ "တည်နေရာ" "ဤစက်ပစ္စည်း၏ တည်နေရာကို ရယူရန်" "ပြက္ခဒိန်" - "သင့်ပြက္ခဒိန်အား ဝင်ရောက်သုံးရန်" + "သင့်ပြက္ခဒိန်အား ဝင်သုံးရန်" "SMS စာတိုစနစ်" "SMS စာများကို ပို့ကာ ကြည့်မည်" "ဖိုင်များ" @@ -2129,11 +2129,6 @@ "ခေါ်ဆိုမှုများနှင့် အကြောင်းကြားချက်များ တုန်ခါပါမည်" "ခေါ်ဆိုမှုများနှင့် အကြောင်းကြားချက်များကို အသံပိတ်ထားပါမည်" "စနစ် အပြောင်းအလဲများ" - "မနှောင့်ယှက်ရ" - "အသစ်− \'မနှောင့်ယှက်ရ\' က အကြောင်းကြားချက်များကို ဖျောက်ထားသည်" - "ပိုမိုလေ့လာရန်နှင့် ပြောင်းလဲရန် တို့ပါ။" - "\'မနှောင့်ယှက်ရ\' ပြောင်းလဲသွားပါပြီ" - "ပိတ်ထားသည့်အရာများကို ကြည့်ရန် တို့ပါ။" "အကြောင်းကြားချက် ဆက်တင်များ စိစစ်ရန်" "Android 13 မှစ၍ ထည့်သွင်းသော အက်ပ်များသည် အကြောင်းကြားချက်များပို့ရန် သင်၏ခွင့်ပြုချက် လိုအပ်ပါမည်။ ရှိပြီးသားအက်ပ်များအတွက် ဤခွင့်ပြုချက်ကိုပြောင်းရန် တို့ပါ။" "နောက်မှ သတိပေးပါ" diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 6d24bdfc39b397323dcff3abcfce84a7f082de0a..f39a4f8a1a8ee85ed3fbe1f78a4511f0e2271975 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -2129,11 +2129,6 @@ "Anrop og varsler vibrerer" "Anrop og varsler er lydløse" "Systemendringer" - "Ikke forstyrr" - "Nytt: «Ikke forstyrr» skjuler varsler" - "Trykk for å finne ut mer og endre." - "Ikke forstyrr er endret" - "Trykk for å sjekke hva som er blokkert." "Gjennomgå varslingsinnstillingene" "Fra og med Android 13 må apper du installerer, få tillatelse til å sende varsler. Trykk for å endre denne tillatelsen for eksisterende apper." "Påminn meg senere" diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 914c04c6549518d43670f1ad159f73502ac87fec..82052b5b2806ac6c8e991b40bada1b2ec773a36f 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -2129,11 +2129,6 @@ "कल तथा सूचनाहरू आउँदा कम्पन हुने छ" "कल तथा सूचनाहरूलाई म्युट गरिने छ" "प्रणालीसम्बन्धी परिवर्तनहरू" - "बाधा नपुऱ्याउनुहोस्" - "नयाँ: बाधा नपुर्‍याउनुहोस् नामक मोडले सूचनाहरू लुकाइरहेको छ" - "थप जान्न र परिवर्तन गर्न ट्याप गर्नुहोस्।" - "बाधा नपुर्‍याउनुहोस् मोड परिवर्तन भएको छ" - "रोक लगाइएका कुराहरू जाँच गर्न ट्याप गर्नुहोस्‌।" "सूचनाका सेटिङको समीक्षा गर्नुहोस्" "Android १३ मा तपाईंले अनुमति दिनुभएका खण्डमा मात्र तपाईंले इन्स्टल गर्नुभएका एपले सूचना पठाउन सक्छन्। यसअघि इन्स्टल गरिसकिएका एपका हकमा यो अनुमति परिवर्तन गर्न ट्याप गर्नुहोस्।" "मलाई पछि स्मरण गराइयोस्" diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index d2ec4ba725a3bf8e505bf79f10d10367b4e86b94..f591754aafb65c3a36e3feaa5963af609722a0d7 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -2129,11 +2129,6 @@ "Trillen bij gesprekken en meldingen" "Telefoon- en meldingsgeluid wordt uitgezet" "Systeemwijzigingen" - "Niet storen" - "Nieuw: \'Niet storen\' verbergt meldingen" - "Tik voor meer informatie en om te wijzigen." - "\'Niet storen\' is gewijzigd" - "Tik om te controleren wat er is geblokkeerd." "Instellingen voor meldingen bekijken" "Vanaf Android 13 hebben de apps die je installeert je toestemming nodig om meldingen te sturen. Tik om deze toestemming voor bestaande apps te wijzigen." "Later herinneren" diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 85a6aecda0bd31d1f882d52ba6b2a4d8d2b6817e..1a952a3a8e14f0ad3c70a7ccc2aa6557e7f53147 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -2129,11 +2129,6 @@ "କଲ୍ ଓ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଭାଇବ୍ରେଟ୍ ହେବ" "କଲ୍ ଓ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ନିଃଶବ୍ଦ କରିଦିଆଯିବ" "ସିଷ୍ଟମ୍‌ରେ ପରିବର୍ତ୍ତନ" - "ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ" - "ନୂଆ: \"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ମୋଡ୍‌ ଅନ୍‌ ଥିବା ଯୋଗୁଁ ବିଜ୍ଞପ୍ତି ଲୁଚାଇ ଦିଆଯାଉଛି" - "ଅଧିକ ଜାଣିବାକୁ ଟ୍ୟାପ୍‌ କରନ୍ତୁ ଏବଂ ବଦଳାନ୍ତୁ।" - "’ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ’ ବଦଳିଯାଇଛି" - "କ’ଣ ଅବରୋଧ ହୋଇଛି ଯାଞ୍ଚ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।" "ବିଜ୍ଞପ୍ତି ସେଟିଂସକୁ ସମୀକ୍ଷା କରନ୍ତୁ" "Android 13ଠାରୁ, ଆପଣ ଇନଷ୍ଟଲ କରୁଥିବା ଆପ୍ସ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ପଠାଇବା ପାଇଁ ଆପଣଙ୍କ ଅନୁମତି ଆବଶ୍ୟକ କରେ। ପୂର୍ବରୁ ଥିବା ଆପ୍ସ ପାଇଁ ଏହି ଅନୁମତିକୁ ପରିବର୍ତ୍ତନ କରିବାକୁ ଟାପ କରନ୍ତୁ।" "ମୋତେ ପରେ ରିମାଇଣ୍ଡ କର" diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 51d71ff28260f1737b88b9418fd2a9f01d1e7167..69535091213ac3ae7e1e565c63e65bbf51ba1737 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -2129,11 +2129,6 @@ "ਕਾਲਾਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਦੀ ਥਰਥਰਾਹਟ ਹੋਵੇਗੀ" "ਕਾਲਾਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਨੂੰ ਮਿਊਟ ਕੀਤਾ ਜਾਵੇਗਾ" "ਸਿਸਟਮ ਬਦਲਾਅ" - "ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ" - "ਨਵਾਂ: \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਮੋਡ ਸੂਚਨਾਵਾਂ ਨੂੰ ਲੁਕਾ ਰਿਹਾ ਹੈ" - "ਹੋਰ ਜਾਣਨ ਲਈ ਅਤੇ ਬਦਲਾਅ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।" - "\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਵਿਕਲਪ ਬਦਲ ਗਿਆ ਹੈ" - "ਟੈਪ ਕਰਕੇ ਦੋਖੋ ਕਿ ਕਿਹੜੀਆਂ ਚੀਜ਼ਾਂ ਬਲਾਕ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ।" "ਸੂਚਨਾ ਸੈਟਿੰਗਾਂ ਦੀ ਸਮੀਖਿਆ ਕਰੋ" "Android 13 ਜਾਂ ਇਸ ਤੋਂ ਬਾਅਦ ਵਾਲੇ ਵਰਜਨਾਂ ਵਿੱਚ, ਤੁਹਾਡੇ ਵੱਲੋਂ ਸਥਾਪਤ ਕੀਤੀਆਂ ਜਾਣ ਵਾਲੀਆਂ ਐਪਾਂ ਨੂੰ ਸੂਚਨਾਵਾਂ ਭੇਜਣ ਲਈ ਤੁਹਾਡੀ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ। ਮੌਜੂਦਾ ਐਪਾਂ ਲਈ ਇਸ ਇਜਾਜ਼ਤ ਨੂੰ ਬਦਲਣ ਵਾਸਤੇ ਟੈਪ ਕਰੋ।" "ਬਾਅਦ ਵਿੱਚ ਯਾਦ ਕਰਵਾਓ" diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 49c30c7710440af09fdc4db47fe5267e37ed697e..065e9bae533408ec7d943b79ab7b67934aeb0612 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -2131,11 +2131,6 @@ "Wibracje przy połączeniach i powiadomieniach" "Wyciszenie połączeń i powiadomień" "Zmiany w systemie" - "Nie przeszkadzać" - "Nowość: w trybie Nie przeszkadzać powiadomienia są ukrywane" - "Kliknij, by dowiedzieć się więcej i zmienić ustawienia." - "Zmiany w trybie Nie przeszkadzać" - "Kliknij, by sprawdzić, co jest zablokowane." "Sprawdź ustawienia powiadomień" "W Androidzie 13 i nowszych zainstalowane aplikacje będą potrzebowały zezwolenia na wysyłanie powiadomień. Kliknij, aby zmienić uprawnienia dla istniejących aplikacji." "Przypomnij później" diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 99ef60484276741027da06bb9fa157f7f001008b..c972e7afa3b17a81dd5700a7c4a723176df95b3f 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -2130,11 +2130,6 @@ "Chamadas e notificações farão o dispositivo vibrar" "Chamadas e notificações ficarão silenciadas" "Alterações do sistema" - "Não perturbe" - "Novo: o modo Não perturbe está ocultando as notificações" - "Toque para saber mais e fazer alterações." - "O modo \"Não perturbe\" foi alterado" - "Toque para verificar o que está bloqueado." "Revise as configurações de notificação" "No Android 13 ou em versões mais recentes, os apps que você instala precisam de permissão para enviar notificações. Toque para mudar essa permissão para os apps já instalados." "Lembrar mais tarde" diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 12245b9a65e349f8f73e44691f4200f423674e41..8dddd95b87cc1d85ef3430fd436c7fbe77679a74 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -2130,11 +2130,6 @@ "As chamadas e as notificações vibram." "É desativado o som das chamadas e das notificações." "Alterações ao sistema" - "Não incomodar" - "Novo: o modo Não incomodar está a ocultar as notificações" - "Toque para saber mais e alterar." - "O modo Não incomodar foi alterado" - "Toque para verificar o que está bloqueado." "Analise as definições de notificação" "A partir do Android 13, as apps que instalar precisam da sua autorização para enviar notificações. Toque para alterar esta autorização para as apps existentes." "Lembrar mais tarde" diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 99ef60484276741027da06bb9fa157f7f001008b..c972e7afa3b17a81dd5700a7c4a723176df95b3f 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -2130,11 +2130,6 @@ "Chamadas e notificações farão o dispositivo vibrar" "Chamadas e notificações ficarão silenciadas" "Alterações do sistema" - "Não perturbe" - "Novo: o modo Não perturbe está ocultando as notificações" - "Toque para saber mais e fazer alterações." - "O modo \"Não perturbe\" foi alterado" - "Toque para verificar o que está bloqueado." "Revise as configurações de notificação" "No Android 13 ou em versões mais recentes, os apps que você instala precisam de permissão para enviar notificações. Toque para mudar essa permissão para os apps já instalados." "Lembrar mais tarde" diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 85f859240b7f9471edb7134e59893b90840c910f..014a11543ea20d26ec3c9fba3a13712f0136782c 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -2130,11 +2130,6 @@ "Apelurile și notificările vor vibra" "Apelurile și notificările vor avea sunetul dezactivat" "Modificări de sistem" - "Nu deranja" - "Funcția nouă Nu deranja ascunde notificările" - "Atinge ca să afli mai multe și să modifici" - "Funcția Nu deranja s-a schimbat" - "Atinge pentru a verifica ce este blocat." "Verifică setările pentru notificări" "Începând cu Android 13, aplicațiile pe care le instalezi necesită permisiunea de a trimite notificări. Atinge ca să modifici permisiunea pentru aplicațiile existente." "Mai târziu" diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 65597649917c83a3438f72f4440bf323885a7e7f..1e819d6078585f34fa8f2469b161292c0b6da15f 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -2131,11 +2131,6 @@ "Для звонков и уведомлений включен вибросигнал." "Для звонков и уведомлений отключен звук." "Системные изменения" - "Не беспокоить" - "Теперь в режиме \"Не беспокоить\" уведомления не приходят" - "Нажмите, чтобы узнать больше и изменить настройки." - "Настройки режима \"Не беспокоить\" изменены" - "Нажмите, чтобы проверить настройки." "Проверьте настройки уведомлений" "В Android 13 и более поздних версий приложения могут отправлять вам уведомления только в том случае, если вы предоставили им такое разрешение. Нажмите, чтобы настроить разрешения для установленных приложений." "Напомнить позже" diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index be4151280e3af514de84e9db81adc9475a493f23..8c808038f89881ccfb441f1e403ceaa1886f0e58 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -2129,11 +2129,6 @@ "ඇමතුම් සහ දැනුම්දීම් කම්පනය වනු ඇත" "ඇමතුම් සහ දැනුම්දීම් නිහඬ වනු ඇත" "පද්ධති වෙනස් කිරීම්" - "බාධා නොකරන්න" - "නව: බාධා නොකරන්න දැනුම්දීම් සඟවමින්" - "තව දැන ගැනීමට සහ වෙනස් කිරීමට තට්ටු කරන්න." - "බාධා නොකරන්න වෙනස් කර ඇත" - "අවහිර කර ඇති දේ පරීක්ෂා කිරීමට තට්ටු කරන්න." "දැනුම්දීම් සැකසීම් සමාලෝචනය කරන්න" "Android 13 හි සිට ආරම්භ වෙමින්, ඔබ ස්ථාපනය කරන යෙදුම්වලට දැනුම්දීම් යැවීමට ඔබගේ අවසරය අවශ්‍ය වේ. තිබෙන යෙදුම් සඳහා මෙම අවසරය වෙනස් කිරීමට තට්ටු කරන්න." "මට පසුව මතක් කරන්න" diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 61d322951879b6ec19dcc64e344b97fca4e6b094..cb3cd610c67882dc99b46dc75994e4abccfebab2 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -2131,11 +2131,6 @@ "Hovory a upozornenia budú vibrovať" "Hovory a upozornenia budú stlmené" "Zmeny systému" - "Režim bez vyrušení" - "Novinka: režim bez vyrušení skrýva upozornenia" - "Klepnutím získate ďalšie informácie a budete môcť vykonať zmeny." - "Režim bez vyrušení sa zmenil" - "Klepnutím skontrolujete, čo je blokované." "Kontrola nastavení upozornení" "V Androide verzie 13 a novších vyžadujú nainštalované aplikácie povolenie, aby mohli odosielať upozornenia. Klepnutím môžete zmeniť toto povolenie pre existujúce aplikácie." "Pripomenúť neskôr" diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index ed8cf505d7eb43d5c88a423c6df3ae299bc5475c..5378875ed2f298440c0b29c3cbda5d1d366ab8d0 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -2131,11 +2131,6 @@ "Vibriranje bo vklopljeno za klice in obvestila" "Zvonjenje bo izklopljeno za klice in obvestila" "Sistemske spremembe" - "Ne moti" - "Novi način »ne moti« skriva obvestila" - "Dotaknite se, če želite izvedeti več in spremeniti." - "Način »ne moti« je spremenjen" - "Dotaknite se, da preverite, kaj je blokirano." "Preglejte nastavitve obvestil" "V Androidu 13 in novejših različicah bodo aplikacije, ki jih namestite, za pošiljanje obvestil potrebovale vaše dovoljenje. Dotaknite se, če želite spremeniti to dovoljenje za obstoječe aplikacije." "Opomni me pozneje" diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index e24e310e5772eb2760425834382a2f266545553c..4c8dfbcf6f1ee4cd81528cca0757ce422296a8db 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -2129,11 +2129,6 @@ "Do të lëshojë dridhje për telefonatat dhe njoftimet" "Do të hiqet zëri për telefonatat dhe njoftimet" "Ndryshimet e sistemit" - "Mos shqetëso" - "E re: Modaliteti \"Mos shqetëso\" po fsheh njoftimet" - "Trokit për të mësuar më shumë dhe për të ndryshuar." - "\"Mos shqetëso\" ka ndryshuar" - "Trokit për të shënuar atë që është bllokuar" "Rishiko cilësimet e njoftimeve" "Nga Android 13, aplikacionet që instalon kanë nevojë për lejen tënde për të dërguar njoftime. Trokit për ta ndryshuar këtë leje për aplikacionet ekzistuese." "Më kujto më vonë" diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 915486e990e96849e8a1761bfc36d43dbd85429c..c9f44dbbfc43332ac2a0b267cc7e3444c7cede8e 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -2130,11 +2130,6 @@ "Вибрација за позиве и обавештења је укључена" "Мелодија звона за позиве и обавештење је искључена" "Системске промене" - "Не узнемиравај" - "Ново: Режим Не узнемиравај крије обавештења" - "Додирните да бисте сазнали више и променили подешавање." - "Режим Не узнемиравај је промењен" - "Додирните да бисте проверили шта је блокирано." "Прегледајте подешавања обавештења" "Од Android-а 13 апликације које инсталирате морају да имају дозволу за слање обавештења. Додирните да бисте променили ову дозволу за постојеће апликације." "Подсети ме касније" diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 0c5b6acec0fd06b0e7c9769b02403adf39980fd5..574b4e8ce92504150cd2e2ab8c228a7c6d3026c4 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -202,7 +202,7 @@ "För många försök med lösenord" "Administratören tillåter inte längre privat bruk av enheten" "Privat område har tagits bort" - "Din organisation tillåter inte privata områden på den här hanterade enheten." + "Din organisation tillåter inte privata utrymmen på den här hanterade enheten." "Enheten hanteras" "Organisationen hanterar den här enheten och kan övervaka nätverkstrafiken. Tryck om du vill veta mer." "Appar har åtkomst till din plats" @@ -2009,8 +2009,8 @@ "Nödsituation" "Ställ in ett skärmlås" "Ställ in skärmlås" - "Ställ in ett skärmlås för enheten om du vill använda ditt privata område." - "Ange ett skärmlås för enheten om du vill radera privat område" + "Ställ in ett skärmlås för enheten om du vill använda ditt privata utrymme." + "Ange ett skärmlås för enheten om du vill radera privat utrymme" "Appen är inte tillgänglig" "%1$s är inte tillgängligt just nu." "%1$s är inte tillgänglig" @@ -2129,11 +2129,6 @@ "Vibrerar vid samtal och aviseringar" "Ljudet stängs av för samtal och aviseringar" "Systemändringar" - "Stör ej" - "Nytt: Aviseringar döljs av Stör ej" - "Tryck här om du vill läsa mer och ändra inställningarna." - "Stör ej har ändrats" - "Tryck om du vill se vad som blockeras." "Granska aviseringsinställningarna" "I Android 13 behöver appar som du installerar behörighet att skicka aviseringar. Tryck om du vill ändra denna behörighet för befintliga appar." "Påminn mig senare" diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index c002866aa6ad86e1bc75d51c277e88ba874e8cf1..676e7abef414c7cee98d872ffbb0edef392f1be6 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -2129,11 +2129,6 @@ "Itatetema arifa ikitumwa au simu ikipigwa" "Haitatoa mlio arifa ikitumwa au simu ikipigwa" "Mabadiliko kwenye mfumo" - "Usinisumbue" - "Mpya: Kipengele cha Usinisumbue kinaficha arifa" - "Gusa ili upate maelezo zaidi na ubadilishe." - "Kipengele cha Usinisumbue kimebadilishwa" - "Gusa ili uangalie kipengee ambacho kimezuiwa." "Kagua mipangilio ya arifa" "Kuanzia Android toleo la 13, programu unazosakinisha zitahitaji ruhusa yako ili zitume arifa. Gusa ili ubadilishe ruhusa hii kwa programu zilizopo." "Nikumbushe baadaye" diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index dfc6a791e8769a1ecb4d9281995c089702411c02..35cad789af8a9137e29e55e46e66098e898ef9d1 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -2129,11 +2129,6 @@ "அழைப்புகள் மற்றும் அறிவிப்புகளுக்கு அதிரும்" "அழைப்புகள் மற்றும் அறிவிப்புகளுக்கு ஒலியை முடக்கும்" "சிஸ்டம் மாற்றங்கள்" - "தொந்தரவு செய்ய வேண்டாம்" - "புதியது: \'தொந்தரவு செய்ய வேண்டாம்\' பயன்முறையானது அறிவிப்புகளைக் காட்டாமல் மறைக்கிறது" - "மேலும் அறிந்து மாற்ற, தட்டவும்." - "தொந்தரவு செய்ய வேண்டாம் அமைப்புகள் மாற்றப்பட்டன" - "எவற்றையெல்லாம் தடுக்கிறது என்பதைப் பார்க்க, தட்டவும்." "அறிவிப்பு அமைப்புகளை மதிப்பாய்வு செய்யுங்கள்" "Android 13 பதிப்பு முதல், நீங்கள் நிறுவுகின்ற ஆப்ஸ் உங்களுக்கு அறிவிப்புகளை அனுப்ப அனுமதி தேவை. ஏற்கெனவே உள்ள ஆப்ஸுக்கு இந்த அனுமதியை மாற்ற தட்டவும்." "பின்னர் நினைவூட்டு" diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 70732664c973a900117fd2f43487302a01902a3e..fd78f1d13304c69f14660ff1640302a7c6c3f248 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -2129,11 +2129,6 @@ "కాల్స్‌ మరియు నోటిఫికేషన్‌లు వైబ్రేట్ అవుతాయి" "కాల్స్‌ మరియు నోటిఫికేషన్‌లు మ్యూట్ చేయబడతాయి" "సిస్టమ్ మార్పులు" - "అంతరాయం కలిగించవద్దు" - "కొత్తది: అంతరాయం కలిగించవద్దు నోటిఫికేషన్‌లను దాస్తోంది" - "మరింత తెలుసుకోవడానికి మరియు మార్చడానికి నొక్కండి." - "అంతరాయం కలిగించవద్దు మార్చబడింది" - "బ్లాక్ చేయబడిన దాన్ని చెక్ చేయడానికి నొక్కండి." "నోటిఫికేషన్ సెట్టింగ్‌లను రివ్యూ చేయండి" "Android 13తో మొదలుకుని, మీరు ఇన్‌స్టాల్ చేసే యాప్‌లకు నోటిఫికేషన్‌లను పంపడానికి మీ అనుమతి అవసరం. ఇప్పటికే ఉన్న యాప్‌ల కోసం ఈ అనుమతిని మార్చడానికి ట్యాప్ చేయండి." "తర్వాత గుర్తు చేయి" diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 5ee187fa8a41ef2b690cecc5ae07d437111a4a3c..cbac93d4d6811dd4eaf577a084b2a864942206b0 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -2129,11 +2129,6 @@ "สายเรียกเข้าและการแจ้งเตือนจะสั่น" "สายเรียกเข้าและการแจ้งเตือนจะไม่ส่งเสียง" "การเปลี่ยนแปลงระบบ" - "ห้ามรบกวน" - "ใหม่: โหมดห้ามรบกวนซ่อนการแจ้งเตือนไว้" - "แตะเพื่อดูข้อมูลเพิ่มเติมและเปลี่ยนแปลง" - "เปลี่ยน \"ห้ามรบกวน\" แล้ว" - "แตะเพื่อดูรายการที่ถูกบล็อก" "ตรวจสอบการตั้งค่าการแจ้งเตือน" "ตั้งแต่ Android 13 เป็นต้นไป แอปที่คุณติดตั้งจะต้องได้รับสิทธิ์จากคุณเพื่อส่งการแจ้งเตือน แตะเพื่อเปลี่ยนแปลงสิทธิ์นี้สำหรับแอปที่มีอยู่" "เตือนภายหลัง" diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index e76dacdf019114c11c474a12312bbecb3bbb4f59..f9edcdca9a4a612c861006b8ca7c192af6ee1c41 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -2129,11 +2129,6 @@ "Magva-vibrate ang mga tawag at notification" "Mamu-mute ang mga tawag at notification" "Mga pagbabago sa system" - "Huwag Istorbohin" - "Bago: Itinatago ng Huwag Istorbohin ang mga notification" - "I-tap para matuto pa at baguhin." - "Binago ang Huwag Istorbohin" - "I-tap para tingnan kung ano ang naka-block." "Suriin ang mga setting ng notification" "Simula sa Android 13, kakailanganin na ng mga app na ii-install mo ang iyong pahintulot para makapagpadala ng mga notification. I-tap para baguhin ang pahintulot na ito para sa mga kasalukuyang app." "Ipaalala mamaya" diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index ad6fc0de5cd5b7484484a45315b49bd333df97a4..fea095101da9559d196e4b29f87169a766acef0e 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -2129,11 +2129,6 @@ "Aramalar ve bildirimler titreşim yapacak" "Aramalar ve bildirimlerin sesi kapalı olacak" "Sistem değişiklikleri" - "Rahatsız Etmeyin" - "Yeni: Rahatsız Etmeyin ayarı bildirimleri gizliyor" - "Daha fazla bilgi edinmek ve değiştirmek için dokunun." - "Rahatsız Etmeyin modu değişti" - "Nelerin engellendiğini kontrol etmek için dokunun." "Bildirim ayarlarını inceleyin" "Android 13\'te başlayarak yüklediğiniz uygulamaların bildirim gönderebilmesi için izniniz gereklidir. Mevcut uygulamalarda bu izni değiştirmek için dokunun." "Sonra hatırlat" diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index a63f3fbf4e922282e1e05c301303ec24b0cfa2e1..68f85b8b53425490e3bd46ce5a6374e9413ea94d 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -2131,11 +2131,6 @@ "Вібросигнал для викликів і сповіщень увімкнено" "Звуковий сигнал для викликів і сповіщень вимкнено" "Системні зміни" - "Не турбувати" - "Нове: у режимі \"Не турбувати\" сповіщення ховаються" - "Торкніться, щоб дізнатися більше та змінити." - "Налаштування режиму \"Не турбувати\" змінено" - "Торкніться, щоб перевірити, що заблоковано." "Перегляньте налаштування сповіщень" "Починаючи з ОС Android 13, установленим додаткам потрібно надати дозвіл, щоб вони могли надсилати сповіщення. Натисніть, щоб змінити цей дозвіл для наявних додатків." "Нагадати пізніше" diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 52d9e3bbc5cc80d6dc8915984b414c0047a28069..4f0971765bcc4a1c04ffd9788b9a6c669a5cf022 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -2129,11 +2129,6 @@ "کالز اور اطلاعات پر وائبریٹ کرے گا" "کالز اور اطلاعات کی آواز خاموش کر دی جائے گی" "سسٹم کی تبدیلیاں" - "ڈسٹرب نہ کریں" - "نئی: \'ڈسٹرب نہ کریں\' اطلاعات کو چھپا رہی ہے" - "مزید جاننے اور تبدیل کرنے کیلئے تھپتھپائیں۔" - "\'ڈسٹرب نہ کریں\' تبدیل ہو گيا ہے" - "مسدود کی گئی چیزوں کو چیک کرنے کے لیے تھپتھپائیں۔" "اطلاع کی ترتیبات کا جائزہ لیں" "‏Android 13 کے ساتھ اب آپ جو بھی ایپس انسٹال کریں گے انہیں اطلاعات بھیجنے کے لیے آپ کی اجازت درکار ہوگی۔ موجودہ ایپس کے لیے اس اجازت کو تبدیل کرنے کی خاطر تھپتھپائیں۔" "بعد میں یاد دلائیں" diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index f62074e060d779859d289f9313a0626c8ab02b0a..1bb3bcdc9759e7213d0325629ea22e37420842b8 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -2129,11 +2129,6 @@ "Chaqiruvlar va bildirishnomalar tebranadi" "Chaqiruvlar va bildirishnomalar ovozsiz qilinadi" "Tizimga oid o‘zgarishlar" - "Bezovta qilinmasin" - "Yangi: Bezovta qilinmasin rejimi bildirishnomalarni berkitmoqda" - "Batafsil axborot olish va o‘zgartirish uchun bosing." - "Bezovta qilinmasin rejimi sozlamalari o‘zgartirildi" - "Nimalar bloklanganini tekshirish uchun bosing" "Bildirishnoma sozlamalarini tekshiring" "Android 13 versiyasidan boshlab, oʻrnatiladigan ilovalar bildirishnoma yuborish uchun sizdan ruxsat oladi. Mavjud ilovalarda ushbu ruxsatni oʻzgartirish uchun bosing." "Keyinroq eslatilsin" diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 3b08a22ea956f745d2879b6477aac0599f4c2133..3b1b21d7c3c63a827cbdc71c0d18d12908e1e8b3 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -2129,11 +2129,6 @@ "Cuộc gọi và thông báo sẽ rung" "Cuộc gọi và thông báo sẽ tắt tiếng" "Thay đổi hệ thống" - "Không làm phiền" - "Mới: Chế độ Không làm phiền sẽ ẩn thông báo" - "Nhấn để tìm hiểu thêm và thay đổi." - "Cài đặt Không làm phiền đã thay đổi" - "Nhấn để xem những thông báo bị chặn." "Xem lại chế độ cài đặt thông báo" "Bắt đầu trên Android 13, các ứng dụng bạn cài đặt sẽ cần bạn cấp quyền để gửi thông báo. Hãy nhấn để thay đổi quyền này cho các ứng dụng hiện có." "Nhắc tôi sau" diff --git a/core/res/res/values-watch-v36/colors.xml b/core/res/res/values-watch-v36/colors.xml new file mode 100644 index 0000000000000000000000000000000000000000..4bc2a66fa206c2175d23769f1d4a04de5f91706d --- /dev/null +++ b/core/res/res/values-watch-v36/colors.xml @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git a/core/res/res/values-watch-v36/config.xml b/core/res/res/values-watch-v36/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..c8f347afb3185a269d8ab36ab5ebccb4e292ffe1 --- /dev/null +++ b/core/res/res/values-watch-v36/config.xml @@ -0,0 +1,20 @@ + + + + + 26dp + diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml new file mode 100644 index 0000000000000000000000000000000000000000..ad3c1a3ef3a11baa0667a2b4cbec3dbe3703388a --- /dev/null +++ b/core/res/res/values-watch-v36/dimens_material.xml @@ -0,0 +1,28 @@ + + + + 172dp + 52dp + 14dp + 6dp + 18sp + 15sp + + + 0.12 + 0.38 + diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml new file mode 100644 index 0000000000000000000000000000000000000000..32a22bb755cbba0665c5ac5848facd5e9a830481 --- /dev/null +++ b/core/res/res/values-watch-v36/styles_material.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 95753ebb093ba3e64953d53bc3df2adfa379920d..e7ebb6cff1c53fcb519af196a87afe529b0640d6 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -2129,11 +2129,6 @@ "有来电和通知时会振动" "有来电和通知时会静音" "系统变更" - "勿扰" - "新功能:勿扰模式目前可隐藏通知" - "点按即可了解详情以及进行更改。" - "“勿扰”设置有变更" - "点按即可查看屏蔽内容。" "查看通知设置" "从 Android 13 开始,您安装的应用需要您授予相应权限才能发送通知。点按即可为现有应用更改此权限。" "稍后提醒我" diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index df04ec8e8387be4e10922d9d6f69e12410ca6bb9..8f7e17c749fa3cd99d35d601477edce8b031566e 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -2129,11 +2129,6 @@ "有來電和通知時會震動" "有來電和通知時會靜音" "系統變更" - "請勿騷擾" - "新通知:「請勿騷擾」模式目前隱藏通知" - "輕按即可瞭解詳情和作出變更。" - "請勿騷擾已變更" - "輕按即可查看封鎖內容。" "查看通知設定" "由 Android 13 開始,你安裝的應用程式須獲得授權才能傳送通知。輕按即可變更現有應用程式的這項權限。" "稍後提醒我" diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index f0351ceb5591eeab37118d35e9d98e5ea45eb573..76c581c8f73d6fafc2f3ea5c8c5f4fe0fa373a34 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -2129,11 +2129,6 @@ "有來電和通知時會震動" "有來電和通知時會靜音" "系統變更" - "零打擾" - "新功能:「零打擾」模式現在可以隱藏通知" - "輕觸即可瞭解詳情及進行變更。" - "「零打擾」設定已變更" - "輕觸即可查看遭封鎖的項目。" "查看通知設定" "從 Android 13 開始,你安裝的應用程式必須獲得授權,才能傳送通知。輕觸即可為現有應用程式變更這項權限。" "稍後提醒我" diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index f051a456a9bb34da041e6c021ab9bc9a55b7a596..5aab13b2f087337435dc3146d26f611a8ec596d4 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -2129,11 +2129,6 @@ "Amakholi nezaziso zizodlidliza" "Amakholi nezaziso zizothuliswa" "Ushintsho lwesistimu" - "Ungaphazamisi" - "Ukungaphazamisi kufihle izaziso" - "Thepha ukuze ufunde kabanzi futhi ushintshe." - "Ukungaphazamisi kushintshile" - "Thepha ukuze uhlole ukuthi yini evinjelwe." "Buyekeza amasethingi wesaziso" "Kusukela ku-Android 13, ama-app owafakayo adinga imvume yakho yokuthumela izaziso. Thepha ukuze ushintshe le mvume yama-app akhona kakade." "Ngikhumbuze ngesinye isikhathi" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 440219de9561c9eda5a78d27e32d08797863f27f..02f9f3c5f0dbeb24222f40394e0fd114fbc42fda 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -7720,6 +7720,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index b90ee2b7e57f989d5c1602101e6ed3b940ea5186..8a2d767e9ad15806682c3d196029558e07dcb975 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -6918,6 +6918,14 @@ entering the corresponding modes --> + + 100 + + + 50 + diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 6683dc044c9a56034995cb8f37589fa99cab0467..b92aa2f355ed6f7161bd5e1c702b4507aab6a5b5 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -304,6 +304,9 @@ 8dp + + 2dp + 56dp diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c50c336f8fd866430780d0fd29013545727bae88..4f63fac96a8d274a4967414dcf4c1b0effaeac5d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -224,6 +224,8 @@ + + @@ -1580,6 +1582,8 @@ + + @@ -2390,6 +2394,7 @@ + @@ -3205,6 +3210,7 @@ + @@ -4076,6 +4082,7 @@ + @@ -5235,6 +5242,9 @@ + + + diff --git a/core/tests/coretests/src/android/app/ActivityManagerTest.java b/core/tests/coretests/src/android/app/ActivityManagerTest.java index d850f86070bc3c66b12833e2a569c7892be795a0..85ff8463725e4d2928d8c2175b556ac35218c0e9 100644 --- a/core/tests/coretests/src/android/app/ActivityManagerTest.java +++ b/core/tests/coretests/src/android/app/ActivityManagerTest.java @@ -60,7 +60,6 @@ public class ActivityManagerTest { public void testProcState() throws Exception { // For the moment mostly want to confirm we don't crash assertNotNull(ActivityManager.procStateToString(PROCESS_STATE_SERVICE)); - assertNotNull(ActivityManager.processStateAmToProto(PROCESS_STATE_SERVICE)); assertTrue(ActivityManager.isProcStateBackground(PROCESS_STATE_SERVICE)); assertFalse(ActivityManager.isProcStateCached(PROCESS_STATE_SERVICE)); assertFalse(ActivityManager.isForegroundService(PROCESS_STATE_SERVICE)); diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java index b972882e68e63a6316868ab484257a191ad55d16..cd524214e6af554d681b58cbb4d1b0fce52bb92a 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java @@ -111,12 +111,6 @@ public class ActivityManagerTest extends AndroidTestCase { assertEquals(config.reqKeyboardType, vconfig.keyboard); assertEquals(config.reqTouchScreen, vconfig.touchscreen); assertEquals(config.reqNavigation, vconfig.navigation); - if (vconfig.navigation == Configuration.NAVIGATION_NONAV) { - assertNotNull(config.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV); - } - if (vconfig.keyboard != Configuration.KEYBOARD_UNDEFINED) { - assertNotNull(config.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD); - } } @SmallTest diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java index a28b2f66aaa89483d91887a6bc44572ca046b34e..51e79e7ac4e13065065aac13661580e235c3166f 100644 --- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java +++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java @@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import android.app.assist.AssistStructure.ViewNode; import android.app.assist.AssistStructure.ViewNodeBuilder; @@ -37,6 +38,7 @@ import android.os.Bundle; import android.os.LocaleList; import android.os.OutcomeReceiver; import android.os.Parcel; +import android.os.PooledStringWriter; import android.os.SystemClock; import android.text.InputFilter; import android.util.Log; @@ -355,6 +357,18 @@ public class AssistStructureTest { } + @Test + public void testParcelTransferWriter_writeNull() { + AssistStructure structure = new AssistStructure(mActivity, FOR_AUTOFILL, NO_FLAGS); + Parcel parcel = Parcel.obtain(); + AssistStructure.ParcelTransferWriter writer = + new AssistStructure.ParcelTransferWriter(structure, parcel); + writer.writeView(null, parcel, new PooledStringWriter(parcel), 0); + + // No throw any exception. + assertTrue(true); + } + private EditText newSmallView() { EditText view = new EditText(mContext); view.setText("I AM GROOT"); diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java index b16c237252f64ef7c8bbf3bfb753d8ba43260be5..be8ecbe667916510ad1c9747833580c0bbe54df9 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java @@ -470,6 +470,7 @@ public class ResourcesManagerTest { @Test @SmallTest + @DisabledOnRavenwood(blockedBy = ResourcesManager.class) public void testResourceConfigurationAppliedWhenOverrideDoesNotExist() { final int width = 240; final int height = 360; diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index ba6f62c6ed198108f06cba3bfcacbfcad82f0d98..d7f6a29d7c8655a175346d0181707e72a26358a2 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -100,14 +100,14 @@ public class InsetsAnimationControlImplTest { topConsumer.setControl( new InsetsSourceControl(ID_STATUS_BAR, WindowInsets.Type.statusBars(), mStatusLeash, true, new Point(0, 0), Insets.of(0, 100, 0, 0)), - new int[1], new int[1]); + new int[1], new int[1], new int[1]); InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(ID_NAVIGATION_BAR, WindowInsets.Type.navigationBars(), mInsetsState, mMockController); navConsumer.setControl( new InsetsSourceControl(ID_NAVIGATION_BAR, WindowInsets.Type.navigationBars(), mNavLeash, true, new Point(400, 0), Insets.of(0, 0, 100, 0)), - new int[1], new int[1]); + new int[1], new int[1], new int[1]); mMockController.setRequestedVisibleTypes(0, WindowInsets.Type.navigationBars()); navConsumer.applyLocalVisibilityOverride(); diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index d6d45e839f2f9c0280b5faec48feb760e614a6cf..3a8f7ee3d7c8a0fb2aefdc2f48dcf1d7ca86044f 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -117,7 +117,23 @@ public class InsetsSourceConsumerTest { mConsumer.setControl( new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash, true /* initialVisible */, new Point(), Insets.NONE), - new int[1], new int[1]); + new int[1], new int[1], new int[1]); + } + + @Test + public void testSetControl_cancelAnimation() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + final InsetsSourceControl newControl = new InsetsSourceControl(mConsumer.getControl()); + + // Change the side of the insets hint. + newControl.setInsetsHint(Insets.of(0, 0, 0, 100)); + + int[] cancelTypes = {0}; + mConsumer.setControl(newControl, new int[1], new int[1], cancelTypes); + + assertEquals(statusBars(), cancelTypes[0]); + }); + } @Test @@ -180,7 +196,7 @@ public class InsetsSourceConsumerTest { @Test public void testRestore() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { - mConsumer.setControl(null, new int[1], new int[1]); + mConsumer.setControl(null, new int[1], new int[1], new int[1]); mSurfaceParamsApplied = false; mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars()); assertFalse(mSurfaceParamsApplied); @@ -188,7 +204,7 @@ public class InsetsSourceConsumerTest { mConsumer.setControl( new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash, true /* initialVisible */, new Point(), Insets.NONE), - new int[1], hideTypes); + new int[1], hideTypes, new int[1]); assertEquals(statusBars(), hideTypes[0]); assertFalse(mRemoveSurfaceCalled); }); @@ -198,7 +214,7 @@ public class InsetsSourceConsumerTest { public void testRestore_noAnimation() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars()); - mConsumer.setControl(null, new int[1], new int[1]); + mConsumer.setControl(null, new int[1], new int[1], new int[1]); mLeash = new SurfaceControl.Builder(mSession) .setName("testSurface") .build(); @@ -207,7 +223,7 @@ public class InsetsSourceConsumerTest { mConsumer.setControl( new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash, false /* initialVisible */, new Point(), Insets.NONE), - new int[1], hideTypes); + new int[1], hideTypes, new int[1]); assertTrue(mRemoveSurfaceCalled); assertEquals(0, hideTypes[0]); }); @@ -235,7 +251,8 @@ public class InsetsSourceConsumerTest { // Initial IME insets source control with its leash. imeConsumer.setControl(new InsetsSourceControl(ID_IME, ime(), mLeash, - false /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]); + false /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1], + new int[1]); mSurfaceParamsApplied = false; // Verify when the app requests controlling show IME animation, the IME leash @@ -244,7 +261,8 @@ public class InsetsSourceConsumerTest { null /* interpolator */, null /* cancellationSignal */, null /* listener */); assertEquals(ANIMATION_TYPE_USER, insetsController.getAnimationType(ime())); imeConsumer.setControl(new InsetsSourceControl(ID_IME, ime(), mLeash, - true /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]); + true /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1], + new int[1]); assertFalse(mSurfaceParamsApplied); }); } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java index 6e563ff4447847a4f4890d97c2f0a3c3f58dfcb8..da202b63d0f10c7452b7053f376d2b6da9f12b8f 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java @@ -46,7 +46,7 @@ public class AccessibilityNodeInfoTest { // The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest: // See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo, // and assertAccessibilityNodeInfoCleared in that class. - private static final int NUM_MARSHALLED_PROPERTIES = 44; + private static final int NUM_MARSHALLED_PROPERTIES = 45; /** * The number of properties that are purposely not marshalled diff --git a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt index b7bccd46cede9ea9a3c36fe9318f4dcd85687949..381b566018c7c36f65e127bd5941cf3aea7374b9 100644 --- a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt +++ b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt @@ -46,45 +46,43 @@ class BackTouchTrackerTest { val linearTracker = linearTouchTracker() linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT) var touchX = 10f - val velocityX = 0f - val velocityY = 0f // Pre-commit - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE) // Post-commit touchX += 100f linearTracker.setTriggerBack(true) - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE) // Cancel touchX -= 10f linearTracker.setTriggerBack(false) - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress(0f) // Cancel more touchX -= 10f - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress(0f) // Restarted, but pre-commit val restartX = touchX touchX += 10f - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - restartX) / MAX_DISTANCE) // continue restart within pre-commit touchX += 10f - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - restartX) / MAX_DISTANCE) // Restarted, post-commit touchX += 10f linearTracker.setTriggerBack(true) - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE) } @@ -93,46 +91,44 @@ class BackTouchTrackerTest { val linearTracker = linearTouchTracker() linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT) var touchX = INITIAL_X_RIGHT_EDGE - 10 // Fake right edge - val velocityX = 0f - val velocityY = 0f val target = MAX_DISTANCE // Pre-commit - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target) // Post-commit touchX -= 100f linearTracker.setTriggerBack(true) - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target) // Cancel touchX += 10f linearTracker.setTriggerBack(false) - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress(0f) // Cancel more touchX += 10f - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress(0f) // Restarted, but pre-commit val restartX = touchX touchX -= 10f - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((restartX - touchX) / target) // continue restart within pre-commit touchX -= 10f - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((restartX - touchX) / target) // Restarted, post-commit touchX -= 10f linearTracker.setTriggerBack(true) - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target) } @@ -141,28 +137,26 @@ class BackTouchTrackerTest { val nonLinearTracker = nonLinearTouchTracker() nonLinearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT) var touchX = 10f - val velocityX = 0f - val velocityY = 0f val linearTarget = LINEAR_DISTANCE + (MAX_DISTANCE - LINEAR_DISTANCE) * NON_LINEAR_FACTOR // Pre-commit: linear progress - nonLinearTracker.update(touchX, 0f, velocityX, velocityY) + nonLinearTracker.update(touchX, 0f) nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget) // Post-commit: still linear progress touchX += 100f nonLinearTracker.setTriggerBack(true) - nonLinearTracker.update(touchX, 0f, velocityX, velocityY) + nonLinearTracker.update(touchX, 0f) nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget) // still linear progress touchX = INITIAL_X_LEFT_EDGE + LINEAR_DISTANCE - nonLinearTracker.update(touchX, 0f, velocityX, velocityY) + nonLinearTracker.update(touchX, 0f) nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget) // non linear progress touchX += 10 - nonLinearTracker.update(touchX, 0f, velocityX, velocityY) + nonLinearTracker.update(touchX, 0f) val nonLinearTouch = (touchX - INITIAL_X_LEFT_EDGE) - LINEAR_DISTANCE val nonLinearProgress = nonLinearTouch / NON_LINEAR_DISTANCE val nonLinearTarget = MathUtils.lerp(linearTarget, MAX_DISTANCE, nonLinearProgress) @@ -178,7 +172,7 @@ class BackTouchTrackerTest { val velocityY = 0f // assert that progress is increased when increasing touchX - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE) // assert that progress is reset to 0 when start location is updated @@ -187,13 +181,13 @@ class BackTouchTrackerTest { // assert that progress remains 0 when touchX is decreased touchX -= 50 - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress(0f) // assert that progress uses new minimal touchX for progress calculation val newInitialTouchX = touchX touchX += 100 - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE) // assert the same for triggerBack==true @@ -207,11 +201,8 @@ class BackTouchTrackerTest { linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT) var touchX = INITIAL_X_RIGHT_EDGE - 100f - val velocityX = 0f - val velocityY = 0f - // assert that progress is increased when decreasing touchX - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / MAX_DISTANCE) // assert that progress is reset to 0 when start location is updated @@ -220,13 +211,13 @@ class BackTouchTrackerTest { // assert that progress remains 0 when touchX is increased touchX += 50 - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress(0f) // assert that progress uses new maximal touchX for progress calculation val newInitialTouchX = touchX touchX -= 100 - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE) // assert the same for triggerBack==true diff --git a/core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java b/core/tests/coretests/src/android/window/DesktopModeFlagsTest.java similarity index 88% rename from core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java rename to core/tests/coretests/src/android/window/DesktopModeFlagsTest.java index a311d0d81010349cad01cfee613964b4301dca2a..b28e2b04b342b978261b0c55ab6f544d8b055100 100644 --- a/core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java +++ b/core/tests/coretests/src/android/window/DesktopModeFlagsTest.java @@ -14,13 +14,14 @@ * limitations under the License. */ -package android.window.flags; +package android.window; -import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE; -import static android.window.flags.DesktopModeFlags.ToggleOverride.OVERRIDE_OFF; -import static android.window.flags.DesktopModeFlags.ToggleOverride.OVERRIDE_ON; -import static android.window.flags.DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET; -import static android.window.flags.DesktopModeFlags.ToggleOverride.fromSetting; +import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE; +import static android.window.DesktopModeFlags.ToggleOverride.OVERRIDE_OFF; +import static android.window.DesktopModeFlags.ToggleOverride.OVERRIDE_ON; +import static android.window.DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET; +import static android.window.DesktopModeFlags.ToggleOverride.fromSetting; +import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS; import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE; import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS; @@ -40,6 +41,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -48,7 +50,7 @@ import org.junit.runner.RunWith; import java.lang.reflect.Field; /** - * Test class for {@link DesktopModeFlags} + * Test class for {@link android.window.DesktopModeFlags} * * Build/Install/Run: * atest FrameworksCoreTests:DesktopModeFlagsTest @@ -68,8 +70,12 @@ public class DesktopModeFlagsTest { private static final int OVERRIDE_UNSET_SETTING = -1; @Before - public void setUp() throws Exception { + public void setUp() { mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + } + + @After + public void tearDown() throws Exception { resetCache(); } @@ -203,7 +209,7 @@ public class DesktopModeFlagsTest { setOverride(OVERRIDE_UNSET_SETTING); // For unset overrides, follow flag - assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue(); + assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue(); } @Test @@ -212,7 +218,7 @@ public class DesktopModeFlagsTest { public void isTrue_dwFlagOn_overrideUnset_featureFlagOff_returnsFalse() { setOverride(OVERRIDE_UNSET_SETTING); // For unset overrides, follow flag - assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse(); + assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse(); } @Test @@ -225,7 +231,7 @@ public class DesktopModeFlagsTest { setOverride(OVERRIDE_ON_SETTING); // When toggle override matches its default state (dw flag), don't override flags - assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue(); + assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue(); } @Test @@ -235,7 +241,7 @@ public class DesktopModeFlagsTest { setOverride(OVERRIDE_ON_SETTING); // When toggle override matches its default state (dw flag), don't override flags - assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse(); + assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse(); } @Test @@ -248,7 +254,7 @@ public class DesktopModeFlagsTest { setOverride(OVERRIDE_OFF_SETTING); // Follow override if they exist, and is not equal to default toggle state (dw flag) - assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue(); + assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue(); } @Test @@ -258,7 +264,7 @@ public class DesktopModeFlagsTest { setOverride(OVERRIDE_OFF_SETTING); // Follow override if they exist, and is not equal to default toggle state (dw flag) - assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse(); + assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse(); } @Test @@ -271,7 +277,7 @@ public class DesktopModeFlagsTest { setOverride(OVERRIDE_UNSET_SETTING); // For unset overrides, follow flag - assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue(); + assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue(); } @Test @@ -284,7 +290,7 @@ public class DesktopModeFlagsTest { setOverride(OVERRIDE_UNSET_SETTING); // For unset overrides, follow flag - assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse(); + assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse(); } @Test @@ -297,7 +303,7 @@ public class DesktopModeFlagsTest { setOverride(OVERRIDE_ON_SETTING); // Follow override if they exist, and is not equal to default toggle state (dw flag) - assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue(); + assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue(); } @Test @@ -310,7 +316,7 @@ public class DesktopModeFlagsTest { setOverride(OVERRIDE_ON_SETTING); // Follow override if they exist, and is not equal to default toggle state (dw flag) - assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse(); + assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse(); } @Test @@ -323,7 +329,7 @@ public class DesktopModeFlagsTest { setOverride(OVERRIDE_OFF_SETTING); // When toggle override matches its default state (dw flag), don't override flags - assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue(); + assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue(); } @Test @@ -336,7 +342,7 @@ public class DesktopModeFlagsTest { setOverride(OVERRIDE_OFF_SETTING); // When toggle override matches its default state (dw flag), don't override flags - assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse(); + assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse(); } @Test @@ -375,5 +381,6 @@ public class DesktopModeFlagsTest { "sCachedToggleOverride"); cachedToggleOverride.setAccessible(true); cachedToggleOverride.set(null, null); + setOverride(OVERRIDE_UNSET_SETTING); } } diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index 0a4c5e6eb91a1428bfc1abb2d8b3e149828e1c37..b001cc2929e3d55f384836d8a30059b25d09a8c4 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -586,13 +586,13 @@ public class WindowOnBackInvokedDispatcherTest { @Test(expected = IllegalArgumentException.class) @RequiresFlagsDisabled(FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER) - public void testNoUiCallback_registrationFailsWithoutFlaggedApiEnabled() { + public void testObserverCallback_registrationFailsWithoutFlaggedApiEnabled() { mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback2); } @Test @RequiresFlagsEnabled(FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER) - public void testNoUiCallback_invokedWithSystemCallback() throws RemoteException { + public void testObserverCallback_invokedWithSystemCallback() throws RemoteException { mDispatcher.registerSystemOnBackInvokedCallback(mCallback1); mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback2); @@ -607,7 +607,7 @@ public class WindowOnBackInvokedDispatcherTest { callbackInfo.getCallback().onBackProgressed(mBackEvent); waitForIdle(); - verify(mCallback1).onBackProgressed(any()); + verify(mCallback1, atLeast(1)).onBackProgressed(any()); verify(mCallback2, never()).onBackProgressed(any()); callbackInfo.getCallback().onBackCancelled(); @@ -625,7 +625,7 @@ public class WindowOnBackInvokedDispatcherTest { @Test @RequiresFlagsEnabled(FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER) - public void testNoUiCallback_notInvokedWithNonSystemCallback() throws RemoteException { + public void testObserverCallback_notInvokedWithNonSystemCallback() throws RemoteException { mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1); mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback2); @@ -640,7 +640,7 @@ public class WindowOnBackInvokedDispatcherTest { callbackInfo.getCallback().onBackProgressed(mBackEvent); waitForIdle(); - verify(mCallback1).onBackProgressed(any()); + verify(mCallback1, atLeast(1)).onBackProgressed(any()); verify(mCallback2, never()).onBackProgressed(any()); callbackInfo.getCallback().onBackCancelled(); @@ -658,7 +658,7 @@ public class WindowOnBackInvokedDispatcherTest { @Test @RequiresFlagsEnabled(FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER) - public void testNoUiCallback_reregistrations() { + public void testObserverCallback_reregistrations() { mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback1); assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 1); assertEquals(mCallback1, mDispatcher.mSystemNavigationObserverCallback); @@ -685,8 +685,6 @@ public class WindowOnBackInvokedDispatcherTest { /* touchX = */ 0, /* touchY = */ 0, /* progress = */ progress, - /* velocityX = */ 0, - /* velocityY = */ 0, /* triggerBack = */ false, /* swipeEdge = */ BackEvent.EDGE_LEFT, /* departingAnimationTarget = */ null); diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityServiceWarningTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityServiceWarningTest.java index 362eeeacfc1ec05c8e2cb4cc9b1443853c9764f8..8e906fda89f04fdb5951cd7b119fce5d97847043 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityServiceWarningTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityServiceWarningTest.java @@ -25,6 +25,8 @@ import android.accessibilityservice.AccessibilityServiceInfo; import android.app.AlertDialog; import android.content.Context; import android.os.RemoteException; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.testing.AndroidTestingRunner; @@ -33,6 +35,7 @@ import android.view.InputDevice; import android.view.MotionEvent; import android.view.View; import android.view.Window; +import android.view.accessibility.Flags; import android.widget.TextView; import androidx.test.platform.app.InstrumentationRegistry; @@ -89,7 +92,19 @@ public class AccessibilityServiceWarningTest { } @Test - public void createAccessibilityServiceWarningDialog_hasExpectedWindowParams() { + @RequiresFlagsDisabled(Flags.FLAG_WARNING_USE_DEFAULT_DIALOG_TYPE) + public void createAccessibilityServiceWarningDialog_hasExpectedWindowParams_isSystemDialog() { + createAccessibilityServiceWarningDialog_hasExpectedWindowParams(true); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_WARNING_USE_DEFAULT_DIALOG_TYPE) + public void createAccessibilityServiceWarningDialog_hasExpectedWindowParams_notSystemDialog() { + createAccessibilityServiceWarningDialog_hasExpectedWindowParams(false); + } + + private void createAccessibilityServiceWarningDialog_hasExpectedWindowParams( + boolean expectSystemDialog) { final AlertDialog dialog = AccessibilityServiceWarning.createAccessibilityServiceWarningDialog( mContext, @@ -101,7 +116,11 @@ public class AccessibilityServiceWarningTest { expect.that(dialogWindow.getAttributes().privateFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS).isEqualTo( SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); - expect.that(dialogWindow.getAttributes().type).isEqualTo(TYPE_SYSTEM_DIALOG); + if (expectSystemDialog) { + expect.that(dialogWindow.getAttributes().type).isEqualTo(TYPE_SYSTEM_DIALOG); + } else { + expect.that(dialogWindow.getAttributes().type).isNotEqualTo(TYPE_SYSTEM_DIALOG); + } } @Test diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6419c1e07f2e07f35ea126dfab7047b27bb3fe3d --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Notification.ProgressStyle; +import android.graphics.Color; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.internal.widget.NotificationProgressDrawable.Part; +import com.android.internal.widget.NotificationProgressDrawable.Point; +import com.android.internal.widget.NotificationProgressDrawable.Segment; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class NotificationProgressBarTest { + + @Test(expected = IllegalArgumentException.class) + public void processAndConvertToDrawableParts_segmentsIsEmpty() { + List segments = new ArrayList<>(); + List points = new ArrayList<>(); + int progress = 50; + boolean isStyledByProgress = true; + + NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + isStyledByProgress); + } + + @Test(expected = IllegalArgumentException.class) + public void processAndConvertToDrawableParts_segmentLengthIsNegative() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(-50)); + List points = new ArrayList<>(); + int progress = 50; + boolean isStyledByProgress = true; + + NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + isStyledByProgress); + } + + @Test(expected = IllegalArgumentException.class) + public void processAndConvertToDrawableParts_segmentLengthIsZero() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(0)); + List points = new ArrayList<>(); + int progress = 50; + boolean isStyledByProgress = true; + + NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + isStyledByProgress); + } + + @Test(expected = IllegalArgumentException.class) + public void processAndConvertToDrawableParts_progressIsNegative() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(100)); + List points = new ArrayList<>(); + int progress = -50; + boolean isStyledByProgress = true; + + NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + isStyledByProgress); + } + + @Test + public void processAndConvertToDrawableParts_progressIsZero() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(100).setColor(Color.RED)); + List points = new ArrayList<>(); + int progress = 0; + boolean isStyledByProgress = true; + + List parts = NotificationProgressBar.processAndConvertToDrawableParts( + segments, points, progress, isStyledByProgress); + + int fadedRed = 0x7FFF0000; + List expected = new ArrayList<>(List.of(new Segment(1f, fadedRed, true))); + + assertThat(parts).isEqualTo(expected); + } + + @Test + public void processAndConvertToDrawableParts_progressAtMax() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(100).setColor(Color.RED)); + List points = new ArrayList<>(); + int progress = 100; + boolean isStyledByProgress = true; + + List parts = NotificationProgressBar.processAndConvertToDrawableParts( + segments, points, progress, isStyledByProgress); + + List expected = new ArrayList<>(List.of(new Segment(1f, Color.RED))); + + assertThat(parts).isEqualTo(expected); + } + + @Test(expected = IllegalArgumentException.class) + public void processAndConvertToDrawableParts_progressAboveMax() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(100)); + List points = new ArrayList<>(); + int progress = 150; + boolean isStyledByProgress = true; + + NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + isStyledByProgress); + } + + @Test(expected = IllegalArgumentException.class) + public void processAndConvertToDrawableParts_pointPositionIsNegative() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(100)); + List points = new ArrayList<>(); + points.add(new ProgressStyle.Point(-50).setColor(Color.RED)); + int progress = 50; + boolean isStyledByProgress = true; + + NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + isStyledByProgress); + } + + @Test(expected = IllegalArgumentException.class) + public void processAndConvertToDrawableParts_pointPositionIsZero() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(100)); + List points = new ArrayList<>(); + points.add(new ProgressStyle.Point(0).setColor(Color.RED)); + int progress = 50; + boolean isStyledByProgress = true; + + NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + isStyledByProgress); + } + + @Test(expected = IllegalArgumentException.class) + public void processAndConvertToDrawableParts_pointPositionAtMax() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(100)); + List points = new ArrayList<>(); + points.add(new ProgressStyle.Point(100).setColor(Color.RED)); + int progress = 50; + boolean isStyledByProgress = true; + + NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + isStyledByProgress); + } + + @Test(expected = IllegalArgumentException.class) + public void processAndConvertToDrawableParts_pointPositionAboveMax() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(100)); + List points = new ArrayList<>(); + points.add(new ProgressStyle.Point(150).setColor(Color.RED)); + int progress = 50; + boolean isStyledByProgress = true; + + NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + isStyledByProgress); + } + + @Test + public void processAndConvertToDrawableParts_multipleSegmentsWithoutPoints() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(50).setColor(Color.RED)); + segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN)); + List points = new ArrayList<>(); + int progress = 60; + boolean isStyledByProgress = true; + + List parts = NotificationProgressBar.processAndConvertToDrawableParts( + segments, points, progress, isStyledByProgress); + + // Colors with 50% opacity + int fadedGreen = 0x7F00FF00; + + List expected = new ArrayList<>(List.of( + new Segment(0.50f, Color.RED), + new Segment(0.10f, Color.GREEN), + new Segment(0.40f, fadedGreen, true))); + + assertThat(parts).isEqualTo(expected); + } + + @Test + public void processAndConvertToDrawableParts_singleSegmentWithPoints() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(100).setColor(Color.BLUE)); + List points = new ArrayList<>(); + points.add(new ProgressStyle.Point(15).setColor(Color.RED)); + points.add(new ProgressStyle.Point(25).setColor(Color.BLUE)); + points.add(new ProgressStyle.Point(60).setColor(Color.BLUE)); + points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW)); + int progress = 60; + boolean isStyledByProgress = true; + + // Colors with 50% opacity + int fadedBlue = 0x7F0000FF; + int fadedYellow = 0x7FFFFF00; + + List expected = new ArrayList<>(List.of( + new Segment(0.15f, Color.BLUE), + new Point(null, Color.RED), + new Segment(0.10f, Color.BLUE), + new Point(null, Color.BLUE), + new Segment(0.35f, Color.BLUE), + new Point(null, Color.BLUE), + new Segment(0.15f, fadedBlue, true), + new Point(null, fadedYellow, true), + new Segment(0.25f, fadedBlue, true))); + + List parts = NotificationProgressBar.processAndConvertToDrawableParts( + segments, points, progress, isStyledByProgress); + + assertThat(parts).isEqualTo(expected); + } + + @Test + public void processAndConvertToDrawableParts_multipleSegmentsWithPoints() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(50).setColor(Color.RED)); + segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN)); + List points = new ArrayList<>(); + points.add(new ProgressStyle.Point(15).setColor(Color.RED)); + points.add(new ProgressStyle.Point(25).setColor(Color.BLUE)); + points.add(new ProgressStyle.Point(60).setColor(Color.BLUE)); + points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW)); + int progress = 60; + boolean isStyledByProgress = true; + + List parts = NotificationProgressBar.processAndConvertToDrawableParts( + segments, points, progress, isStyledByProgress); + + // Colors with 50% opacity + int fadedGreen = 0x7F00FF00; + int fadedBlue = 0x7F0000FF; + int fadedYellow = 0x7FFFFF00; + + List expected = new ArrayList<>(List.of( + new Segment(0.15f, Color.RED), + new Point(null, Color.RED), + new Segment(0.10f, Color.RED), + new Point(null, Color.BLUE), + new Segment(0.25f, Color.RED), + new Segment(0.10f, Color.GREEN), + new Point(null, Color.BLUE), + new Segment(0.15f, fadedGreen, true), + new Point(null, fadedYellow, true), + new Segment(0.25f, fadedGreen, true))); + + assertThat(parts).isEqualTo(expected); + } + + @Test + public void processAndConvertToDrawableParts_multipleSegmentsWithPoints_notStyledByProgress() { + List segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(50).setColor(Color.RED)); + segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN)); + List points = new ArrayList<>(); + points.add(new ProgressStyle.Point(15).setColor(Color.RED)); + points.add(new ProgressStyle.Point(25).setColor(Color.BLUE)); + points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW)); + int progress = 60; + boolean isStyledByProgress = false; + + List parts = NotificationProgressBar.processAndConvertToDrawableParts( + segments, points, progress, isStyledByProgress); + + List expected = new ArrayList<>(List.of( + new Segment(0.15f, Color.RED), + new Point(null, Color.RED), + new Segment(0.10f, Color.RED), + new Point(null, Color.BLUE), + new Segment(0.25f, Color.RED), + new Segment(0.25f, Color.GREEN), + new Point(null, Color.YELLOW), + new Segment(0.25f, Color.GREEN))); + + assertThat(parts).isEqualTo(expected); + } +} diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressModelTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressModelTest.java new file mode 100644 index 0000000000000000000000000000000000000000..962399e48fb81b4324a8bbd9eeb888e7aae95d1c --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressModelTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Flags; +import android.app.Notification; +import android.graphics.Color; +import android.os.Bundle; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.filters.SmallTest; + +import org.junit.Rule; +import org.junit.Test; + +import java.util.List; + +@SmallTest +@EnableFlags(Flags.FLAG_API_RICH_ONGOING) +public class NotificationProgressModelTest { + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + @Test(expected = IllegalArgumentException.class) + public void throw_exception_on_transparent_indeterminate_color() { + new NotificationProgressModel(Color.TRANSPARENT); + } + + @Test(expected = IllegalArgumentException.class) + public void throw_exception_on_empty_segments() { + new NotificationProgressModel(List.of(), + List.of(), + 10, + false); + } + + @Test(expected = IllegalArgumentException.class) + public void throw_exception_on_negative_progress() { + new NotificationProgressModel( + List.of(new Notification.ProgressStyle.Segment(50).setColor(Color.YELLOW)), + List.of(), + -1, + false); + } + + @Test + public void save_and_restore_indeterminate_progress_model() { + // GIVEN + final NotificationProgressModel savedModel = new NotificationProgressModel(Color.RED); + final Bundle bundle = savedModel.toBundle(); + + // WHEN + final NotificationProgressModel restoredModel = + NotificationProgressModel.fromBundle(bundle); + + // THEN + assertThat(restoredModel.getIndeterminateColor()).isEqualTo(Color.RED); + assertThat(restoredModel.isIndeterminate()).isTrue(); + assertThat(restoredModel.getProgress()).isEqualTo(-1); + assertThat(restoredModel.getSegments()).isEmpty(); + assertThat(restoredModel.getPoints()).isEmpty(); + assertThat(restoredModel.isStyledByProgress()).isFalse(); + } + + @Test + public void save_and_restore_non_indeterminate_progress_model() { + // GIVEN + final List segments = List.of( + new Notification.ProgressStyle.Segment(50).setColor(Color.YELLOW), + new Notification.ProgressStyle.Segment(50).setColor(Color.LTGRAY)); + final List points = List.of( + new Notification.ProgressStyle.Point(0).setColor(Color.RED), + new Notification.ProgressStyle.Point(20).setColor(Color.BLUE)); + final NotificationProgressModel savedModel = new NotificationProgressModel(segments, + points, + 100, + true); + + final Bundle bundle = savedModel.toBundle(); + + // WHEN + final NotificationProgressModel restoredModel = + NotificationProgressModel.fromBundle(bundle); + + // THEN + assertThat(restoredModel.isIndeterminate()).isFalse(); + assertThat(restoredModel.getSegments()).isEqualTo(segments); + assertThat(restoredModel.getPoints()).isEqualTo(points); + assertThat(restoredModel.getProgress()).isEqualTo(100); + assertThat(restoredModel.isStyledByProgress()).isTrue(); + assertThat(restoredModel.getIndeterminateColor()).isEqualTo(-1); + } +} diff --git a/core/tests/devicestatetests/Android.bp b/core/tests/devicestatetests/Android.bp index a3303c6ca6d7cf45510a2acaee3affcec5e1b157..e573a514c698dceabca92c2bce8df85324f3ed02 100644 --- a/core/tests/devicestatetests/Android.bp +++ b/core/tests/devicestatetests/Android.bp @@ -26,11 +26,14 @@ android_test { // Include all test java files srcs: ["src/**/*.java"], static_libs: [ + "androidx.test.ext.junit", "androidx.test.rules", + "flag-junit", "frameworks-base-testutils", "mockito-target-minus-junit4", + "platform-parametric-runner-lib", "platform-test-annotations", - "testng", + "truth", ], libs: ["android.test.runner.stubs.system"], platform_apis: true, diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java index cf7c5491f7870b885b86eec24e56d04b92f9d3ed..1b78433a5bda843471d6194013ad01e21c85fae0 100644 --- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java +++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java @@ -22,21 +22,15 @@ import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWAR import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN; import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; import android.os.Parcel; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import java.util.ArrayList; import java.util.List; @@ -44,13 +38,13 @@ import java.util.Set; /** * Unit tests for {@link DeviceStateInfo}. - *

        - * Run with atest DeviceStateInfoTest. + * + *

        Build/Install/Run: + * atest FrameworksCoreDeviceStateManagerTests:DeviceStateInfoTest */ -@RunWith(JUnit4.class) @SmallTest +@RunWith(AndroidJUnit4.class) public final class DeviceStateInfoTest { - private static final DeviceState DEVICE_STATE_0 = new DeviceState( new DeviceState.Configuration.Builder(0, "STATE_0") .setSystemProperties( @@ -74,88 +68,113 @@ public final class DeviceStateInfoTest { @Test public void create() { - final ArrayList supportedStates = new ArrayList<>( - List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2)); + final ArrayList supportedStates = + new ArrayList<>(List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2)); final DeviceState baseState = DEVICE_STATE_0; final DeviceState currentState = DEVICE_STATE_2; - final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState); - assertNotNull(info.supportedStates); - assertEquals(supportedStates, info.supportedStates); - assertEquals(baseState, info.baseState); - assertEquals(currentState, info.currentState); + final DeviceStateInfo info = + new DeviceStateInfo(supportedStates, baseState, currentState); + + assertThat(info.supportedStates).containsExactlyElementsIn(supportedStates).inOrder(); + assertThat(info.baseState).isEqualTo(baseState); + assertThat(info.currentState).isEqualTo(currentState); } @Test public void equals() { - final ArrayList supportedStates = new ArrayList<>( - List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2)); + final ArrayList supportedStates = + new ArrayList<>(List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2)); final DeviceState baseState = DEVICE_STATE_0; final DeviceState currentState = DEVICE_STATE_2; final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState); - Assert.assertEquals(info, info); + final DeviceStateInfo sameInstance = info; + assertThat(info).isEqualTo(sameInstance); - final DeviceStateInfo sameInfo = new DeviceStateInfo(supportedStates, baseState, - currentState); - Assert.assertEquals(info, sameInfo); + final DeviceStateInfo sameInfo = + new DeviceStateInfo(supportedStates, baseState, currentState); + assertThat(info).isEqualTo(sameInfo); + + final DeviceStateInfo copiedInfo = new DeviceStateInfo(info); + assertThat(info).isEqualTo(copiedInfo); final DeviceStateInfo differentInfo = new DeviceStateInfo( new ArrayList<>(List.of(DEVICE_STATE_0, DEVICE_STATE_2)), baseState, currentState); - assertNotEquals(info, differentInfo); + assertThat(differentInfo).isNotEqualTo(info); + } + + @Test + public void hashCode_sameObject() { + final ArrayList supportedStates = + new ArrayList<>(List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2)); + final DeviceState baseState = DEVICE_STATE_0; + final DeviceState currentState = DEVICE_STATE_2; + final DeviceStateInfo info = + new DeviceStateInfo(supportedStates, baseState, currentState); + final DeviceStateInfo copiedInfo = new DeviceStateInfo(info); + + assertThat(info.hashCode()).isEqualTo(copiedInfo.hashCode()); } @Test public void diff_sameObject() { - final ArrayList supportedStates = new ArrayList<>( - List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2)); + final ArrayList supportedStates = + new ArrayList<>(List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2)); final DeviceState baseState = DEVICE_STATE_0; final DeviceState currentState = DEVICE_STATE_2; final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState); - assertEquals(0, info.diff(info)); + + assertThat(info.diff(info)).isEqualTo(0); } @Test public void diff_differentSupportedStates() { - final DeviceStateInfo info = new DeviceStateInfo(new ArrayList<>(List.of(DEVICE_STATE_1)), - DEVICE_STATE_0, DEVICE_STATE_0); + final DeviceStateInfo info = new DeviceStateInfo( + new ArrayList<>(List.of(DEVICE_STATE_1)), DEVICE_STATE_0, DEVICE_STATE_0); final DeviceStateInfo otherInfo = new DeviceStateInfo( new ArrayList<>(List.of(DEVICE_STATE_2)), DEVICE_STATE_0, DEVICE_STATE_0); + final int diff = info.diff(otherInfo); - assertTrue((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0); - assertFalse((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0); - assertFalse((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0); + + assertThat(diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES).isGreaterThan(0); + assertThat(diff & DeviceStateInfo.CHANGED_BASE_STATE).isEqualTo(0); + assertThat(diff & DeviceStateInfo.CHANGED_CURRENT_STATE).isEqualTo(0); } @Test public void diff_differentNonOverrideState() { - final DeviceStateInfo info = new DeviceStateInfo(new ArrayList<>(List.of(DEVICE_STATE_1)), - DEVICE_STATE_1, DEVICE_STATE_0); + final DeviceStateInfo info = new DeviceStateInfo( + new ArrayList<>(List.of(DEVICE_STATE_1)), DEVICE_STATE_1, DEVICE_STATE_0); final DeviceStateInfo otherInfo = new DeviceStateInfo( new ArrayList<>(List.of(DEVICE_STATE_1)), DEVICE_STATE_2, DEVICE_STATE_0); + final int diff = info.diff(otherInfo); - assertFalse((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0); - assertTrue((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0); - assertFalse((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0); + + assertThat(diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES).isEqualTo(0); + assertThat(diff & DeviceStateInfo.CHANGED_BASE_STATE).isGreaterThan(0); + assertThat(diff & DeviceStateInfo.CHANGED_CURRENT_STATE).isEqualTo(0); } @Test public void diff_differentState() { - final DeviceStateInfo info = new DeviceStateInfo(new ArrayList<>(List.of(DEVICE_STATE_1)), - DEVICE_STATE_0, DEVICE_STATE_1); + final DeviceStateInfo info = new DeviceStateInfo( + new ArrayList<>(List.of(DEVICE_STATE_1)), DEVICE_STATE_0, DEVICE_STATE_1); final DeviceStateInfo otherInfo = new DeviceStateInfo( new ArrayList<>(List.of(DEVICE_STATE_1)), DEVICE_STATE_0, DEVICE_STATE_2); + final int diff = info.diff(otherInfo); - assertFalse((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0); - assertFalse((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0); - assertTrue((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0); + + assertThat(diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES).isEqualTo(0); + assertThat(diff & DeviceStateInfo.CHANGED_BASE_STATE).isEqualTo(0); + assertThat(diff & DeviceStateInfo.CHANGED_CURRENT_STATE).isGreaterThan(0); } @Test public void writeToParcel() { - final ArrayList supportedStates = new ArrayList<>( - List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2)); + final ArrayList supportedStates = + new ArrayList<>(List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2)); final DeviceState nonOverrideState = DEVICE_STATE_0; final DeviceState state = DEVICE_STATE_2; final DeviceStateInfo originalInfo = @@ -166,6 +185,6 @@ public final class DeviceStateInfoTest { parcel.setDataPosition(0); final DeviceStateInfo info = DeviceStateInfo.CREATOR.createFromParcel(parcel); - assertEquals(originalInfo, info); + assertThat(info).isEqualTo(originalInfo); } } diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java index f4d363167a75e22151bd24d66dc9b436a5e40e30..e640ce5daf1102a56bed559e2b7956ac612617f3 100644 --- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java +++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java @@ -16,11 +16,12 @@ package android.hardware.devicestate; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; +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.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -30,49 +31,104 @@ import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.os.IBinder; import android.os.RemoteException; import android.os.test.FakePermissionEnforcer; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.FlagsParameterization; +import android.platform.test.flag.junit.SetFlagsRule; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.internal.util.ConcurrentUtils; +import com.android.window.flags.Flags; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + /** * Unit tests for {@link DeviceStateManagerGlobal}. - *

        - * Run with atest DeviceStateManagerGlobalTest. + * + *

        Build/Install/Run: + * atest FrameworksCoreDeviceStateManagerTests:DeviceStateManagerGlobalTest */ -@RunWith(JUnit4.class) @SmallTest +@RunWith(ParameterizedAndroidJunit4.class) public final class DeviceStateManagerGlobalTest { private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState( new DeviceState.Configuration.Builder(0 /* identifier */, "" /* name */).build()); private static final DeviceState OTHER_DEVICE_STATE = new DeviceState( new DeviceState.Configuration.Builder(1 /* identifier */, "" /* name */).build()); + @Rule + public final SetFlagsRule mSetFlagsRule; + + @Parameters(name = "{0}") + public static List getParams() { + return FlagsParameterization.allCombinationsOf(Flags.FLAG_WLINFO_ONCREATE); + } + + @NonNull private TestDeviceStateManagerService mService; + @NonNull private DeviceStateManagerGlobal mDeviceStateManagerGlobal; + public DeviceStateManagerGlobalTest(FlagsParameterization flags) { + mSetFlagsRule = new SetFlagsRule(flags); + } + @Before public void setUp() { - FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer(); + final FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer(); mService = new TestDeviceStateManagerService(permissionEnforcer); mDeviceStateManagerGlobal = new DeviceStateManagerGlobal(mService); - assertFalse(mService.mCallbacks.isEmpty()); + assertThat(mService.mCallbacks).isNotEmpty(); + } + + @Test + @DisableFlags(Flags.FLAG_WLINFO_ONCREATE) + public void create_whenWlinfoOncreateIsDisabled_receivesDeviceStateInfoFromCallback() { + final FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer(); + final TestDeviceStateManagerService service = new TestDeviceStateManagerService( + permissionEnforcer, true /* simulatePostCallback */); + final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service); + final DeviceStateCallback callback = mock(DeviceStateCallback.class); + dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); + + verify(callback, never()).onDeviceStateChanged(any()); + + // Simulate DeviceStateManagerService#registerProcess by notifying clients of current device + // state via callback. + service.notifyDeviceStateInfoChanged(); + verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE)); + } + + @Test + @EnableFlags(Flags.FLAG_WLINFO_ONCREATE) + public void create_whenWlinfoOncreateIsEnabled_returnsDeviceStateInfoFromRegistration() { + final FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer(); + final IDeviceStateManager service = new TestDeviceStateManagerService(permissionEnforcer); + final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service); + final DeviceStateCallback callback = mock(DeviceStateCallback.class); + dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); + + verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE)); } @Test public void registerCallback() { - DeviceStateCallback callback1 = mock(DeviceStateCallback.class); - DeviceStateCallback callback2 = mock(DeviceStateCallback.class); + final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); + final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, ConcurrentUtils.DIRECT_EXECUTOR); @@ -105,8 +161,8 @@ public final class DeviceStateManagerGlobalTest { reset(callback2); // Change the requested state and verify callback - DeviceStateRequest request = DeviceStateRequest.newBuilder( - DEFAULT_DEVICE_STATE.getIdentifier()).build(); + final DeviceStateRequest request = + DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE.getIdentifier()).build(); mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); verify(callback1).onDeviceStateChanged(eq(mService.getMergedState())); @@ -115,10 +171,10 @@ public final class DeviceStateManagerGlobalTest { @Test public void unregisterCallback() { - DeviceStateCallback callback = mock(DeviceStateCallback.class); + final DeviceStateCallback callback = mock(DeviceStateCallback.class); - mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, - ConcurrentUtils.DIRECT_EXECUTOR); + mDeviceStateManagerGlobal + .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); // Verify initial callbacks verify(callback).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); @@ -134,15 +190,15 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitRequest() { - DeviceStateCallback callback = mock(DeviceStateCallback.class); - mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, - ConcurrentUtils.DIRECT_EXECUTOR); + final DeviceStateCallback callback = mock(DeviceStateCallback.class); + mDeviceStateManagerGlobal + .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(mService.getBaseState())); reset(callback); - DeviceStateRequest request = DeviceStateRequest.newBuilder( - OTHER_DEVICE_STATE.getIdentifier()).build(); + final DeviceStateRequest request = + DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE.getIdentifier()).build(); mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); verify(callback).onDeviceStateChanged(eq(OTHER_DEVICE_STATE)); @@ -155,15 +211,15 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitBaseStateOverrideRequest() { - DeviceStateCallback callback = mock(DeviceStateCallback.class); - mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, - ConcurrentUtils.DIRECT_EXECUTOR); + final DeviceStateCallback callback = mock(DeviceStateCallback.class); + mDeviceStateManagerGlobal + .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(mService.getBaseState())); reset(callback); - DeviceStateRequest request = DeviceStateRequest.newBuilder( - OTHER_DEVICE_STATE.getIdentifier()).build(); + final DeviceStateRequest request = + DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE.getIdentifier()).build(); mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */, null /* callback */); @@ -177,28 +233,28 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitBaseAndEmulatedStateOverride() { - DeviceStateCallback callback = mock(DeviceStateCallback.class); - mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, - ConcurrentUtils.DIRECT_EXECUTOR); + final DeviceStateCallback callback = mock(DeviceStateCallback.class); + mDeviceStateManagerGlobal + .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(mService.getBaseState())); reset(callback); - DeviceStateRequest request = DeviceStateRequest.newBuilder( - OTHER_DEVICE_STATE.getIdentifier()).build(); + final DeviceStateRequest request = + DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE.getIdentifier()).build(); mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */, null /* callback */); verify(callback).onDeviceStateChanged(eq(OTHER_DEVICE_STATE)); - assertEquals(OTHER_DEVICE_STATE, mService.getBaseState()); + assertThat(mService.getBaseState()).isEqualTo(OTHER_DEVICE_STATE); reset(callback); - DeviceStateRequest secondRequest = DeviceStateRequest.newBuilder( - DEFAULT_DEVICE_STATE.getIdentifier()).build(); + final DeviceStateRequest secondRequest = + DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE.getIdentifier()).build(); mDeviceStateManagerGlobal.requestState(secondRequest, null, null); - assertEquals(OTHER_DEVICE_STATE, mService.getBaseState()); + assertThat(mService.getBaseState()).isEqualTo(OTHER_DEVICE_STATE); verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE)); reset(callback); @@ -214,10 +270,10 @@ public final class DeviceStateManagerGlobalTest { @Test public void verifyDeviceStateRequestCallbacksCalled() { - DeviceStateRequest.Callback callback = mock(TestDeviceStateRequestCallback.class); + final DeviceStateRequest.Callback callback = mock(TestDeviceStateRequestCallback.class); - DeviceStateRequest request = DeviceStateRequest.newBuilder( - OTHER_DEVICE_STATE.getIdentifier()).build(); + final DeviceStateRequest request = + DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE.getIdentifier()).build(); mDeviceStateManagerGlobal.requestState(request, ConcurrentUtils.DIRECT_EXECUTOR /* executor */, callback /* callback */); @@ -232,52 +288,62 @@ public final class DeviceStateManagerGlobalTest { public static class TestDeviceStateRequestCallback implements DeviceStateRequest.Callback { @Override - public void onRequestActivated(DeviceStateRequest request) { } + public void onRequestActivated(@NonNull DeviceStateRequest request) { } @Override - public void onRequestCanceled(DeviceStateRequest request) { } + public void onRequestCanceled(@NonNull DeviceStateRequest request) { } @Override - public void onRequestSuspended(DeviceStateRequest request) { } + public void onRequestSuspended(@NonNull DeviceStateRequest request) { } } private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub { - public static final class Request { - public final IBinder token; - public final int state; - public final int flags; - - private Request(IBinder token, int state, int flags) { - this.token = token; - this.state = state; - this.flags = flags; + static final class Request { + @NonNull + final IBinder mToken; + final int mState; + + private Request(@NonNull IBinder token, int state) { + this.mToken = token; + this.mState = state; } } - private List mSupportedDeviceStates = List.of(DEFAULT_DEVICE_STATE, - OTHER_DEVICE_STATE); - + @NonNull + private List mSupportedDeviceStates = + List.of(DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE); + @NonNull private DeviceState mBaseState = DEFAULT_DEVICE_STATE; + @Nullable private Request mRequest; + @Nullable private Request mBaseStateRequest; + private final boolean mSimulatePostCallback; private final Set mCallbacks = new HashSet<>(); - TestDeviceStateManagerService(FakePermissionEnforcer enforcer) { + TestDeviceStateManagerService(@NonNull FakePermissionEnforcer enforcer) { + this(enforcer, false /* simulatePostCallback */); + } + + TestDeviceStateManagerService(@NonNull FakePermissionEnforcer enforcer, + boolean simulatePostCallback) { super(enforcer); + mSimulatePostCallback = simulatePostCallback; } + @NonNull private DeviceStateInfo getInfo() { final int mergedBaseState = mBaseStateRequest == null - ? mBaseState.getIdentifier() : mBaseStateRequest.state; - final int mergedState = mRequest == null - ? mergedBaseState : mRequest.state; + ? mBaseState.getIdentifier() : mBaseStateRequest.mState; + final int mergedState = mRequest == null ? mergedBaseState : mRequest.mState; + final ArrayList supportedStates = new ArrayList<>(mSupportedDeviceStates); final DeviceState baseState = new DeviceState( new DeviceState.Configuration.Builder(mergedBaseState, "" /* name */).build()); final DeviceState state = new DeviceState( new DeviceState.Configuration.Builder(mergedState, "" /* name */).build()); - return new DeviceStateInfo(new ArrayList<>(mSupportedDeviceStates), baseState, state); + return new DeviceStateInfo(supportedStates, baseState, state); } private void notifyDeviceStateInfoChanged() { @@ -291,38 +357,47 @@ public final class DeviceStateManagerGlobalTest { } } + @NonNull @Override public DeviceStateInfo getDeviceStateInfo() { return getInfo(); } + @Nullable @Override - public void registerCallback(IDeviceStateManagerCallback callback) { + public DeviceStateInfo registerCallback(IDeviceStateManagerCallback callback) { if (mCallbacks.contains(callback)) { throw new SecurityException("Callback is already registered."); } mCallbacks.add(callback); - try { - callback.onDeviceStateInfoChanged(getInfo()); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); + if (Flags.wlinfoOncreate()) { + return getInfo(); + } + + if (!mSimulatePostCallback) { + try { + callback.onDeviceStateInfoChanged(getInfo()); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } + return null; } @Override - public void requestState(IBinder token, int state, int flags) { + public void requestState(@NonNull IBinder token, int state, int unusedFlags) { if (mRequest != null) { for (IDeviceStateManagerCallback callback : mCallbacks) { try { - callback.onRequestCanceled(mRequest.token); + callback.onRequestCanceled(mRequest.mToken); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } } - final Request request = new Request(token, state, flags); + final Request request = new Request(token, state); mRequest = request; notifyDeviceStateInfoChanged(); @@ -337,7 +412,7 @@ public final class DeviceStateManagerGlobalTest { @Override public void cancelStateRequest() { - IBinder token = mRequest.token; + final IBinder token = mRequest.mToken; mRequest = null; for (IDeviceStateManagerCallback callback : mCallbacks) { try { @@ -350,19 +425,18 @@ public final class DeviceStateManagerGlobalTest { } @Override - public void requestBaseStateOverride(IBinder token, int state, int flags) { + public void requestBaseStateOverride(@NonNull IBinder token, int state, int unusedFlags) { if (mBaseStateRequest != null) { for (IDeviceStateManagerCallback callback : mCallbacks) { try { - callback.onRequestCanceled(mBaseStateRequest.token); + callback.onRequestCanceled(mBaseStateRequest.mToken); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } } - final Request request = new Request(token, state, flags); - mBaseStateRequest = request; + mBaseStateRequest = new Request(token, state); notifyDeviceStateInfoChanged(); for (IDeviceStateManagerCallback callback : mCallbacks) { @@ -376,7 +450,7 @@ public final class DeviceStateManagerGlobalTest { @Override public void cancelBaseStateOverride() throws RemoteException { - IBinder token = mBaseStateRequest.token; + final IBinder token = mBaseStateRequest.mToken; mBaseStateRequest = null; for (IDeviceStateManagerCallback callback : mCallbacks) { try { @@ -396,24 +470,27 @@ public final class DeviceStateManagerGlobalTest { onStateRequestOverlayDismissed_enforcePermission(); } - public void setSupportedStates(List states) { + public void setSupportedStates(@NonNull List states) { mSupportedDeviceStates = states; notifyDeviceStateInfoChanged(); } + @NonNull public List getSupportedDeviceStates() { return mSupportedDeviceStates; } - public void setBaseState(DeviceState state) { + public void setBaseState(@NonNull DeviceState state) { mBaseState = state; notifyDeviceStateInfoChanged(); } + @NonNull public DeviceState getBaseState() { return getInfo().baseState; } + @NonNull public DeviceState getMergedState() { return getInfo().currentState; } diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java index 78d4324ebb1a4d750e41f0778e003f633a162511..83b5ff3ce0a53806239bb2b73712f5d1c1d4d1e4 100644 --- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java +++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java @@ -21,17 +21,16 @@ import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_AVAILABLE import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS; import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; import android.os.Parcel; import android.platform.test.annotations.Presubmit; -import junit.framework.Assert; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import java.util.HashSet; import java.util.List; @@ -39,28 +38,32 @@ import java.util.Set; /** * Unit tests for {@link android.hardware.devicestate.DeviceState}. - *

        - * Run with atest DeviceStateTest. + * + *

        Build/Install/Run: + * atest FrameworksCoreDeviceStateManagerTests:DeviceStateTest */ @Presubmit -@RunWith(JUnit4.class) +@SmallTest +@RunWith(AndroidJUnit4.class) public final class DeviceStateTest { @Test public void testConstruct() { - DeviceState.Configuration config = new DeviceState.Configuration.Builder( + final DeviceState.Configuration config = new DeviceState.Configuration.Builder( MINIMUM_DEVICE_STATE_IDENTIFIER, "TEST_CLOSED") .setSystemProperties( new HashSet<>(List.of(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS))) .build(); + final DeviceState state = new DeviceState(config); - assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE_IDENTIFIER); - assertEquals(state.getName(), "TEST_CLOSED"); - assertTrue(state.hasProperty(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS)); + + assertThat(state.getIdentifier()).isEqualTo(MINIMUM_DEVICE_STATE_IDENTIFIER); + assertThat(state.getName()).isEqualTo("TEST_CLOSED"); + assertThat(state.hasProperty(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS)).isTrue(); } @Test public void testHasProperties() { - DeviceState.Configuration config = new DeviceState.Configuration.Builder( + final DeviceState.Configuration config = new DeviceState.Configuration.Builder( MINIMUM_DEVICE_STATE_IDENTIFIER, "TEST") .setSystemProperties(new HashSet<>(List.of(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS, PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST))) @@ -68,10 +71,10 @@ public final class DeviceStateTest { final DeviceState state = new DeviceState(config); - assertTrue(state.hasProperty(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS)); - assertTrue(state.hasProperty(PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST)); - assertTrue(state.hasProperties(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS, - PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST)); + assertThat(state.hasProperty(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS)).isTrue(); + assertThat(state.hasProperty(PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST)).isTrue(); + assertThat(state.hasProperties(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS, + PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST)).isTrue(); } @Test @@ -91,7 +94,7 @@ public final class DeviceStateTest { final DeviceState.Configuration stateConfiguration = DeviceState.Configuration.CREATOR.createFromParcel(parcel); - Assert.assertEquals(originalState, new DeviceState(stateConfiguration)); + assertThat(originalState).isEqualTo(new DeviceState(stateConfiguration)); } @Test @@ -109,6 +112,6 @@ public final class DeviceStateTest { final DeviceState.Configuration stateConfiguration = DeviceState.Configuration.CREATOR.createFromParcel(parcel); - Assert.assertEquals(originalState, new DeviceState(stateConfiguration)); + assertThat(originalState).isEqualTo(new DeviceState(stateConfiguration)); } } diff --git a/core/tests/vibrator/src/android/os/VibratorInfoTest.java b/core/tests/vibrator/src/android/os/VibratorInfoTest.java index c8108107585965a75d439d7466fc74bc3c9c2da8..04945f38e3191e62af2ca5de528d95e38a1ffef6 100644 --- a/core/tests/vibrator/src/android/os/VibratorInfoTest.java +++ b/core/tests/vibrator/src/android/os/VibratorInfoTest.java @@ -16,6 +16,8 @@ package android.os; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -24,6 +26,7 @@ import static org.junit.Assert.assertTrue; import android.hardware.vibrator.Braking; import android.hardware.vibrator.IVibrator; +import android.util.Range; import org.junit.Test; import org.junit.runner.RunWith; @@ -39,12 +42,19 @@ public class VibratorInfoTest { private static final float TEST_FREQUENCY_RESOLUTION = 25; private static final float[] TEST_AMPLITUDE_MAP = new float[]{ /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f}; - - private static final VibratorInfo.FrequencyProfile EMPTY_FREQUENCY_PROFILE = - new VibratorInfo.FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null); - private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE = - new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY, + private static final float[] TEST_FREQUENCIES = + new float[]{90f, 120f, 150f, 60f, 30f, 210f, 270f, 300f, 240f, 180f}; + private static final float[] TEST_OUTPUT_ACCELERATIONS = + new float[]{1.2f, 1.8f, 2.4f, 0.6f, 0.1f, 2.2f, 1.0f, 0.5f, 1.9f, 3.0f}; + + private static final VibratorInfo.FrequencyProfileLegacy EMPTY_FREQUENCY_PROFILE = + new VibratorInfo.FrequencyProfileLegacy(Float.NaN, Float.NaN, Float.NaN, null); + private static final VibratorInfo.FrequencyProfileLegacy TEST_FREQUENCY_PROFILE_LEGACY = + new VibratorInfo.FrequencyProfileLegacy(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY, TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP); + private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE = + new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_FREQUENCIES, + TEST_OUTPUT_ACCELERATIONS); @Test public void testHasAmplitudeControl() { @@ -180,52 +190,166 @@ public class VibratorInfoTest { @Test public void testGetFrequencyProfile_unsetProfileIsEmpty() { - assertTrue( - new VibratorInfo.Builder(TEST_VIBRATOR_ID).build().getFrequencyProfile().isEmpty()); + assertTrue(new VibratorInfo.Builder( + TEST_VIBRATOR_ID).build().getFrequencyProfile().isEmpty()); } @Test public void testFrequencyProfile_invalidValuesCreatesEmptyProfile() { + // Invalid resonant frequency. + assertThat(new VibratorInfo.FrequencyProfile(Float.NaN, + TEST_FREQUENCIES, TEST_OUTPUT_ACCELERATIONS).isEmpty()).isTrue(); + assertThat(new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/-1f, + TEST_FREQUENCIES, TEST_OUTPUT_ACCELERATIONS).isEmpty()).isTrue(); + // No frequency-acceleration data + assertThat(new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f, + /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null).isEmpty()).isTrue(); + // Mismatching frequency and output acceleration lists + assertThat(new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f, + /*frequenciesHz=*/ new float[]{30f, 40f, 50f, 100f}, + /*outputAccelerationsGs=*/ new float[]{0.8f, 1.0f, 2.0f}).isEmpty()).isTrue(); + } + + @Test + public void testGetFrequenciesAndOutputAccelerations_noFrequencyAccelerationData_returnNull() { + VibratorInfo.FrequencyProfile emptyFrequencyProfile = + new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f, + /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null); + assertThat(emptyFrequencyProfile.getFrequenciesHz()).isNull(); + assertThat(emptyFrequencyProfile.getOutputAccelerationsGs()).isNull(); + } + + @Test + public void testGetFrequenciesAndOutputAccelerations_mismatchingDataLength_returnNull() { + VibratorInfo.FrequencyProfile emptyFrequencyProfile = + new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f, + /*frequenciesHz=*/ new float[]{150f, 200f}, + /*outputAccelerationsGs=*/ new float[]{1.2f, 2.2f, 3.0f}); + assertThat(emptyFrequencyProfile.getFrequenciesHz()).isNull(); + assertThat(emptyFrequencyProfile.getOutputAccelerationsGs()).isNull(); + } + + @Test + public void testGetFrequenciesAndOutputAccelerations_dataIsDedupedAndSorted() { + VibratorInfo.FrequencyProfile frequencyProfile = + new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f, + /*frequenciesHz=*/ new float[]{150f, 150f, 150f, 130f, 200f, 160f}, + /*outputAccelerationsGs=*/ new float[]{1.2f, 1.5f, 1.9f, 1.0f, 2.2f, 3.0f}); + float[] frequencies = frequencyProfile.getFrequenciesHz(); + assertThat(frequencies).isEqualTo( + new float[]{130f, 150f, 160f, 200f}); + assertThat(frequencyProfile.getOutputAccelerationsGs()).isEqualTo( + new float[]{1.0f, 1.2f, 3.0f, 2.2f}); + } + + @Test + public void testGetFrequencyRangeHz_emptyProfileReturnsNull() { + VibratorInfo.FrequencyProfile emptyFrequencyProfile = + new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f, + /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null); + assertThat( + emptyFrequencyProfile.getFrequencyRangeHz(/*minOutputAcceleration=*/0.2f)).isNull(); + } + + @Test + public void testGetFrequencyRangeHz_validProfileReturnsMappedValues() { + VibratorInfo.FrequencyProfile frequencyProfile = + new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f, + /*frequenciesHz=*/new float[]{90f, 120f, 150f, 60f, 30f, 210f, 180f}, + /*outputAccelerationsGs=*/ new float[]{1.2f, 1.8f, 2.4f, 0.6f, 0.4f, 2.2f, 3.0f}); + + // lower and upper bounds are min and max frequencies + assertThat(frequencyProfile.getFrequencyRangeHz(/*minOutputAcceleration=*/0.33f)).isEqualTo( + new Range<>(frequencyProfile.getMinFrequencyHz(), + frequencyProfile.getMaxFrequencyHz())); + + // lower and upper bounds are within frequency range and use interpolation + assertThat(frequencyProfile.getFrequencyRangeHz(/*minOutputAcceleration=*/2.6f)) + .isEqualTo(new Range<>(160f, 195f)); + + // upper bound is max frequency + assertThat(frequencyProfile.getFrequencyRangeHz(/*minOutputAcceleration=*/2.0f)) + .isEqualTo(new Range<>(130f, frequencyProfile.getMaxFrequencyHz())); + } + + @Test + public void testFrequencyProfile_emptyProfileReturnsNanValues() { + VibratorInfo.FrequencyProfile frequencyProfile = new VibratorInfo.FrequencyProfile( + /*resonantFrequencyHz=*/150f, /*frequenciesHz=*/ null, + /*outputAccelerationsGs=*/ null); + + assertThat(frequencyProfile.getMaxOutputAccelerationGs()).isNaN(); + assertThat(frequencyProfile.getMinFrequencyHz()).isNaN(); + assertThat(frequencyProfile.getMaxFrequencyHz()).isNaN(); + assertThat(frequencyProfile.getOutputAccelerationGs(/*frequencyHz=*/150f)).isNaN(); + } + + @Test + public void testFrequencyProfile_validProfileReturnsAppropriateValues() { + VibratorInfo.FrequencyProfile frequencyProfile = new VibratorInfo.FrequencyProfile( + /*resonantFrequencyHz=*/150f, TEST_FREQUENCIES, TEST_OUTPUT_ACCELERATIONS); + + assertThat(frequencyProfile.getMaxOutputAccelerationGs()).isEqualTo(3f); + assertThat(frequencyProfile.getMinFrequencyHz()).isEqualTo(30f); + assertThat(frequencyProfile.getMaxFrequencyHz()).isEqualTo(300f); + assertThat(frequencyProfile.getOutputAccelerationGs(/*frequencyHz=*/150f)).isEqualTo(2.4f); + // Test getting output acceleration using linear interpolation + assertThat(frequencyProfile.getOutputAccelerationGs(/*frequencyHz=*/166f)).isEqualTo( + 2.72f); + } + + @Test + public void testGetFrequencyProfileLegacy_unsetProfileIsEmpty() { + assertTrue(new VibratorInfo.Builder( + TEST_VIBRATOR_ID).build().getFrequencyProfileLegacy().isEmpty()); + } + + @Test + public void testFrequencyProfileLegacy_invalidValuesCreatesEmptyProfile() { // Invalid, contains NaN values or empty array. - assertTrue(new VibratorInfo.FrequencyProfile( + assertTrue(new VibratorInfo.FrequencyProfileLegacy( Float.NaN, 50, 25, TEST_AMPLITUDE_MAP).isEmpty()); - assertTrue(new VibratorInfo.FrequencyProfile( + assertTrue(new VibratorInfo.FrequencyProfileLegacy( 150, Float.NaN, 25, TEST_AMPLITUDE_MAP).isEmpty()); - assertTrue(new VibratorInfo.FrequencyProfile( + assertTrue(new VibratorInfo.FrequencyProfileLegacy( 150, 50, Float.NaN, TEST_AMPLITUDE_MAP).isEmpty()); - assertTrue(new VibratorInfo.FrequencyProfile(150, 50, 25, null).isEmpty()); + assertTrue(new VibratorInfo.FrequencyProfileLegacy(150, 50, 25, null).isEmpty()); // Invalid, contains zero or negative frequency values. - assertTrue(new VibratorInfo.FrequencyProfile(-1, 50, 25, TEST_AMPLITUDE_MAP).isEmpty()); - assertTrue(new VibratorInfo.FrequencyProfile(150, 0, 25, TEST_AMPLITUDE_MAP).isEmpty()); - assertTrue(new VibratorInfo.FrequencyProfile(150, 50, -2, TEST_AMPLITUDE_MAP).isEmpty()); + assertTrue( + new VibratorInfo.FrequencyProfileLegacy(-1, 50, 25, TEST_AMPLITUDE_MAP).isEmpty()); + assertTrue( + new VibratorInfo.FrequencyProfileLegacy(150, 0, 25, TEST_AMPLITUDE_MAP).isEmpty()); + assertTrue( + new VibratorInfo.FrequencyProfileLegacy(150, 50, -2, TEST_AMPLITUDE_MAP).isEmpty()); // Invalid max amplitude entries. - assertTrue(new VibratorInfo.FrequencyProfile( + assertTrue(new VibratorInfo.FrequencyProfileLegacy( 150, 50, 50, new float[] { -1, 0, 1, 1, 0 }).isEmpty()); - assertTrue(new VibratorInfo.FrequencyProfile( + assertTrue(new VibratorInfo.FrequencyProfileLegacy( 150, 50, 50, new float[] { 0, 1, 2, 1, 0 }).isEmpty()); // Invalid, minFrequency > resonantFrequency - assertTrue(new VibratorInfo.FrequencyProfile( + assertTrue(new VibratorInfo.FrequencyProfileLegacy( /* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ 250, 25, TEST_AMPLITUDE_MAP) .isEmpty()); // Invalid, maxFrequency < resonantFrequency by changing resolution. - assertTrue(new VibratorInfo.FrequencyProfile( + assertTrue(new VibratorInfo.FrequencyProfileLegacy( 150, 50, /* frequencyResolutionHz= */ 10, TEST_AMPLITUDE_MAP).isEmpty()); } @Test - public void testGetFrequencyRangeHz_emptyProfileReturnsNull() { - assertNull(new VibratorInfo.FrequencyProfile( + public void testLegacyGetFrequencyRangeHz_emptyProfileReturnsNull() { + assertNull(new VibratorInfo.FrequencyProfileLegacy( Float.NaN, 50, 25, TEST_AMPLITUDE_MAP).getFrequencyRangeHz()); - assertNull(new VibratorInfo.FrequencyProfile( + assertNull(new VibratorInfo.FrequencyProfileLegacy( 150, Float.NaN, 25, TEST_AMPLITUDE_MAP).getFrequencyRangeHz()); - assertNull(new VibratorInfo.FrequencyProfile( + assertNull(new VibratorInfo.FrequencyProfileLegacy( 150, 50, Float.NaN, TEST_AMPLITUDE_MAP).getFrequencyRangeHz()); - assertNull(new VibratorInfo.FrequencyProfile(150, 50, 25, null).getFrequencyRangeHz()); + assertNull( + new VibratorInfo.FrequencyProfileLegacy(150, 50, 25, null).getFrequencyRangeHz()); } @Test - public void testGetFrequencyRangeHz_validProfileReturnsMappedValues() { - VibratorInfo.FrequencyProfile profile = new VibratorInfo.FrequencyProfile( + public void testLegacyGetFrequencyRangeHz_validProfileReturnsMappedValues() { + VibratorInfo.FrequencyProfileLegacy profile = new VibratorInfo.FrequencyProfileLegacy( /* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ 50, /* frequencyResolutionHz= */ 25, @@ -239,12 +363,12 @@ public class VibratorInfoTest { @Test public void testGetMaxAmplitude_emptyProfileReturnsAlwaysZero() { - VibratorInfo.FrequencyProfile profile = EMPTY_FREQUENCY_PROFILE; + VibratorInfo.FrequencyProfileLegacy profile = EMPTY_FREQUENCY_PROFILE; assertEquals(0f, profile.getMaxAmplitude(Float.NaN), TEST_TOLERANCE); assertEquals(0f, profile.getMaxAmplitude(100f), TEST_TOLERANCE); assertEquals(0f, profile.getMaxAmplitude(200f), TEST_TOLERANCE); - profile = new VibratorInfo.FrequencyProfile( + profile = new VibratorInfo.FrequencyProfileLegacy( /* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ Float.NaN, /* frequencyResolutionHz= */ Float.NaN, @@ -257,7 +381,7 @@ public class VibratorInfoTest { @Test public void testGetMaxAmplitude_validProfileReturnsMappedValues() { - VibratorInfo.FrequencyProfile profile = new VibratorInfo.FrequencyProfile( + VibratorInfo.FrequencyProfileLegacy profile = new VibratorInfo.FrequencyProfileLegacy( /* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ 50, /* frequencyResolutionHz= */ 25, @@ -301,6 +425,7 @@ public class VibratorInfoTest { .setPwlePrimitiveDurationMax(50) .setPwleSizeMax(20) .setQFactor(2f) + .setFrequencyProfileLegacy(TEST_FREQUENCY_PROFILE_LEGACY) .setFrequencyProfile(TEST_FREQUENCY_PROFILE) .setMaxEnvelopeEffectSize(16) .setMinEnvelopeEffectControlPointDurationMillis(20) @@ -343,18 +468,33 @@ public class VibratorInfoTest { assertNotEquals(complete, completeWithDifferentPrimitiveDuration); assertFalse(complete.equalContent(completeWithDifferentPrimitiveDuration)); - VibratorInfo completeWithDifferentFrequencyProfile = completeBuilder - .setFrequencyProfile(new VibratorInfo.FrequencyProfile( + VibratorInfo completeWithDifferentFrequencyProfileLegacy = completeBuilder + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy( TEST_RESONANT_FREQUENCY + 20, TEST_MIN_FREQUENCY + 10, TEST_FREQUENCY_RESOLUTION + 5, TEST_AMPLITUDE_MAP)) .build(); + assertNotEquals(complete, completeWithDifferentFrequencyProfileLegacy); + assertFalse(complete.equalContent(completeWithDifferentFrequencyProfileLegacy)); + + VibratorInfo completeWithEmptyFrequencyProfileLegacy = completeBuilder + .setFrequencyProfileLegacy(EMPTY_FREQUENCY_PROFILE) + .build(); + assertNotEquals(complete, completeWithEmptyFrequencyProfileLegacy); + assertFalse(complete.equalContent(completeWithEmptyFrequencyProfileLegacy)); + + VibratorInfo completeWithDifferentFrequencyProfile = completeBuilder + .setFrequencyProfile( + new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY + 20, + new float[]{90f, 150f}, new float[]{1.2f, 2.2f})) + .build(); assertNotEquals(complete, completeWithDifferentFrequencyProfile); assertFalse(complete.equalContent(completeWithDifferentFrequencyProfile)); VibratorInfo completeWithEmptyFrequencyProfile = completeBuilder - .setFrequencyProfile(EMPTY_FREQUENCY_PROFILE) + .setFrequencyProfile( + new VibratorInfo.FrequencyProfile(Float.NaN, null, null)) .build(); assertNotEquals(complete, completeWithEmptyFrequencyProfile); assertFalse(complete.equalContent(completeWithEmptyFrequencyProfile)); @@ -391,6 +531,7 @@ public class VibratorInfoTest { .setSupportedEffects(VibrationEffect.EFFECT_CLICK) .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20) .setQFactor(Float.NaN) + .setFrequencyProfileLegacy(TEST_FREQUENCY_PROFILE_LEGACY) .setFrequencyProfile(TEST_FREQUENCY_PROFILE) .build(); diff --git a/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java b/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java index fc31ac44b362030e8fae91e453d78a4ca42d652e..c9ab29722011e4ab96792e67d1cdd2e107813266 100644 --- a/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java @@ -16,6 +16,8 @@ package android.os.vibrator; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertEquals; @@ -24,7 +26,11 @@ import android.hardware.vibrator.IVibrator; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.VibratorInfo; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -33,6 +39,9 @@ import org.junit.runners.JUnit4; public class MultiVibratorInfoTest { private static final float TEST_TOLERANCE = 1e-5f; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Test public void testGetId() { VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) @@ -157,16 +166,17 @@ public class MultiVibratorInfoTest { } @Test + @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public void testGetQFactorAndResonantFrequency_differentValues_returnsNaN() { VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) .setQFactor(1f) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null)) + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(1, 1, 1, null)) .build(); VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) .setQFactor(2f) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 2, 2, null)) + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(2, 2, 2, null)) .build(); VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, @@ -187,17 +197,18 @@ public class MultiVibratorInfoTest { } @Test + @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public void testGetQFactorAndResonantFrequency_sameValues_returnsValue() { VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) .setQFactor(10f) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile( + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy( /* resonantFrequencyHz= */ 11, 10, 0.5f, null)) .build(); VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) .setQFactor(10f) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile( + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy( /* resonantFrequencyHz= */ 11, 5, 1, null)) .build(); @@ -207,20 +218,21 @@ public class MultiVibratorInfoTest { assertEquals(10f, info.getQFactor(), TEST_TOLERANCE); assertEquals(11f, info.getResonantFrequencyHz(), TEST_TOLERANCE); // No frequency range defined. - assertTrue(info.getFrequencyProfile().isEmpty()); + assertTrue(info.getFrequencyProfileLegacy().isEmpty()); assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); } @Test - public void testGetFrequencyProfile_differentResonantFrequencyOrResolutions_returnsEmpty() { + @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public void testGetFrequencyProfileLegacy_differentResonantFreqOrResolutions_returnsEmpty() { VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(1, 1, 1, new float[] { 0, 1 })) .build(); VibratorInfo differentResonantFrequency = new VibratorInfo.Builder(/* id= */ 2) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 1, 1, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(2, 1, 1, new float[] { 0, 1 })) .build(); VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, @@ -230,7 +242,7 @@ public class MultiVibratorInfoTest { VibratorInfo differentFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 2, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(1, 1, 2, new float[] { 0, 1 })) .build(); info = new MultiVibratorInfo(/* id= */ 1, @@ -240,15 +252,16 @@ public class MultiVibratorInfoTest { } @Test - public void testGetFrequencyProfile_missingValues_returnsEmpty() { + @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public void testGetFrequencyProfileLegacy_missingValues_returnsEmpty() { VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(1, 1, 1, new float[] { 0, 1 })) .build(); VibratorInfo missingResonantFrequency = new VibratorInfo.Builder(/* id= */ 2) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(Float.NaN, 1, 1, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(Float.NaN, 1, 1, new float[] { 0, 1 })) .build(); @@ -259,7 +272,7 @@ public class MultiVibratorInfoTest { VibratorInfo missingMinFrequency = new VibratorInfo.Builder(/* id= */ 2) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, Float.NaN, 1, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(1, Float.NaN, 1, new float[] { 0, 1 })) .build(); info = new MultiVibratorInfo(/* id= */ 1, @@ -269,7 +282,7 @@ public class MultiVibratorInfoTest { VibratorInfo missingFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, Float.NaN, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(1, 1, Float.NaN, new float[] { 0, 1 })) .build(); info = new MultiVibratorInfo(/* id= */ 1, @@ -279,7 +292,7 @@ public class MultiVibratorInfoTest { VibratorInfo missingMaxAmplitudes = new VibratorInfo.Builder(/* id= */ 2) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null)) + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(1, 1, 1, null)) .build(); info = new MultiVibratorInfo(/* id= */ 1, new VibratorInfo[]{firstInfo, missingMaxAmplitudes}); @@ -288,20 +301,21 @@ public class MultiVibratorInfoTest { } @Test - public void testGetFrequencyProfile_unalignedMaxAmplitudes_returnsEmpty() { + @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public void testGetFrequencyProfileLegacy_unalignedMaxAmplitudes_returnsEmpty() { VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(11, 10, 0.5f, new float[] { 0, 1, 1, 0 })) .build(); VibratorInfo unalignedMinFrequency = new VibratorInfo.Builder(/* id= */ 2) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.1f, 0.5f, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(11, 10.1f, 0.5f, new float[] { 0, 1, 1, 0 })) .build(); VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 2) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(11, 10.5f, 0.5f, new float[] { 0, 1, 1, 0 })) .build(); @@ -312,20 +326,21 @@ public class MultiVibratorInfoTest { } @Test - public void testGetFrequencyProfile_alignedProfiles_returnsIntersection() { + @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public void testGetFrequencyProfileLegacy_alignedProfiles_returnsIntersection() { VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(11, 10, 0.5f, new float[] { 0.5f, 1, 1, 0.5f })) .build(); VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(11, 10.5f, 0.5f, new float[] { 1, 1, 1 })) .build(); VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3) .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.8f, 0.5f })) .build(); @@ -333,29 +348,157 @@ public class MultiVibratorInfoTest { new VibratorInfo[]{firstInfo, secondInfo, thirdVibrator}); assertEquals( - new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }), - info.getFrequencyProfile()); + new VibratorInfo.FrequencyProfileLegacy(11, 10.5f, 0.5f, + new float[]{0.8f, 1, 0.5f}), + info.getFrequencyProfileLegacy()); assertEquals(true, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); // Third vibrator without frequency control capability. thirdVibrator = new VibratorInfo.Builder(/* id= */ 3) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, + .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.8f, 0.5f })) .build(); info = new MultiVibratorInfo(/* id= */ 1, new VibratorInfo[]{firstInfo, secondInfo, thirdVibrator}); assertEquals( - new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }), - info.getFrequencyProfile()); + new VibratorInfo.FrequencyProfileLegacy(11, 10.5f, 0.5f, + new float[]{0.8f, 1, 0.5f}), + info.getFrequencyProfileLegacy()); assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); } + @Test + @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public void testGetFrequencyProfile_alignedProfiles_returnsIntersection() { + VibratorInfo firstInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 1, + IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f, + /*frequencies=*/new float[]{30f, 60f, 120f, 150f, 180f, 210f, 270f, 300f}, + /*accelerations=*/new float[]{0.1f, 0.6f, 1.8f, 2.4f, 3.0f, 2.2f, 1.0f, 0.5f}); + + VibratorInfo secondInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 2, + IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f, + /*frequencies=*/new float[]{120f, 150f, 180f, 210f}, + /*accelerations=*/new float[]{1.5f, 2.6f, 2.7f, 2.1f}); + + VibratorInfo.FrequencyProfile expectedFrequencyProfile = + new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/ + 180f, /*frequenciesHz=*/new float[]{120.0f, 150.0f, 180.0f, 210.0f}, + /*outputAccelerationsGs=*/new float[]{1.5f, 2.4f, 2.7f, 2.1f}); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, secondInfo}); + + assertThat(info.getFrequencyProfile()).isEqualTo(expectedFrequencyProfile); + } + + @Test + @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public void testGetFrequencyProfile_alignedProfilesUsingInterpolation_returnsIntersection() { + VibratorInfo firstInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 1, + IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f, + /*frequencies=*/new float[]{30f, 60f, 120f}, + /*accelerations=*/new float[]{0.25f, 1.0f, 4.0f}); + + VibratorInfo secondInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 2, + IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f, + /*frequencies=*/new float[]{40f, 70f, 110f}, + /*accelerations=*/new float[]{1.0f, 2.5f, 4.0f}); + + VibratorInfo.FrequencyProfile expectedFrequencyProfile = + new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/ + 180f, /*frequenciesHz=*/new float[]{40f, 60f, 70f, 110f}, + /*outputAccelerationsGs=*/new float[]{0.5f, 1.0f, 1.5f, 3.5f}); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, secondInfo}); + + assertThat(info.getFrequencyProfile()).isEqualTo(expectedFrequencyProfile); + } + + @Test + @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public void testGetFrequencyProfile_disjointFrequencyRange_returnsEmpty() { + + VibratorInfo firstInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 1, + IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f, + /*frequencies=*/new float[]{30f, 60f, 120f, 150f, 180f, 210f, 270f, 300f}, + /*accelerations=*/new float[]{0.1f, 0.6f, 1.8f, 2.4f, 3.0f, 2.2f, 1.0f, 0.5f}); + + VibratorInfo secondInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 2, + IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f, + /*frequencies=*/new float[]{310f, 320f, 350f, 380f, 410f, 440f}, + /*accelerations=*/new float[]{0.3f, 0.75f, 1.82f, 2.11f, 2.8f, 2.12f, 1.4f, 0.42f}); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, secondInfo}); + + assertThat(info.getFrequencyProfile()).isEqualTo( + new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/ Float.NaN, + /*frequenciesHz=*/null, /*outputAccelerationsGs=*/null)); + assertThat(info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)).isFalse(); + } + + @Test + @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public void testGetFrequencyProfile_emptyFrequencyRange_returnsEmpty() { + VibratorInfo firstInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 1, + IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/180f, + /*frequencies=*/null, /*accelerations=*/null); + + VibratorInfo secondInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 2, + IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/180f, + /*frequencies=*/new float[]{30f, 60f, 150f, 180f, 210f, 240f, 300f}, + /*accelerations=*/new float[]{0.1f, 0.6f, 2.4f, 3.0f, 2.2f, 1.9f, 0.5f}); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, secondInfo}); + + assertThat(info.getFrequencyProfile()).isEqualTo( + new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/ Float.NaN, + /*frequenciesHz=*/null, + /*outputAccelerationsGs=*/null)); + assertThat(info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)).isFalse(); + } + + @Test + @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS) + public void testGetFrequencyProfile_differentResonantFrequency_returnsEmpty() { + VibratorInfo firstInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 1, + IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 160f, + /*frequencies=*/new float[]{30f, 60f, 120f, 150f, 180f, 210f, 270f, 300f}, + /*accelerations=*/new float[]{0.1f, 0.6f, 1.8f, 2.4f, 3.0f, 2.2f, 1.0f, 0.5f}); + + VibratorInfo secondInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 2, + IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f, + /*frequencies=*/new float[]{30f, 60f, 120f, 150f, 180f, 210f, 270f, 300f}, + /*accelerations=*/new float[]{0.1f, 0.6f, 1.8f, 2.4f, 3.0f, 2.2f, 1.0f, 0.5f}); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, secondInfo}); + + assertThat(info.getFrequencyProfile()).isEqualTo( + new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/ Float.NaN, + /*frequenciesHz=*/null, + /*outputAccelerationsGs=*/null)); + assertThat(info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)).isFalse(); + } + + private VibratorInfo createVibratorInfoWithFrequencyProfile(int id, long capabilities, + float resonantFrequencyHz, float[] frequencies, float[] accelerations) { + return new VibratorInfo.Builder(id) + .setCapabilities(capabilities) + .setFrequencyProfile( + new VibratorInfo.FrequencyProfile(resonantFrequencyHz, frequencies, + accelerations)) + .build(); + } + /** * Asserts that the frequency profile is empty, and therefore frequency control isn't supported. */ private void assertEmptyFrequencyProfileAndControl(VibratorInfo info) { - assertTrue(info.getFrequencyProfile().isEmpty()); + assertTrue(info.getFrequencyProfileLegacy().isEmpty()); assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); } } diff --git a/keystore/OWNERS b/keystore/OWNERS index 68917771571130b17b90b40314a36b8c866a1e08..ea783e7f0c06bc17c77cca40e420dba89408c918 100644 --- a/keystore/OWNERS +++ b/keystore/OWNERS @@ -1,5 +1,6 @@ # Bug component: 189335 +ascull@google.com drysdale@google.com -eranm@google.com jbires@google.com +sethmo@google.com swillden@google.com diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index e493ed1110c8f8d1d45a771ecb677b32bfdc16fd..4642fe59bcb21275860070e97f72d48a1cd4b964 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -234,10 +234,6 @@ android_library { // *.kt sources are inside a filegroup. "kotlin-annotations", ], - required: [ - "wmshell.protolog.json.gz", - "wmshell.protolog.pb", - ], flags_packages: [ "com_android_wm_shell_flags", ], @@ -246,3 +242,11 @@ android_library { plugins: ["dagger2-compiler"], use_resource_processor: true, } + +java_defaults { + name: "wmshell_defaults", + required: [ + "wmshell.protolog.json.gz", + "wmshell.protolog.pb", + ], +} diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml index 3b739c3d581706b8c55f585de656e2a8ca0a80eb..1260796810c2024678282b5389e54a74ed18700d 100644 --- a/libs/WindowManager/Shell/AndroidManifest.xml +++ b/libs/WindowManager/Shell/AndroidManifest.xml @@ -24,6 +24,7 @@ + + android:viewportHeight="24" + android:viewportWidth="24"> - + android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4C10,21.1 10.9,22 12,22z"/> + + + diff --git a/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_background.xml index a12a7465895381bb3aa8b85dfdff4a1c101a98b9..473236c967dfa108a985df2a1eec2523d4f66e9e 100644 --- a/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_background.xml +++ b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_background.xml @@ -18,7 +18,7 @@ - + diff --git a/libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_dismiss_button_background.xml b/libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_confirm_button_background.xml similarity index 100% rename from libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_dismiss_button_background.xml rename to libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_confirm_button_background.xml diff --git a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml index a269b9ee1dd580bbef8f24d2037abb1701c91103..fd75827cff75f742ecbc8c88e7f3dbe3e79f7f64 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml @@ -26,6 +26,7 @@ android:id="@+id/arrow_icon" android:layout_width="10dp" android:layout_height="12dp" + android:elevation="2dp" android:layout_gravity="center_vertical" android:src="@drawable/desktop_windowing_education_tooltip_left_arrow" /> diff --git a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml index 09a049c060eb6498d7e9d6151da988b908362429..42f955dc0fdb0bdeec884798d6cfc193275ccbe4 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml @@ -16,16 +16,18 @@ @@ -34,9 +36,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginStart="2dp" + android:layout_marginHorizontal="8dp" android:lineHeight="20dp" - android:maxWidth="150dp" + android:maxWidth="220dp" android:textColor="@android:color/system_on_tertiary_container_light" android:textFontWeight="500" android:textSize="14sp" /> diff --git a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_top_arrow_tooltip.xml b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_top_arrow_tooltip.xml index c73c1dad0e18b37b58d25f79db2df148fc2ec589..83d7ef7c842c60ea0acfe71e034c804e48c8fe92 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_top_arrow_tooltip.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_top_arrow_tooltip.xml @@ -25,6 +25,7 @@ android:id="@+id/arrow_icon" android:layout_width="12dp" android:layout_height="9dp" + android:elevation="2dp" android:layout_gravity="center_horizontal" android:src="@drawable/desktop_windowing_education_tooltip_top_arrow" /> diff --git a/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml b/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml index 8ff382bbc7b48c1371f7110d55d45ff659f0ed66..b5bceda9a623fe6e4e8286941bf64185e4e442a3 100644 --- a/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml +++ b/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml @@ -111,7 +111,7 @@