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.
*
*
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 {
*