Merge "GPU Memory: add unittests for libgpumem"
diff --git a/.gitignore b/.gitignore
index 0d20b64..685e379 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
+*.iml
 *.pyc
+.idea/
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 9ba4819..d4c1616 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -305,8 +305,9 @@
 
 /*
  * Returns a vector of dump fds under |dir_path| with a given |file_prefix|.
- * The returned vector is sorted by the mtimes of the dumps. If |limit_by_mtime|
- * is set, the vector only contains files that were written in the last 30 minutes.
+ * The returned vector is sorted by the mtimes of the dumps with descending
+ * order. If |limit_by_mtime| is set, the vector only contains files that
+ * were written in the last 30 minutes.
  */
 static std::vector<DumpData> GetDumpFds(const std::string& dir_path,
                                         const std::string& file_prefix,
@@ -353,6 +354,10 @@
 
         dump_data.emplace_back(DumpData{abs_path, std::move(fd), st.st_mtime});
     }
+    if (!dump_data.empty()) {
+        std::sort(dump_data.begin(), dump_data.end(),
+            [](const auto& a, const auto& b) { return a.mtime > b.mtime; });
+    }
 
     return dump_data;
 }
@@ -1362,6 +1367,53 @@
     printf("\n");
 }
 
+static void DumpstateArcOnly() {
+    // Trimmed-down version of dumpstate to only include a whitelisted
+    // set of logs (system log, event log, and system server / system app
+    // crashes, and ARC networking logs). See b/136273873 and b/138459828
+    // for context. New sections must be first approved by Chrome OS Privacy
+    // and then added to server side cros monitoring PII scrubber before adding
+    // them here. See cl/312126645 for an example.
+    DurationReporter duration_reporter("DUMPSTATE");
+    unsigned long timeout_ms;
+    // calculate timeout
+    timeout_ms = logcat_timeout({"main", "system", "crash"});
+    RunCommand("SYSTEM LOG",
+               {"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+               CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+    timeout_ms = logcat_timeout({"events"});
+    RunCommand(
+        "EVENT LOG",
+        {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+        CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+
+    printf("========================================================\n");
+    printf("== Networking Service\n");
+    printf("========================================================\n");
+
+    // ARC networking service implements dumpsys by reusing the 'wifi' service name.
+    // The top-level handler is implemented in handleDump() in
+    // vendor/google_arc/libs/arc-services/src/com/android/server/arc/net/ArcNetworkService.java.
+    // It outputs a subset of Android system server state relevant for debugging ARC
+    // connectivity issues, in a PII-free manner. See b/147270970.
+    RunDumpsys("DUMPSYS NETWORK_SERVICE_LIMITED", {"wifi", "-a"},
+               CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+
+    printf("========================================================\n");
+    printf("== Dropbox crashes\n");
+    printf("========================================================\n");
+
+    RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"});
+    RunDumpsys("DROPBOX SYSTEM APP CRASHES", {"dropbox", "-p", "system_app_crash"});
+
+    printf("========================================================\n");
+    printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(),
+           ds.progress_->GetMax(), ds.progress_->GetInitialMax());
+    printf("========================================================\n");
+    printf("== dumpstate: done (id %d)\n", ds.id_);
+    printf("========================================================\n");
+}
+
 // Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent
 // via the consent they are shown. Ignores other errors that occur while running various
 // commands. The consent checking is currently done around long running tasks, which happen to
@@ -2048,7 +2100,7 @@
 static void ShowUsage() {
     fprintf(stderr,
             "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-d] [-p] "
-            "[-z] [-s] [-S] [-q] [-P] [-R] [-V version]\n"
+            "[-z] [-s] [-S] [-q] [-P] [-R] [-A] [-V version]\n"
             "  -h: display this help message\n"
             "  -b: play sound file instead of vibrate, at beginning of job\n"
             "  -e: play sound file instead of vibrate, at end of job\n"
@@ -2061,6 +2113,7 @@
             "  -P: send broadcast when started and do progress updates\n"
             "  -R: take bugreport in remote mode (requires -z and -d, shouldn't be used with -P)\n"
             "  -w: start binder service and make it wait for a call to startBugreport\n"
+            "  -A: output limited information that is safe for submission in ARC++ bugreports\n"
             "  -v: prints the dumpstate header and exit\n");
 }
 
@@ -2306,13 +2359,12 @@
         "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d "
         "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
         "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
-        "args: %s\n",
+        "arc_only: %d args: %s\n",
         options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
         options.do_screenshot, options.is_remote_mode, options.show_header_only,
-        options.do_start_service,
-        options.telephony_only, options.wifi_only, options.do_progress_updates,
-        options.bugreport_fd.get(), options.bugreport_mode.c_str(),
-        toString(options.dumpstate_hal_mode).c_str(), options.args.c_str());
+        options.do_start_service, options.telephony_only, options.wifi_only,
+        options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(),
+        toString(options.dumpstate_hal_mode).c_str(), options.arc_only, options.args.c_str());
 }
 
 void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
@@ -2334,7 +2386,7 @@
 Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) {
     RunStatus status = RunStatus::OK;
     int c;
-    while ((c = getopt(argc, argv, "dho:svqzpPBRSV:w")) != -1) {
+    while ((c = getopt(argc, argv, "dho:svqzpAPBRSV:w")) != -1) {
         switch (c) {
             // clang-format off
             case 'd': do_add_date = true;            break;
@@ -2346,6 +2398,7 @@
             case 'p': do_screenshot = true;          break;
             case 'P': do_progress_updates = true;    break;
             case 'R': is_remote_mode = true;         break;
+            case 'A': arc_only = true;               break;
             case 'V':                                break;  // compatibility no-op
             case 'w':
                 // This was already processed
@@ -2630,6 +2683,7 @@
     // duration is logged into MYLOG instead.
     PrintHeader();
 
+    // TODO(nandana) reduce code repetition in if branches
     if (options_->telephony_only) {
         MaybeTakeEarlyScreenshot();
         onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
@@ -2641,6 +2695,11 @@
         onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
         MaybeCheckUserConsent(calling_uid, calling_package);
         DumpstateWifiOnly();
+    } else if (options_->arc_only) {
+        MaybeTakeEarlyScreenshot();
+        onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
+        MaybeCheckUserConsent(calling_uid, calling_package);
+        DumpstateArcOnly();
     } else {
         // Invoke critical dumpsys first to preserve system state, before doing anything else.
         RunDumpsysCritical();
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 28d8936..c8c9f45 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -377,6 +377,8 @@
         bool do_start_service = false;
         bool telephony_only = false;
         bool wifi_only = false;
+        // Trimmed-down version of dumpstate to only include whitelisted logs.
+        bool arc_only = false;
         // Whether progress updates should be published.
         bool do_progress_updates = false;
         // The mode we'll use when calling IDumpstateDevice::dumpstateBoard.
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp
index 68d3733..f1342a5 100644
--- a/cmds/dumpstate/main.cpp
+++ b/cmds/dumpstate/main.cpp
@@ -30,7 +30,7 @@
     bool do_wait = false;
     int c;
     // Keep flags in sync with Dumpstate::DumpOptions::Initialize.
-    while ((c = getopt(argc, argv, "wdho:svqzpPBRSV:")) != -1 && !do_wait) {
+    while ((c = getopt(argc, argv, "dho:svqzpAPBRSV:w")) != -1 && !do_wait) {
         switch (c) {
             case 'w':
                 do_wait = true;
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 9871a3b..7078521 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -179,6 +179,7 @@
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
+    EXPECT_FALSE(options_.arc_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -206,6 +207,7 @@
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.arc_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -231,6 +233,7 @@
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
+    EXPECT_FALSE(options_.arc_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -249,6 +252,7 @@
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.do_start_service);
+    EXPECT_FALSE(options_.arc_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
@@ -266,6 +270,7 @@
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.arc_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
@@ -282,6 +287,7 @@
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.arc_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeWearBugReport) {
@@ -299,6 +305,7 @@
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.arc_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
@@ -316,6 +323,7 @@
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.arc_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
@@ -333,6 +341,37 @@
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.arc_only);
+}
+
+TEST_F(DumpOptionsTest, InitializeArcOnlyBugreport) {
+    // clang-format off
+    char* argv[] = {
+        const_cast<char*>("dumpstatez"),
+        const_cast<char*>("-S"),
+        const_cast<char*>("-d"),
+        const_cast<char*>("-z"),
+        const_cast<char*>("-q"),
+        const_cast<char*>("-A")
+    };
+    // clang-format on
+
+    Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
+
+    EXPECT_EQ(status, Dumpstate::RunStatus::OK);
+    EXPECT_TRUE(options_.do_add_date);
+    EXPECT_TRUE(options_.do_zip_file);
+    EXPECT_TRUE(options_.use_control_socket);
+    EXPECT_FALSE(options_.do_vibrate);
+    EXPECT_TRUE(options_.arc_only);
+
+    // Other options retain default values
+    EXPECT_FALSE(options_.show_header_only);
+    EXPECT_FALSE(options_.do_screenshot);
+    EXPECT_FALSE(options_.do_progress_updates);
+    EXPECT_FALSE(options_.is_remote_mode);
+    EXPECT_FALSE(options_.use_socket);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeDefaultBugReport) {
@@ -361,6 +400,7 @@
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.wifi_only);
+    EXPECT_FALSE(options_.arc_only);
 }
 
 TEST_F(DumpOptionsTest, InitializePartial1) {
@@ -390,6 +430,7 @@
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
+    EXPECT_FALSE(options_.arc_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -419,6 +460,7 @@
     EXPECT_FALSE(options_.do_zip_file);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_FALSE(options_.arc_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
diff --git a/cmds/servicemanager/Access.cpp b/cmds/servicemanager/Access.cpp
index 1bbde23..b7e520f 100644
--- a/cmds/servicemanager/Access.cpp
+++ b/cmds/servicemanager/Access.cpp
@@ -31,7 +31,7 @@
 #endif
 
 static std::string getPidcon(pid_t pid) {
-    if (pid != getpid()) return "";
+    android_errorWriteLog(0x534e4554, "121035042");
 
     char* lookup = nullptr;
     if (getpidcon(pid, &lookup) < 0) {
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 2631b14..f195399 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -125,6 +125,8 @@
  * Note that {@link ADataSpace} only exposes a few values. This may return
  * {@link ADATASPACE_UNKNOWN}, even for Named ColorSpaces, if they have no
  * corresponding ADataSpace.
+ *
+ * Available since API level 30.
  */
 int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap)  __INTRODUCED_IN(30);
 
@@ -189,6 +191,8 @@
 /**
  *  User-defined function for writing the output of compression.
  *
+ *  Available since API level 30.
+ *
  *  @param userContext Pointer to user-defined data passed to
  *         {@link AndroidBitmap_compress}.
  *  @param data Compressed data of |size| bytes to write.
@@ -202,6 +206,8 @@
 /**
  *  Compress |pixels| as described by |info|.
  *
+ *  Available since API level 30.
+ *
  *  @param info Description of the pixels to compress.
  *  @param dataspace {@link ADataSpace} describing the color space of the
  *                   pixels.
@@ -234,6 +240,8 @@
  *
  *  Client must not modify it while a Bitmap is wrapping it.
  *
+ *  Available since API level 30.
+ *
  *  @param bitmap Handle to an android.graphics.Bitmap.
  *  @param outBuffer On success, is set to a pointer to the
  *         {@link AHardwareBuffer} associated with bitmap. This acquires
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index c1c4a72..bdf11e4 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -129,9 +129,12 @@
  *
  * This api is thread-safe. Any thread is allowed to register a new refresh
  * rate callback for the choreographer instance.
+ *
+ * Available since API level 30.
  */
 void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
-                                                AChoreographer_refreshRateCallback, void* data);
+                                                AChoreographer_refreshRateCallback, void* data)
+        __INTRODUCED_IN(30);
 
 /**
  * Unregisters a callback to be run when the display refresh rate changes, along
@@ -144,9 +147,12 @@
  * callback and associated data pointer are unregistered, then there is a
  * guarantee that when the unregistration completes that that callback will not
  * be run with the data pointer passed.
+ *
+ * Available since API level 30.
  */
 void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
-                                                  AChoreographer_refreshRateCallback, void* data);
+                                                  AChoreographer_refreshRateCallback, void* data)
+        __INTRODUCED_IN(30);
 #endif /* __ANDROID_API__ >= 30 */
 
 __END_DECLS
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 05f4340..ccf3e59 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -58,13 +58,13 @@
     ACONFIGURATION_ORIENTATION_ANY  = 0x0000,
     /**
      * Orientation: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">port</a>
+     * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">port</a>
      * resource qualifier.
      */
     ACONFIGURATION_ORIENTATION_PORT = 0x0001,
     /**
      * Orientation: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">land</a>
+     * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">land</a>
      * resource qualifier.
      */
     ACONFIGURATION_ORIENTATION_LAND = 0x0002,
@@ -75,7 +75,7 @@
     ACONFIGURATION_TOUCHSCREEN_ANY  = 0x0000,
     /**
      * Touchscreen: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a>
+     * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a>
      * resource qualifier.
      */
     ACONFIGURATION_TOUCHSCREEN_NOTOUCH  = 0x0001,
@@ -83,7 +83,7 @@
     ACONFIGURATION_TOUCHSCREEN_STYLUS  = 0x0002,
     /**
      * Touchscreen: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
+     * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
      * resource qualifier.
      */
     ACONFIGURATION_TOUCHSCREEN_FINGER  = 0x0003,
@@ -92,43 +92,43 @@
     ACONFIGURATION_DENSITY_DEFAULT = 0,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">ldpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">ldpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_LOW = 120,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">mdpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">mdpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_MEDIUM = 160,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">tvdpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">tvdpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_TV = 213,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">hdpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">hdpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_HIGH = 240,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xhdpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xhdpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_XHIGH = 320,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xxhdpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xxhdpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_XXHIGH = 480,
     /**
      * Density: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xxxhdpi</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xxxhdpi</a>
      * resource qualifier.
      */
     ACONFIGURATION_DENSITY_XXXHIGH = 640,
@@ -141,19 +141,19 @@
     ACONFIGURATION_KEYBOARD_ANY  = 0x0000,
     /**
      * Keyboard: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a>
      * resource qualifier.
      */
     ACONFIGURATION_KEYBOARD_NOKEYS  = 0x0001,
     /**
      * Keyboard: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a>
      * resource qualifier.
      */
     ACONFIGURATION_KEYBOARD_QWERTY  = 0x0002,
     /**
      * Keyboard: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
      * resource qualifier.
      */
     ACONFIGURATION_KEYBOARD_12KEY  = 0x0003,
@@ -162,25 +162,25 @@
     ACONFIGURATION_NAVIGATION_ANY  = 0x0000,
     /**
      * Navigation: value corresponding to the
-     * <a href="@@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a>
+     * <a href="@/guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a>
      * resource qualifier.
      */
     ACONFIGURATION_NAVIGATION_NONAV  = 0x0001,
     /**
      * Navigation: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a>
+     * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a>
      * resource qualifier.
      */
     ACONFIGURATION_NAVIGATION_DPAD  = 0x0002,
     /**
      * Navigation: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a>
+     * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a>
      * resource qualifier.
      */
     ACONFIGURATION_NAVIGATION_TRACKBALL  = 0x0003,
     /**
      * Navigation: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
+     * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
      * resource qualifier.
      */
     ACONFIGURATION_NAVIGATION_WHEEL  = 0x0004,
@@ -189,19 +189,19 @@
     ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000,
     /**
      * Keyboard availability: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a>
+     * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a>
      * resource qualifier.
      */
     ACONFIGURATION_KEYSHIDDEN_NO = 0x0001,
     /**
      * Keyboard availability: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a>
+     * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a>
      * resource qualifier.
      */
     ACONFIGURATION_KEYSHIDDEN_YES = 0x0002,
     /**
      * Keyboard availability: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyssoft</a>
+     * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyssoft</a>
      * resource qualifier.
      */
     ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003,
@@ -210,13 +210,13 @@
     ACONFIGURATION_NAVHIDDEN_ANY = 0x0000,
     /**
      * Navigation availability: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a>
+     * <a href="/guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a>
      * resource qualifier.
      */
     ACONFIGURATION_NAVHIDDEN_NO = 0x0001,
     /**
      * Navigation availability: value corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
+     * <a href="/guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
      * resource qualifier.
      */
     ACONFIGURATION_NAVHIDDEN_YES = 0x0002,
@@ -226,28 +226,28 @@
     /**
      * Screen size: value indicating the screen is at least
      * approximately 320x426 dp units, corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a>
      * resource qualifier.
      */
     ACONFIGURATION_SCREENSIZE_SMALL = 0x01,
     /**
      * Screen size: value indicating the screen is at least
      * approximately 320x470 dp units, corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a>
      * resource qualifier.
      */
     ACONFIGURATION_SCREENSIZE_NORMAL = 0x02,
     /**
      * Screen size: value indicating the screen is at least
      * approximately 480x640 dp units, corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
      * resource qualifier.
      */
     ACONFIGURATION_SCREENSIZE_LARGE = 0x03,
     /**
      * Screen size: value indicating the screen is at least
      * approximately 720x960 dp units, corresponding to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a>
      * resource qualifier.
      */
     ACONFIGURATION_SCREENSIZE_XLARGE = 0x04,
@@ -256,13 +256,13 @@
     ACONFIGURATION_SCREENLONG_ANY = 0x00,
     /**
      * Screen layout: value that corresponds to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a>
      * resource qualifier.
      */
     ACONFIGURATION_SCREENLONG_NO = 0x1,
     /**
      * Screen layout: value that corresponds to the
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
      * resource qualifier.
      */
     ACONFIGURATION_SCREENLONG_YES = 0x2,
@@ -275,13 +275,13 @@
     ACONFIGURATION_WIDE_COLOR_GAMUT_ANY = 0x00,
     /**
      * Wide color gamut: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no
+     * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no
      * nowidecg</a> resource qualifier specified.
      */
     ACONFIGURATION_WIDE_COLOR_GAMUT_NO = 0x1,
     /**
      * Wide color gamut: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">
+     * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">
      * widecg</a> resource qualifier specified.
      */
     ACONFIGURATION_WIDE_COLOR_GAMUT_YES = 0x2,
@@ -290,13 +290,13 @@
     ACONFIGURATION_HDR_ANY = 0x00,
     /**
      * HDR: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+     * <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">
      * lowdr</a> resource qualifier specified.
      */
     ACONFIGURATION_HDR_NO = 0x1,
     /**
      * HDR: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+     * <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">
      * highdr</a> resource qualifier specified.
      */
     ACONFIGURATION_HDR_YES = 0x2,
@@ -305,38 +305,38 @@
     ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">no
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">no
      * UI mode type</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">car</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">car</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">television</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">television</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_TELEVISION = 0x04,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_APPLIANCE = 0x05,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_WATCH = 0x06,
     /**
      * UI mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 0x07,
 
@@ -344,12 +344,12 @@
     ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
     /**
      * UI night mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NightQualifier">notnight</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#NightQualifier">notnight</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_NIGHT_NO = 0x1,
     /**
      * UI night mode: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NightQualifier">night</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#NightQualifier">night</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_NIGHT_YES = 0x2,
 
@@ -366,78 +366,78 @@
     ACONFIGURATION_LAYOUTDIR_ANY  = 0x00,
     /**
      * Layout direction: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldltr</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldltr</a> resource qualifier specified.
      */
     ACONFIGURATION_LAYOUTDIR_LTR  = 0x01,
     /**
      * Layout direction: value that corresponds to
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldrtl</a> resource qualifier specified.
+     * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldrtl</a> resource qualifier specified.
      */
     ACONFIGURATION_LAYOUTDIR_RTL  = 0x02,
 
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#MccQualifier">mcc</a>
+     * <a href="/guide/topics/resources/providing-resources.html#MccQualifier">mcc</a>
      * configuration.
      */
     ACONFIGURATION_MCC = 0x0001,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
+     * <a href="/guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
      * configuration.
      */
     ACONFIGURATION_MNC = 0x0002,
     /**
      * Bit mask for
-     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
+     * <a href="/guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
      * configuration.
      */
     ACONFIGURATION_LOCALE = 0x0004,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">touchscreen</a>
+     * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">touchscreen</a>
      * configuration.
      */
     ACONFIGURATION_TOUCHSCREEN = 0x0008,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">keyboard</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">keyboard</a>
      * configuration.
      */
     ACONFIGURATION_KEYBOARD = 0x0010,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyboardHidden</a>
+     * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyboardHidden</a>
      * configuration.
      */
     ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">navigation</a>
+     * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">navigation</a>
      * configuration.
      */
     ACONFIGURATION_NAVIGATION = 0x0040,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">orientation</a>
+     * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">orientation</a>
      * configuration.
      */
     ACONFIGURATION_ORIENTATION = 0x0080,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">density</a>
+     * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">density</a>
      * configuration.
      */
     ACONFIGURATION_DENSITY = 0x0100,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">screen size</a>
+     * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">screen size</a>
      * configuration.
      */
     ACONFIGURATION_SCREEN_SIZE = 0x0200,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#VersionQualifier">platform version</a>
+     * <a href="/guide/topics/resources/providing-resources.html#VersionQualifier">platform version</a>
      * configuration.
      */
     ACONFIGURATION_VERSION = 0x0400,
@@ -447,27 +447,27 @@
     ACONFIGURATION_SCREEN_LAYOUT = 0x0800,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">ui mode</a>
+     * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">ui mode</a>
      * configuration.
      */
     ACONFIGURATION_UI_MODE = 0x1000,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest screen width</a>
+     * <a href="/guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest screen width</a>
      * configuration.
      */
     ACONFIGURATION_SMALLEST_SCREEN_SIZE = 0x2000,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">layout direction</a>
+     * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">layout direction</a>
      * configuration.
      */
     ACONFIGURATION_LAYOUTDIR = 0x4000,
     ACONFIGURATION_SCREEN_ROUND = 0x8000,
     /**
      * Bit mask for
-     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a>
-     * and <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations.
+     * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a>
+     * and <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations.
      */
     ACONFIGURATION_COLOR_MODE = 0x10000,
     /**
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 3a87da0..d7e6e41 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -133,6 +133,8 @@
 /**
  * Create a new {@link AImageDecoder} from an {@link AAsset}.
  *
+ * Available since API level 30.
+ *
  * @param asset {@link AAsset} containing encoded image data. Client is still
  *              responsible for calling {@link AAsset_close} on it, which may be
  *              done after deleting the returned {@link AImageDecoder}.
@@ -162,6 +164,8 @@
 /**
  * Create a new {@link AImageDecoder} from a file descriptor.
  *
+ * Available since API level 30.
+ *
  * @param fd Seekable, readable, open file descriptor for encoded data.
  *           Client is still responsible for closing it, which may be done
  *           after deleting the returned {@link AImageDecoder}.
@@ -190,6 +194,8 @@
 /**
  * Create a new AImageDecoder from a buffer.
  *
+ * Available since API level 30.
+ *
  * @param buffer Pointer to encoded data. Must be valid for the entire time
  *               the {@link AImageDecoder} is used.
  * @param length Byte length of buffer.
@@ -217,12 +223,16 @@
 
 /**
  * Delete the AImageDecoder.
+ *
+ * Available since API level 30.
  */
 void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30);
 
 /**
  * Choose the desired output format.
  *
+ * Available since API level 30.
+ *
  * @param format {@link AndroidBitmapFormat} to use for the output.
  * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
  *         indicating the reason for the failure. On failure, the
@@ -247,6 +257,8 @@
  * Pass true to this method to leave them unpremultiplied. This has no effect on an
  * opaque image.
  *
+ * Available since API level 30.
+ *
  * @param unpremultipliedRequired Pass true to leave the pixels unpremultiplied.
  * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
  *         indicating the reason for the failure.
@@ -267,6 +279,8 @@
  * Ignored by {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support
  * an {@link ADataSpace}.
  *
+ * Available since API level 30.
+ *
  * @param dataspace The {@link ADataSpace} to decode into. An ADataSpace
  *                  specifies how to interpret the colors. By default,
  *                  AImageDecoder will decode into the ADataSpace specified by
@@ -292,6 +306,8 @@
  * specified by width and height, and the output image will be the size of the
  * crop rect.
  *
+ * Available since API level 30.
+ *
  * @param width Width of the output (prior to cropping).
  *              This will affect future calls to
  *              {@link AImageDecoder_getMinimumStride}, which will now return
@@ -319,6 +335,8 @@
  * others. This computes the most efficient target size to use to reach a
  * particular sampleSize.
  *
+ * Available since API level 30.
+ *
  * @param sampleSize A subsampling rate of the original image. Must be greater
  *                   than or equal to 1. A sampleSize of 2 means to skip every
  *                   other pixel/line, resulting in a width and height that are
@@ -344,6 +362,8 @@
  * the specified {@link ARect}. Clients will only need to allocate enough memory
  * for the cropped ARect.
  *
+ * Available since API level 30.
+ *
  * @param crop Rectangle describing a crop of the decode. It must be contained inside of
  *             the (possibly scaled, by {@link AImageDecoder_setTargetSize})
  *             image dimensions. This will affect future calls to
@@ -376,6 +396,8 @@
  *
  * This is owned by the {@link AImageDecoder} and will be destroyed when the
  * AImageDecoder is destroyed via {@link AImageDecoder_delete}.
+ *
+ * Available since API level 30.
  */
 const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(
         const AImageDecoder*) __INTRODUCED_IN(30);
@@ -385,6 +407,8 @@
  * pixel width of the output, unless {@link AImageDecoder_setTargetSize} is
  * used to choose a different size or {@link AImageDecoder_setCrop} is used to
  * set a crop rect.
+ *
+ * Available since API level 30.
  */
 int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
 
@@ -393,12 +417,16 @@
  * pixel height of the output, unless {@link AImageDecoder_setTargetSize} is
  * used to choose a different size or {@link AImageDecoder_setCrop} is used to
  * set a crop rect.
+ *
+ * Available since API level 30.
  */
 int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
 
 /**
  * Report the mimeType of the encoded image.
  *
+ * Available since API level 30.
+ *
  * @return a string literal describing the mime type.
  */
 const char* AImageDecoderHeaderInfo_getMimeType(
@@ -409,6 +437,8 @@
  * by default. {@link AImageDecoder} will try to choose one that is sensible
  * for the image and the system. Note that this does not indicate the
  * encoded format of the image.
+ *
+ * Available since API level 30.
  */
 int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(
         const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
@@ -419,6 +449,8 @@
  * {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE}. If the image may contain alpha,
  * this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}, because
  * {@link AImageDecoder_decodeImage} will premultiply pixels by default.
+ *
+ * Available since API level 30.
  */
 int AImageDecoderHeaderInfo_getAlphaFlags(
         const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
@@ -429,6 +461,8 @@
  * By default, {@link AImageDecoder_decodeImage} will not do any color
  * conversion.
  *
+ * Available since API level 30.
+ *
  * @return The {@link ADataSpace} representing the way the colors
  *         are encoded (or {@link ADATASPACE_UNKNOWN} if there is not a
  *         corresponding ADataSpace). This specifies how to interpret the colors
@@ -452,12 +486,16 @@
  *
  * If the output is scaled (via {@link AImageDecoder_setTargetSize}) and/or
  * cropped (via {@link AImageDecoder_setCrop}), this takes those into account.
+ *
+ * Available since API level 30.
  */
 size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30);
 
 /**
  * Decode the image into pixels, using the settings of the {@link AImageDecoder}.
  *
+ * Available since API level 30.
+ *
  * @param decoder Opaque object representing the decoder.
  * @param pixels On success, will be filled with the result
  *               of the decode. Must be large enough to hold |size| bytes.
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 3247fa1..83582d6 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -60,8 +60,6 @@
 extern "C" {
 #endif
 
-#if __ANDROID_API__ >= 30
-
 enum AThermalStatus {
     /** Error in thermal status. */
     ATHERMAL_STATUS_ERROR = -1,
@@ -111,36 +109,45 @@
  */
 typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status);
 
+#if __ANDROID_API__ >= 30
+
 /**
   * Acquire an instance of the thermal manager. This must be freed using
   * {@link AThermal_releaseManager}.
   *
+  * Available since API level 30.
+  *
   * @return manager instance on success, nullptr on failure.
- */
-AThermalManager* AThermal_acquireManager();
+  */
+AThermalManager* AThermal_acquireManager() __INTRODUCED_IN(30);
 
 /**
  * Release the thermal manager pointer acquired via
  * {@link AThermal_acquireManager}.
  *
- * @param manager The manager to be released.
+ * Available since API level 30.
  *
+ * @param manager The manager to be released.
  */
-void AThermal_releaseManager(AThermalManager *manager);
+void AThermal_releaseManager(AThermalManager *manager) __INTRODUCED_IN(30);
 
 /**
   * Gets the current thermal status.
   *
+  * Available since API level 30.
+  *
   * @param manager The manager instance to use to query the thermal status.
   * Acquired via {@link AThermal_acquireManager}.
   *
   * @return current thermal status, ATHERMAL_STATUS_ERROR on failure.
-*/
-AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager);
+  */
+AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) __INTRODUCED_IN(30);
 
 /**
  * Register the thermal status listener for thermal status change.
  *
+ * Available since API level 30.
+ *
  * @param manager The manager instance to use to register.
  * Acquired via {@link AThermal_acquireManager}.
  * @param callback The callback function to be called when thermal status updated.
@@ -152,11 +159,13 @@
  *         EPIPE if communication with the system service has failed.
  */
 int AThermal_registerThermalStatusListener(AThermalManager *manager,
-        AThermal_StatusCallback callback, void *data);
+        AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
 
 /**
  * Unregister the thermal status listener previously resgistered.
  *
+ * Available since API level 30.
+ *
  * @param manager The manager instance to use to unregister.
  * Acquired via {@link AThermal_acquireManager}.
  * @param callback The callback function to be called when thermal status updated.
@@ -168,8 +177,7 @@
  *         EPIPE if communication with the system service has failed.
  */
 int AThermal_unregisterThermalStatusListener(AThermalManager *manager,
-        AThermal_StatusCallback callback, void *data);
-
+        AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
 
 #endif  //  __ANDROID_API__ >= 30
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 2bf8ff7..1efd98b 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1493,7 +1493,7 @@
     int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix);
 
     if (graphicBuffer != nullptr) {
-        *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get());
+        *buffer = graphicBuffer->toAHardwareBuffer();
         AHardwareBuffer_acquire(*buffer);
     } else {
         *buffer = nullptr;
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 6366529..3afbabf 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -33,7 +33,7 @@
     DECLARE_META_INTERFACE(SurfaceComposerClient)
 
     // flags for createSurface()
-    enum { // (keep in sync with Surface.java)
+    enum { // (keep in sync with SurfaceControl.java)
         eHidden = 0x00000004,
         eDestroyBackbuffer = 0x00000020,
         eSecure = 0x00000080,
@@ -42,6 +42,7 @@
         eProtectedByApp = 0x00000800,
         eProtectedByDRM = 0x00001000,
         eCursorWindow = 0x00002000,
+        eNoColorFill = 0x00004000,
 
         eFXSurfaceBufferQueue = 0x00000000,
         eFXSurfaceEffect = 0x00020000,
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 596f15c..c3fbb60 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -41,9 +41,8 @@
     switch (renderEngineType) {
         case RenderEngineType::THREADED:
             ALOGD("Threaded RenderEngine with GLES Backend");
-            return renderengine::threaded::RenderEngineThreaded::create([&args]() {
-                return android::renderengine::gl::GLESRenderEngine::create(args);
-            });
+            return renderengine::threaded::RenderEngineThreaded::create(
+                    [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); });
         case RenderEngineType::GLES:
         default:
             ALOGD("RenderEngine with GLES Backend");
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index bb78895..f3edd3c 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -48,6 +48,8 @@
         "//apex_available:anyapex",
         "//apex_available:platform",
     ],
+    min_sdk_version: "apex_inherit",
+
     shared_libs: [
         "libutils",
     ],
diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h
index 0556ea1..1f1bcb3 100644
--- a/opengl/include/EGL/eglext_angle.h
+++ b/opengl/include/EGL/eglext_angle.h
@@ -4,12 +4,12 @@
 // found in the LICENSE file.
 //
 // eglext_angle.h: ANGLE modifications to the eglext.h header file.
-//   Currently we don't include this file directly, we patch eglext.h
-//   to include it implicitly so it is visible throughout our code.
 
 #ifndef INCLUDE_EGL_EGLEXT_ANGLE_
 #define INCLUDE_EGL_EGLEXT_ANGLE_
 
+#include <EGL/eglext.h>
+
 // clang-format off
 
 #ifndef EGL_ANGLE_robust_resource_initialization
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index e8d3684..3c76c62 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -102,11 +102,6 @@
         "libbacktrace",
         "libbase",
     ],
-    target: {
-        vendor: {
-            exclude_shared_libs: ["libgraphicsenv"],
-        },
-    },
 }
 
 cc_library_static {
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index fc066a5..85e2c15 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -17,27 +17,23 @@
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <EGL/Loader.h>
-
-#include <string>
-
-#include <dirent.h>
-#include <dlfcn.h>
+#include "EGL/Loader.h"
 
 #include <android-base/properties.h>
 #include <android/dlext.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
 #include <log/log.h>
 #include <utils/Timers.h>
-
-#ifndef __ANDROID_VNDK__
-#include <graphicsenv/GraphicsEnv.h>
-#endif
 #include <vndksupport/linker.h>
 
+#include <string>
+
+#include "EGL/eglext_angle.h"
 #include "egl_platform_entries.h"
 #include "egl_trace.h"
 #include "egldefs.h"
-#include <EGL/eglext_angle.h>
 
 namespace android {
 
@@ -159,13 +155,11 @@
         return true;
     }
 
-#ifndef __ANDROID_VNDK__
     // Return true if updated driver namespace is set.
     ns = android::GraphicsEnv::getInstance().getDriverNamespace();
     if (ns) {
         return true;
     }
-#endif
 
     return false;
 }
@@ -569,7 +563,7 @@
 
 Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) {
     ATRACE_CALL();
-#ifndef __ANDROID_VNDK__
+
     android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace();
     if (!ns) {
         return nullptr;
@@ -599,9 +593,6 @@
         hnd->set(dso, GLESv2);
     }
     return hnd;
-#else
-    return nullptr;
-#endif
 }
 
 Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 3b1cf71..8c6f284 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -19,25 +19,22 @@
 
 #include "egl_display.h"
 
+#include <SurfaceFlingerProperties.h>
+#include <android-base/properties.h>
+#include <android/dlext.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+
 #include "../egl_impl.h"
-
-#include <EGL/eglext_angle.h>
-#include <private/EGL/display.h>
-
+#include "EGL/eglext_angle.h"
 #include "Loader.h"
 #include "egl_angle_platform.h"
 #include "egl_cache.h"
 #include "egl_object.h"
 #include "egl_tls.h"
-
-#include <SurfaceFlingerProperties.h>
-#include <android-base/properties.h>
-#include <android/dlext.h>
-#include <dlfcn.h>
-#include <graphicsenv/GraphicsEnv.h>
-
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
+#include "private/EGL/display.h"
 
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 99283be..570d36b 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -18,36 +18,32 @@
 
 #include "egl_platform_entries.h"
 
-#include <ctype.h>
-#include <dlfcn.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <EGL/eglext_angle.h>
-
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android/hardware_buffer.h>
-#include <graphicsenv/GraphicsEnv.h>
-#include <private/android/AHardwareBufferHelpers.h>
-
+#include <ctype.h>
 #include <cutils/compiler.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
 #include <log/log.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include <condition_variable>
 #include <deque>
 #include <mutex>
-#include <unordered_map>
 #include <string>
 #include <thread>
+#include <unordered_map>
 
 #include "../egl_impl.h"
-
+#include "EGL/egl.h"
+#include "EGL/eglext.h"
+#include "EGL/eglext_angle.h"
 #include "egl_display.h"
-#include "egl_object.h"
 #include "egl_layers.h"
+#include "egl_object.h"
 #include "egl_tls.h"
 #include "egl_trace.h"
 
@@ -2248,15 +2244,8 @@
 }
 
 EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) {
-    // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
-    // this function cannot be implemented when this libEGL is built for
-    // vendors.
-#ifndef __ANDROID_VNDK__
     if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
     return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
-#else
-    return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
-#endif
 }
 
 // ----------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 45e67f7..8a282e2 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -820,7 +820,7 @@
 
     status_t err(NO_ERROR);
     // If the min period or min timeout has changed since the last batch call, call batch.
-    if (prevBestBatchParams != info.bestBatchParams) {
+    if (prevBestBatchParams != info.bestBatchParams && info.numActiveClients() > 0) {
         ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle,
                  info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
         err = checkReturnAndGetStatus(mSensors->batch(
@@ -890,14 +890,13 @@
         Info& info = mActivationCount.editValueAt(i);
 
         if (info.hasBatchParamsForIdent(ident)) {
-            if (updateBatchParamsLocked(handle, info) != NO_ERROR) {
-                bool enable = info.numActiveClients() == 0 && info.isActive;
-                bool disable = info.numActiveClients() > 0 && !info.isActive;
+            updateBatchParamsLocked(handle, info);
+            bool disable = info.numActiveClients() == 0 && info.isActive;
+            bool enable = info.numActiveClients() > 0 && !info.isActive;
 
-                if ((enable || disable) &&
-                    doActivateHardwareLocked(handle, enable) == NO_ERROR) {
-                    info.isActive = enable;
-                }
+            if ((enable || disable) &&
+                doActivateHardwareLocked(handle, enable) == NO_ERROR) {
+                info.isActive = enable;
             }
         }
     }
diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp
index 0dcf8c0..52213cf 100644
--- a/services/sensorservice/SensorDeviceUtils.cpp
+++ b/services/sensorservice/SensorDeviceUtils.cpp
@@ -40,22 +40,12 @@
     switch ((SensorTypeV2_1)event->type) {
         case SensorTypeV2_1::ACCELEROMETER:
         case SensorTypeV2_1::MAGNETIC_FIELD:
-        case SensorTypeV2_1::ORIENTATION:
         case SensorTypeV2_1::GYROSCOPE:
-        case SensorTypeV2_1::GRAVITY:
-        case SensorTypeV2_1::LINEAR_ACCELERATION:
         case SensorTypeV2_1::MAGNETIC_FIELD_UNCALIBRATED:
         case SensorTypeV2_1::GYROSCOPE_UNCALIBRATED:
         case SensorTypeV2_1::ACCELEROMETER_UNCALIBRATED:
             axes = 3;
             break;
-        case SensorTypeV2_1::GAME_ROTATION_VECTOR:
-            axes = 4;
-            break;
-        case SensorTypeV2_1::ROTATION_VECTOR:
-        case SensorTypeV2_1::GEOMAGNETIC_ROTATION_VECTOR:
-            axes = 5;
-            break;
         case SensorTypeV2_1::DEVICE_ORIENTATION:
         case SensorTypeV2_1::LIGHT:
         case SensorTypeV2_1::PRESSURE:
@@ -77,11 +67,8 @@
         case SensorTypeV2_1::HINGE_ANGLE:
             axes = 1;
             break;
-        case SensorTypeV2_1::POSE_6DOF:
-            axes = 15;
-            break;
         default:
-            // No other sensors have data that needs to be rounded.
+            // No other sensors have data that needs to be quantized.
             break;
     }
 
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index e5b94e4..07be791 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -31,6 +31,7 @@
 #include "SurfaceInterceptor.h"
 
 #include "FrameTracer/FrameTracer.h"
+#include "Scheduler/LayerHistory.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android {
@@ -399,7 +400,8 @@
     // Add this buffer from our internal queue tracker
     { // Autolock scope
         const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
-        mFlinger->mScheduler->recordLayerHistory(this, presentTime);
+        mFlinger->mScheduler->recordLayerHistory(this, presentTime,
+                                                 LayerHistory::LayerUpdateType::Buffer);
 
         Mutex::Autolock lock(mQueueItemLock);
         // Reset the frame number tracker when we receive the first buffer after
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index a1ed6d7..4f8fc41 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -283,7 +283,8 @@
     desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
     mCurrentState.desiredPresentTime = desiredPresentTime;
 
-    mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime);
+    mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime,
+                                             LayerHistory::LayerUpdateType::Buffer);
 
     addFrameEvent(acquireFence, postTime, desiredPresentTime);
     return true;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 14f2289..0b32402 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -119,6 +119,13 @@
     mCurrentState.treeHasFrameRateVote = false;
     mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
 
+    if (args.flags & ISurfaceComposerClient::eNoColorFill) {
+        // Set an invalid color so there is no color fill.
+        mCurrentState.color.r = -1.0_hf;
+        mCurrentState.color.g = -1.0_hf;
+        mCurrentState.color.b = -1.0_hf;
+    }
+
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
 
@@ -848,11 +855,14 @@
         }
     }
 
-    // If we still have pending updates, wake SurfaceFlinger back up and point
-    // it at this layer so we can process them
+    // If we still have pending updates, we need to ensure SurfaceFlinger
+    // will keep calling doTransaction, and so we set the transaction flags.
+    // However, our pending states won't clear until a frame is available,
+    // and so there is no need to specifically trigger a wakeup. Rather
+    // we set the flags and wait for something else to wake us up.
     if (!mPendingStates.empty()) {
         setTransactionFlags(eTransactionNeeded);
-        mFlinger->setTransactionFlags(eTraversalNeeded);
+        mFlinger->setTransactionFlagsNoWake(eTraversalNeeded);
     }
 
     mCurrentState.modified = false;
@@ -1392,7 +1402,8 @@
     }
 
     // Activate the layer in Scheduler's LayerHistory
-    mFlinger->mScheduler->recordLayerHistory(this, systemTime());
+    mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
+                                             LayerHistory::LayerUpdateType::SetFrameRate);
 
     mCurrentState.sequence++;
     mCurrentState.frameRate = frameRate;
@@ -2111,7 +2122,9 @@
             // but a transform matrix can define horizontal and vertical scales.
             // Let's take the average between both of them and pass into the shader, practically we
             // never do this type of transformation on windows anyway.
-            parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
+            auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]);
+            auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]);
+            parentState.radius *= (scaleX + scaleY) / 2.0f;
             return parentState;
         }
     }
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 8958d9a..2925109 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -79,7 +79,8 @@
     mLayerInfos.emplace_back(layer, std::move(info));
 }
 
-void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
+void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
+                          LayerUpdateType /*updateType*/) {
     std::lock_guard lock(mLock);
 
     const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index d5bebf6..acd76b0 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -52,10 +52,18 @@
     // Sets the display size. Client is responsible for synchronization.
     virtual void setDisplayArea(uint32_t displayArea) = 0;
 
+    // Sets whether a config change is pending to be applied
     virtual void setConfigChangePending(bool pending) = 0;
 
+    // Represents which layer activity is recorded
+    enum class LayerUpdateType {
+        Buffer,       // a new buffer queued
+        AnimationTX,  // a new transaction with eAnimation flag set
+        SetFrameRate, // setFrameRate API was called
+    };
+
     // Marks the layer as active, and records the given state to its history.
-    virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0;
+    virtual void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) = 0;
 
     using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
 
@@ -83,7 +91,7 @@
     void setConfigChangePending(bool /*pending*/) override {}
 
     // Marks the layer as active, and records the given state to its history.
-    void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
+    void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
 
     // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
     android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
@@ -141,7 +149,7 @@
     void setConfigChangePending(bool pending) override { mConfigChangePending = pending; }
 
     // Marks the layer as active, and records the given state to its history.
-    void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
+    void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
 
     // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
     android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index 6570b1a..316000c 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -99,7 +99,8 @@
     mLayerInfos.emplace_back(layer, std::move(info));
 }
 
-void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
+void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
+                            LayerUpdateType updateType) {
     std::lock_guard lock(mLock);
 
     const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
@@ -107,7 +108,7 @@
     LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
 
     const auto& info = it->second;
-    info->setLastPresentTime(presentTime, now, mConfigChangePending);
+    info->setLastPresentTime(presentTime, now, updateType, mConfigChangePending);
 
     // Activate layer if inactive.
     if (const auto end = activeLayers().end(); it >= end) {
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index 25dca39..82da007 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -35,18 +35,24 @@
         mLayerVote({defaultVote, 0.0f}) {}
 
 void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
-                                     bool pendingConfigChange) {
+                                     LayerUpdateType updateType, bool pendingConfigChange) {
     lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
 
     mLastUpdatedTime = std::max(lastPresentTime, now);
-
-    FrameTimeData frameTime = {.presetTime = lastPresentTime,
-                               .queueTime = mLastUpdatedTime,
-                               .pendingConfigChange = pendingConfigChange};
-
-    mFrameTimes.push_back(frameTime);
-    if (mFrameTimes.size() > HISTORY_SIZE) {
-        mFrameTimes.pop_front();
+    switch (updateType) {
+        case LayerUpdateType::AnimationTX:
+            mLastAnimationTime = std::max(lastPresentTime, now);
+            break;
+        case LayerUpdateType::SetFrameRate:
+        case LayerUpdateType::Buffer:
+            FrameTimeData frameTime = {.presetTime = lastPresentTime,
+                                       .queueTime = mLastUpdatedTime,
+                                       .pendingConfigChange = pendingConfigChange};
+            mFrameTimes.push_back(frameTime);
+            if (mFrameTimes.size() > HISTORY_SIZE) {
+                mFrameTimes.pop_front();
+            }
+            break;
     }
 }
 
@@ -81,6 +87,10 @@
     return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
 }
 
+bool LayerInfoV2::isAnimating(nsecs_t now) const {
+    return mLastAnimationTime >= getActiveLayerThreshold(now);
+}
+
 bool LayerInfoV2::hasEnoughDataForHeuristic() const {
     // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
     if (mFrameTimes.size() < 2) {
@@ -190,6 +200,11 @@
         return {mLayerVote.type, mLayerVote.fps};
     }
 
+    if (isAnimating(now)) {
+        ALOGV("%s is animating", mName.c_str());
+        return {LayerHistory::LayerVoteType::Max, 0};
+    }
+
     if (!isFrequent(now)) {
         ALOGV("%s is infrequent", mName.c_str());
         return {LayerHistory::LayerVoteType::Min, 0};
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index 5f50d5a..9e6783f 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -43,6 +43,8 @@
 
 // Stores history of present times and refresh rates for a layer.
 class LayerInfoV2 {
+    using LayerUpdateType = LayerHistory::LayerUpdateType;
+
     // Layer is considered frequent if the earliest value in the window of most recent present times
     // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
     // favor of a low refresh rate.
@@ -63,7 +65,8 @@
     // Records the last requested present time. It also stores information about when
     // the layer was last updated. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
-    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, bool pendingConfigChange);
+    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
+                            bool pendingConfigChange);
 
     // Sets an explicit layer vote. This usually comes directly from the application via
     // ANativeWindow_setFrameRate API
@@ -107,6 +110,7 @@
     };
 
     bool isFrequent(nsecs_t now) const;
+    bool isAnimating(nsecs_t now) const;
     bool hasEnoughDataForHeuristic() const;
     std::optional<float> calculateRefreshRateIfPossible();
     std::pair<nsecs_t, bool> calculateAverageFrameTime() const;
@@ -121,6 +125,8 @@
 
     nsecs_t mLastUpdatedTime = 0;
 
+    nsecs_t mLastAnimationTime = 0;
+
     float mLastReportedRefreshRate = 0.0f;
 
     // Holds information about the layer vote
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 7c2af23..e250bbf 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -62,7 +62,7 @@
 
 namespace android {
 
-std::unique_ptr<DispSync> createDispSync() {
+std::unique_ptr<DispSync> createDispSync(bool supportKernelTimer) {
     // TODO (140302863) remove this and use the vsync_reactor system.
     if (property_get_bool("debug.sf.vsync_reactor", true)) {
         // TODO (144707443) tune Predictor tunables.
@@ -90,7 +90,7 @@
         static constexpr size_t pendingFenceLimit = 20;
         return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(),
                                                          std::move(dispatch), std::move(tracker),
-                                                         pendingFenceLimit);
+                                                         pendingFenceLimit, supportKernelTimer);
     } else {
         return std::make_unique<impl::DispSync>("SchedulerDispSync",
                                                 sysprop::running_without_sync_framework(true));
@@ -101,9 +101,9 @@
                      const scheduler::RefreshRateConfigs& refreshRateConfig,
                      ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
                      bool useContentDetection)
-      : mPrimaryDispSync(createDispSync()),
+      : mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
+        mPrimaryDispSync(createDispSync(mSupportKernelTimer)),
         mEventControlThread(new impl::EventControlThread(std::move(function))),
-        mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
         mSchedulerCallback(schedulerCallback),
         mRefreshRateConfigs(refreshRateConfig),
         mUseContentDetection(useContentDetection),
@@ -151,9 +151,9 @@
                      const scheduler::RefreshRateConfigs& configs,
                      ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
                      bool useContentDetection)
-      : mPrimaryDispSync(std::move(primaryDispSync)),
+      : mSupportKernelTimer(false),
+        mPrimaryDispSync(std::move(primaryDispSync)),
         mEventControlThread(std::move(eventControlThread)),
-        mSupportKernelTimer(false),
         mSchedulerCallback(schedulerCallback),
         mRefreshRateConfigs(configs),
         mUseContentDetection(useContentDetection),
@@ -417,9 +417,10 @@
     }
 }
 
-void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime) {
+void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
+                                   LayerHistory::LayerUpdateType updateType) {
     if (mLayerHistory) {
-        mLayerHistory->record(layer, presentTime, systemTime());
+        mLayerHistory->record(layer, presentTime, systemTime(), updateType);
     }
 }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 066e9ca..ebee9e3 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -39,6 +39,7 @@
 namespace android {
 
 using namespace std::chrono_literals;
+using scheduler::LayerHistory;
 
 class DispSync;
 class FenceTime;
@@ -118,7 +119,7 @@
 
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
-    void recordLayerHistory(Layer*, nsecs_t presentTime);
+    void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType);
     void setConfigChangePending(bool pending);
 
     // Detects content using layer history, and selects a matching refresh rate.
@@ -207,14 +208,14 @@
 
     std::atomic<nsecs_t> mLastResyncTime = 0;
 
+    // Whether to use idle timer callbacks that support the kernel timer.
+    const bool mSupportKernelTimer;
+
     std::unique_ptr<DispSync> mPrimaryDispSync;
     std::unique_ptr<EventControlThread> mEventControlThread;
 
     // Used to choose refresh rate if content detection is enabled.
-    std::unique_ptr<scheduler::LayerHistory> mLayerHistory;
-
-    // Whether to use idle timer callbacks that support the kernel timer.
-    const bool mSupportKernelTimer;
+    std::unique_ptr<LayerHistory> mLayerHistory;
 
     // Timer that records time between requests for next vsync.
     std::optional<scheduler::OneShotTimer> mIdleTimer;
@@ -236,7 +237,7 @@
         TimerState displayPowerTimer = TimerState::Expired;
 
         std::optional<HwcConfigIndexType> configId;
-        scheduler::LayerHistory::Summary contentRequirements;
+        LayerHistory::Summary contentRequirements;
 
         bool isDisplayPowerStateNormal = true;
     } mFeatures GUARDED_BY(mFeatureStateLock);
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 5f0c9ce..16d102c 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -56,14 +56,16 @@
 };
 
 VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
-                           std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit)
+                           std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit,
+                           bool supportKernelIdleTimer)
       : mClock(std::move(clock)),
         mTracker(std::move(tracker)),
         mDispatch(std::move(dispatch)),
         mPendingLimit(pendingFenceLimit),
         mPredictedVsyncTracer(property_get_bool("debug.sf.show_predicted_vsync", false)
                                       ? std::make_unique<PredictedVsyncTracer>(*mDispatch)
-                                      : nullptr) {}
+                                      : nullptr),
+        mSupportKernelIdleTimer(supportKernelIdleTimer) {}
 
 VSyncReactor::~VSyncReactor() = default;
 
@@ -249,7 +251,8 @@
     ATRACE_INT64("VSR-setPeriod", period);
     std::lock_guard lk(mMutex);
     mLastHwVsync.reset();
-    if (period == getPeriod()) {
+
+    if (!mSupportKernelIdleTimer && period == getPeriod()) {
         endPeriodTransition();
     } else {
         startPeriodTransition(period);
@@ -275,6 +278,11 @@
         return false;
     }
 
+    if (mSupportKernelIdleTimer) {
+        // Clear out the Composer-provided period and use the allowance logic below
+        HwcVsyncPeriod = {};
+    }
+
     auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
     static constexpr int allowancePercent = 10;
     static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 31ddf5a..265d89c 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -36,7 +36,8 @@
 class VSyncReactor : public android::DispSync {
 public:
     VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
-                 std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit);
+                 std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit,
+                 bool supportKernelIdleTimer);
     ~VSyncReactor();
 
     bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final;
@@ -89,6 +90,7 @@
             GUARDED_BY(mMutex);
 
     const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
+    const bool mSupportKernelIdleTimer = false;
 };
 
 class SystemClock : public Clock {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fdb85df..c58e3a4 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -115,6 +115,7 @@
 #include "Scheduler/DispSyncSource.h"
 #include "Scheduler/EventControlThread.h"
 #include "Scheduler/EventThread.h"
+#include "Scheduler/LayerHistory.h"
 #include "Scheduler/MessageQueue.h"
 #include "Scheduler/PhaseOffsets.h"
 #include "Scheduler/Scheduler.h"
@@ -3230,6 +3231,10 @@
     return old;
 }
 
+uint32_t SurfaceFlinger::setTransactionFlagsNoWake(uint32_t flags) {
+    return mTransactionFlags.fetch_or(flags);
+}
+
 bool SurfaceFlinger::flushTransactionQueues() {
     // to prevent onHandleDestroyed from being called while the lock is held,
     // we must keep a copy of the transactions (specifically the composer
@@ -3389,6 +3394,12 @@
     for (const ComposerState& state : states) {
         clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged,
                                                  listenerCallbacksWithSurfaces);
+        if ((flags & eAnimation) && state.state.surface) {
+            if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
+                mScheduler->recordLayerHistory(layer.get(), desiredPresentTime,
+                                               LayerHistory::LayerUpdateType::AnimationTX);
+            }
+        }
     }
 
     for (const auto& listenerCallback : listenerCallbacksWithSurfaces) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d9b40f4..d7acce1 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -628,6 +628,12 @@
     uint32_t peekTransactionFlags();
     // Can only be called from the main thread or with mStateLock held
     uint32_t setTransactionFlags(uint32_t flags);
+    // Set the transaction flags, but don't trigger a wakeup! We use this cases where
+    // there are still pending transactions but we know they won't be ready until a frame
+    // arrives from a different layer. So we need to ensure we performTransaction from invalidate
+    // but there is no need to try and wake up immediately to do it. Rather we rely on
+    // onFrameAvailable to wake us up.
+    uint32_t setTransactionFlagsNoWake(uint32_t flags);
     uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
     void commitTransaction() REQUIRES(mStateLock);
     void commitOffscreenLayers();
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 94ebe89..fe2af80 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -22,6 +22,7 @@
         "Credentials_test.cpp",
         "DereferenceSurfaceControl_test.cpp",
         "DisplayConfigs_test.cpp",
+        "EffectLayer_test.cpp",
         "InvalidHandles_test.cpp",
         "LayerCallback_test.cpp",
         "LayerRenderTypeTransaction_test.cpp",
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
new file mode 100644
index 0000000..3dca391
--- /dev/null
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class EffectLayerTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        ASSERT_FALSE(display == nullptr);
+
+        mParentLayer = createColorLayer("Parent layer", Color::RED);
+        asTransaction([&](Transaction& t) {
+            t.setDisplayLayerStack(display, 0);
+            t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
+            t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+        });
+    }
+
+    virtual void TearDown() {
+        LayerTransactionTest::TearDown();
+        mParentLayer = 0;
+    }
+
+    sp<SurfaceControl> mParentLayer;
+};
+
+TEST_F(EffectLayerTest, DefaultEffectLayerHasSolidBlackFill) {
+    sp<SurfaceControl> effectLayer =
+            mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+                                   PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect,
+                                   mParentLayer.get());
+
+    EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.show(effectLayer);
+    });
+
+    {
+        SCOPED_TRACE("Default effect Layer has solid black fill");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 400, 400), Color::BLACK);
+    }
+}
+
+TEST_F(EffectLayerTest, EffectLayerWithNoFill) {
+    sp<SurfaceControl> effectLayer =
+            mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+                                   PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceEffect |
+                                           ISurfaceComposerClient::eNoColorFill,
+                                   mParentLayer.get());
+
+    EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.show(effectLayer);
+    });
+
+    {
+        SCOPED_TRACE("Effect layer with nofill option is transparent");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 400, 400), Color::RED);
+    }
+}
+
+TEST_F(EffectLayerTest, EffectLayerCanSetColor) {
+    sp<SurfaceControl> effectLayer =
+            mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+                                   PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceEffect |
+                                           ISurfaceComposerClient::eNoColorFill,
+                                   mParentLayer.get());
+
+    EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.setColor(effectLayer,
+                   half3{Color::GREEN.r / 255.0f, Color::GREEN.g / 255.0f,
+                         Color::GREEN.b / 255.0f});
+        t.show(effectLayer);
+    });
+
+    {
+        SCOPED_TRACE("Effect Layer can set color");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 400, 400), Color::GREEN);
+    }
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index d666d7e..7d4314f 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -220,6 +220,51 @@
         shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
         shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
         shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::BLACK);
+        // Solid center
+        shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+                               size / 2 + testArea / 2, size / 2 + testArea / 2),
+                          Color::RED);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusRotated) {
+    sp<SurfaceControl> parent;
+    sp<SurfaceControl> child;
+    const uint8_t size = 64;
+    const uint8_t testArea = 4;
+    const float cornerRadius = 20.0f;
+    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, size, size));
+    ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
+
+    auto transaction = Transaction()
+                               .setCornerRadius(parent, cornerRadius)
+                               .setCrop_legacy(parent, Rect(0, 0, size, size))
+                               .reparent(child, parent->getHandle())
+                               .setPosition(child, 0, size)
+                               // Rotate by half PI
+                               .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f);
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        transaction.setCrop_legacy(parent, Rect(0, 0, size, size));
+    } else {
+        transaction.setFrame(parent, Rect(0, 0, size, size));
+    }
+    transaction.apply();
+
+    {
+        const uint8_t bottom = size - 1;
+        const uint8_t right = size - 1;
+        auto shot = getScreenCapture();
+        // Edges are transparent
+        shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+        shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
+        shot->expectColor(Rect(0, bottom - testArea, testArea, bottom - testArea), Color::BLACK);
+        shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK);
+        // Solid center
+        shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+                               size / 2 + testArea / 2, size / 2 + testArea / 2),
+                          Color::GREEN);
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 71d17a9..cae317b 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -96,14 +96,14 @@
 
     // no layers are returned if active layers have insufficient history.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
-        history().record(layer.get(), 0, mTime);
+        history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
         ASSERT_TRUE(history().summarize(mTime).empty());
         EXPECT_EQ(1, activeLayerCount());
     }
 
     // High FPS is returned once enough history has been recorded.
     for (int i = 0; i < 10; i++) {
-        history().record(layer.get(), 0, mTime);
+        history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
         ASSERT_EQ(1, history().summarize(mTime).size());
         EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime)[0].desiredRefreshRate);
         EXPECT_EQ(1, activeLayerCount());
@@ -121,7 +121,7 @@
 
     nsecs_t time = mTime;
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -155,7 +155,7 @@
 
     // layer1 is active but infrequent.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer1.get(), time, time);
+        history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
     }
 
@@ -166,12 +166,12 @@
 
     // layer2 is frequent and has high refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time);
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
     // layer1 is still active but infrequent.
-    history().record(layer1.get(), time, time);
+    history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
 
     ASSERT_EQ(2, history().summarize(time).size());
     EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
@@ -182,7 +182,7 @@
     // layer1 is no longer active.
     // layer2 is frequent and has low refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time);
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -196,10 +196,10 @@
     constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
         if (i % RATIO == 0) {
-            history().record(layer2.get(), time, time);
+            history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         }
 
-        history().record(layer3.get(), time, time);
+        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -209,7 +209,7 @@
     EXPECT_EQ(2, frequentLayerCount(time));
 
     // layer3 becomes recently active.
-    history().record(layer3.get(), time, time);
+    history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(2, history().summarize(time).size());
     EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
     EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
@@ -228,7 +228,7 @@
     // layer2 still has low refresh rate.
     // layer3 becomes inactive.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time);
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -246,7 +246,7 @@
 
     // layer3 becomes active and has high refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer3.get(), time, time);
+        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index dc705ed..f376b4a 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -17,6 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "LayerHistoryTestV2"
 
+#include <Layer.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -60,6 +61,13 @@
                              [now](const auto& pair) { return pair.second->isFrequent(now); });
     }
 
+    auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+        const auto& infos = history().mLayerInfos;
+        return std::count_if(infos.begin(),
+                             infos.begin() + static_cast<long>(history().mActiveLayersEnd),
+                             [now](const auto& pair) { return pair.second->isAnimating(now); });
+    }
+
     void setLayerInfoVote(Layer* layer,
                           LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
         for (auto& [weak, info] : history().mLayerInfos) {
@@ -109,7 +117,7 @@
 
     // Max returned if active layers have insufficient history.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
-        history().record(layer.get(), 0, time);
+        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
         ASSERT_EQ(1, history().summarize(time).size());
         EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
@@ -117,7 +125,7 @@
 
     // Max is returned since we have enough history but there is no timestamp votes.
     for (int i = 0; i < 10; i++) {
-        history().record(layer.get(), 0, time);
+        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
         ASSERT_EQ(1, history().summarize(time).size());
         EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
@@ -134,7 +142,7 @@
 
     nsecs_t time = systemTime();
 
-    history().record(layer.get(), 0, time);
+    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
     auto summary = history().summarize(time);
     ASSERT_EQ(1, history().summarize(time).size());
     // Layer is still considered inactive so we expect to get Min
@@ -158,7 +166,7 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -181,7 +189,7 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -208,7 +216,7 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -236,7 +244,7 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -264,7 +272,7 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -296,7 +304,7 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -340,7 +348,7 @@
 
     // layer1 is active but infrequent.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer1.get(), time, time);
+        history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
     }
 
@@ -351,12 +359,12 @@
 
     // layer2 is frequent and has high refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time);
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
     // layer1 is still active but infrequent.
-    history().record(layer1.get(), time, time);
+    history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
 
     ASSERT_EQ(2, history().summarize(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
@@ -368,7 +376,7 @@
     // layer1 is no longer active.
     // layer2 is frequent and has low refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time);
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -383,10 +391,10 @@
     constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
         if (i % RATIO == 0) {
-            history().record(layer2.get(), time, time);
+            history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         }
 
-        history().record(layer3.get(), time, time);
+        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -398,7 +406,7 @@
     EXPECT_EQ(2, frequentLayerCount(time));
 
     // layer3 becomes recently active.
-    history().record(layer3.get(), time, time);
+    history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(2, history().summarize(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
     EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
@@ -422,7 +430,7 @@
     // layer2 still has low refresh rate.
     // layer3 becomes inactive.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time);
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -441,7 +449,7 @@
 
     // layer3 becomes active and has high refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer3.get(), time, time);
+        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -470,7 +478,7 @@
 
     // the very first updates makes the layer frequent
     for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
         EXPECT_EQ(1, layerCount());
@@ -481,7 +489,7 @@
     }
 
     // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
-    history().record(layer.get(), time, time);
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
     time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
     EXPECT_EQ(1, layerCount());
@@ -495,7 +503,7 @@
 
     // Now event if we post a quick few frame we should stay infrequent
     for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
-        history().record(layer.get(), time, time);
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
 
         EXPECT_EQ(1, layerCount());
@@ -506,7 +514,7 @@
     }
 
     // More quick frames will get us to frequent again
-    history().record(layer.get(), time, time);
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
     time += HI_FPS_PERIOD;
 
     EXPECT_EQ(1, layerCount());
@@ -533,8 +541,9 @@
     nsecs_t time = systemTime();
 
     // Post a buffer to the layers to make them active
-    history().record(explicitVisiblelayer.get(), time, time);
-    history().record(explicitInvisiblelayer.get(), time, time);
+    history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(explicitInvisiblelayer.get(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
 
     EXPECT_EQ(2, layerCount());
     ASSERT_EQ(1, history().summarize(time).size());
@@ -545,6 +554,52 @@
     EXPECT_EQ(2, frequentLayerCount(time));
 }
 
+TEST_F(LayerHistoryTestV2, infrequentAnimatingLayer) {
+    auto layer = createLayer();
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // layer is active but infrequent.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // another update with the same cadence keep in infrequent
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // an update as animation will immediately vote for Max
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(1, animatingLayerCount(time));
+}
+
 class LayerHistoryTestV2Parameterized
       : public LayerHistoryTestV2,
         public testing::WithParamInterface<std::chrono::nanoseconds> {};
@@ -565,8 +620,10 @@
     const nsecs_t startTime = systemTime();
 
     const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
-    history().record(heuristicLayer.get(), startTime, startTime);
-    history().record(infrequentLayer.get(), startTime, startTime);
+    history().record(heuristicLayer.get(), startTime, startTime,
+                     LayerHistory::LayerUpdateType::Buffer);
+    history().record(infrequentLayer.get(), startTime, startTime,
+                     LayerHistory::LayerUpdateType::Buffer);
 
     nsecs_t time = startTime;
     nsecs_t lastInfrequentUpdate = startTime;
@@ -574,13 +631,14 @@
     int infrequentLayerUpdates = 0;
     while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
         time += heuristicUpdateDelta.count();
-        history().record(heuristicLayer.get(), time, time);
+        history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
 
         if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
             ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
                   totalInfrequentLayerUpdates);
             lastInfrequentUpdate = time;
-            history().record(infrequentLayer.get(), time, time);
+            history().record(infrequentLayer.get(), time, time,
+                             LayerHistory::LayerUpdateType::Buffer);
             infrequentLayerUpdates++;
         }
 
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 3f14d65..ccbd17f 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -157,7 +157,8 @@
             mMockClock(std::make_shared<NiceMock<MockClock>>()),
             mReactor(std::make_unique<ClockWrapper>(mMockClock),
                      std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
-                     std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {
+                     std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit,
+                     false /* supportKernelIdleTimer */) {
         ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
         ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
     }
@@ -663,6 +664,28 @@
     EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 }
 
+TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
+    // Create a reactor which supports the kernel idle timer
+    auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock),
+                                    std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
+                                    std::make_unique<VSyncTrackerWrapper>(mMockTracker),
+                                    kPendingLimit, true /* supportKernelIdleTimer */);
+
+    bool periodFlushed = true;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+    idleReactor.setIgnorePresentFences(true);
+
+    nsecs_t const newPeriod = 5000;
+    idleReactor.setPeriod(newPeriod);
+
+    EXPECT_TRUE(idleReactor.addResyncSample(0, 0, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_FALSE(idleReactor.addResyncSample(newPeriod, 0, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+
+    EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
 using VSyncReactorDeathTest = VSyncReactorTest;
 TEST_F(VSyncReactorDeathTest, invalidRemoval) {
     mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 04a8e6a..db27bd9 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -44,6 +44,18 @@
 // -------------------------------------------------------------------------------------------------
 
 template <class T>
+HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) {
+    if (cache.has_value()) {
+        return HalResult<T>::ok(cache.value());
+    }
+    HalResult<T> ret = loadFn();
+    if (ret.isOk()) {
+        cache.emplace(ret.value());
+    }
+    return ret;
+}
+
+template <class T>
 bool isStaticCastValid(Effect effect) {
     T castEffect = static_cast<T>(effect);
     auto iter = hardware::hidl_enum_range<T>();
@@ -214,15 +226,23 @@
 }
 
 HalResult<Capabilities> AidlHalWrapper::getCapabilities() {
-    int32_t capabilities = 0;
-    auto result = mHandle->getCapabilities(&capabilities);
-    return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
+    std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
+    static auto loadFn = [this]() {
+        int32_t capabilities = 0;
+        auto result = mHandle->getCapabilities(&capabilities);
+        return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
+    };
+    return loadCached<Capabilities>(loadFn, mCapabilities);
 }
 
 HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() {
-    std::vector<Effect> supportedEffects;
-    auto result = mHandle->getSupportedEffects(&supportedEffects);
-    return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
+    std::lock_guard<std::mutex> lock(mSupportedEffectsMutex);
+    static auto loadFn = [this]() {
+        std::vector<Effect> supportedEffects;
+        auto result = mHandle->getSupportedEffects(&supportedEffects);
+        return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
+    };
+    return loadCached<std::vector<Effect>>(loadFn, mSupportedEffects);
 }
 
 HalResult<milliseconds> AidlHalWrapper::performEffect(
@@ -279,10 +299,9 @@
 }
 
 HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilities() {
-    hardware::Return<bool> result = mHandleV1_0->supportsAmplitudeControl();
-    Capabilities capabilities =
-            result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
-    return HalResult<Capabilities>::fromReturn(result, capabilities);
+    std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
+    return loadCached<Capabilities>(std::bind(&HidlHalWrapperV1_0::getCapabilitiesInternal, this),
+                                    mCapabilities);
 }
 
 HalResult<std::vector<Effect>> HidlHalWrapperV1_0::getSupportedEffects() {
@@ -308,6 +327,13 @@
     return HalResult<void>::unsupported();
 }
 
+HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilitiesInternal() {
+    hardware::Return<bool> result = mHandleV1_0->supportsAmplitudeControl();
+    Capabilities capabilities =
+            result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
+    return HalResult<Capabilities>::fromReturn(result, capabilities);
+}
+
 // -------------------------------------------------------------------------------------------------
 
 HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(Effect effect, EffectStrength strength,
@@ -355,19 +381,6 @@
     return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
 }
 
-HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilities() {
-    HalResult<Capabilities> parentResult = HidlHalWrapperV1_2::getCapabilities();
-    if (!parentResult.isOk()) {
-        // Loading for versions up to v1.2 already failed, so propagate failure.
-        return parentResult;
-    }
-
-    Capabilities capabilities = parentResult.value();
-    auto result = mHandleV1_3->supportsExternalControl();
-    capabilities |= result.withDefault(false) ? Capabilities::EXTERNAL_CONTROL : Capabilities::NONE;
-    return HalResult<Capabilities>::fromReturn(result, capabilities);
-}
-
 HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(Effect effect, EffectStrength strength,
                                                           const std::function<void()>&) {
     if (isStaticCastValid<V1_0::Effect>(effect)) {
@@ -392,6 +405,19 @@
     return HalResult<milliseconds>::unsupported();
 }
 
+HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
+    HalResult<Capabilities> parentResult = HidlHalWrapperV1_2::getCapabilitiesInternal();
+    if (!parentResult.isOk()) {
+        // Loading for previous HAL versions already failed, so propagate failure.
+        return parentResult;
+    }
+
+    Capabilities capabilities = parentResult.value();
+    auto result = mHandleV1_3->supportsExternalControl();
+    capabilities |= result.withDefault(false) ? Capabilities::EXTERNAL_CONTROL : Capabilities::NONE;
+    return HalResult<Capabilities>::fromReturn(result, capabilities);
+}
+
 // -------------------------------------------------------------------------------------------------
 
 }; // namespace vibrator
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 3d56c43..1a1f64b 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -181,6 +181,11 @@
 
 private:
     const sp<hardware::vibrator::IVibrator> mHandle;
+    std::mutex mCapabilitiesMutex;
+    std::mutex mSupportedEffectsMutex;
+    std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
+    std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
+            GUARDED_BY(mSupportedEffectsMutex);
 };
 
 // Wrapper for the HDIL Vibrator HAL v1.0.
@@ -215,6 +220,11 @@
 
 protected:
     const sp<hardware::vibrator::V1_0::IVibrator> mHandleV1_0;
+    std::mutex mCapabilitiesMutex;
+    std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
+
+    // Loads directly from IVibrator handle, skipping the mCapabilities cache.
+    virtual HalResult<Capabilities> getCapabilitiesInternal();
 };
 
 // Wrapper for the HDIL Vibrator HAL v1.1.
@@ -255,7 +265,6 @@
             mHandleV1_3(hardware::vibrator::V1_3::IVibrator::castFrom(handleV1_0)) {}
 
     virtual HalResult<void> setExternalControl(bool enabled) override;
-    virtual HalResult<Capabilities> getCapabilities() override;
 
     virtual HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
@@ -263,6 +272,8 @@
 
 protected:
     const sp<hardware::vibrator::V1_3::IVibrator> mHandleV1_3;
+
+    virtual HalResult<Capabilities> getCapabilitiesInternal() override;
 };
 
 // -------------------------------------------------------------------------------------------------
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index a4cdfae..6db449a 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -22,6 +22,7 @@
 #include <gtest/gtest.h>
 
 #include <utils/Log.h>
+#include <thread>
 
 #include <vibratorservice/VibratorHalWrapper.h>
 
@@ -243,38 +244,76 @@
     ASSERT_TRUE(mWrapper->alwaysOnDisable(3).isFailed());
 }
 
-TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilities) {
+TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) {
     EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
             .Times(Exactly(3))
-            .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())))
             .WillOnce(
                     Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
-            .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+
+    ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported());
+    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
 
     auto result = mWrapper->getCapabilities();
     ASSERT_TRUE(result.isOk());
     ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
-    ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported());
-    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
 }
 
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffects) {
+TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesCachesResult) {
+    EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getCapabilities();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+        }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsDoesNotCacheFailedResult) {
     std::vector<Effect> supportedEffects;
     supportedEffects.push_back(Effect::CLICK);
     supportedEffects.push_back(Effect::TICK);
 
     EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
             .Times(Exactly(3))
-            .WillOnce(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())))
             .WillOnce(
                     Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
-            .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+
+    ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
+    ASSERT_TRUE(mWrapper->getSupportedEffects().isFailed());
 
     auto result = mWrapper->getSupportedEffects();
     ASSERT_TRUE(result.isOk());
     ASSERT_EQ(supportedEffects, result.value());
-    ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
-    ASSERT_TRUE(mWrapper->getSupportedEffects().isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsCachesResult) {
+    std::vector<Effect> supportedEffects;
+    supportedEffects.push_back(Effect::CLICK);
+    supportedEffects.push_back(Effect::TICK);
+
+    EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getSupportedEffects();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(supportedEffects, result.value());
+        }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestPerformEffect) {
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
index bc1d14d..7f1016f 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -22,6 +22,7 @@
 #include <gtest/gtest.h>
 
 #include <utils/Log.h>
+#include <thread>
 
 #include <vibratorservice/VibratorHalWrapper.h>
 
@@ -186,29 +187,48 @@
     ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilities) {
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
     EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
-            .Times(Exactly(3))
-            .WillOnce([]() { return hardware::Return<bool>(true); })
-            .WillOnce([]() { return hardware::Return<bool>(false); })
-            .WillRepeatedly([]() {
+            .Times(Exactly(2))
+            .WillOnce([]() {
                 return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
-            });
+            })
+            .WillRepeatedly([]() { return hardware::Return<bool>(true); });
 
-    // Amplitude control enabled.
+    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+
     auto result = mWrapper->getCapabilities();
     ASSERT_TRUE(result.isOk());
     ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
-
-    // Amplitude control disabled.
-    result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
-
-    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedEffects) {
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesWithoutAmplitudeControl) {
+    EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
+        return hardware::Return<bool>(false);
+    });
+
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesCachesResult) {
+    EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
+        return hardware::Return<bool>(true);
+    });
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getCapabilities();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+        }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedEffectsUnsupported) {
     ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
 }
 
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
index f44c6d8..a799257 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
@@ -22,6 +22,7 @@
 #include <gtest/gtest.h>
 
 #include <utils/Log.h>
+#include <thread>
 
 #include <vibratorservice/VibratorHalWrapper.h>
 
@@ -110,21 +111,49 @@
         EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
             return hardware::Return<bool>(true);
         });
+    }
 
-        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
-            return hardware::Return<bool>(false);
-        });
-        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
-            return hardware::Return<bool>(true);
-        });
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL,
+              result.value());
+}
 
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyAmplitudeControl) {
+    {
+        InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
             return hardware::Return<bool>(true);
         });
         EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
             return hardware::Return<bool>(false);
         });
+    }
 
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyExternalControl) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
+            return hardware::Return<bool>(false);
+        });
+        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+            return hardware::Return<bool>(true);
+        });
+    }
+
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesNone) {
+    {
+        InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
                 .Times(Exactly(1))
                 .WillRepeatedly([]() { return hardware::Return<bool>(false); });
@@ -133,25 +162,8 @@
         });
     }
 
-    // Both enabled.
     auto result = mWrapper->getCapabilities();
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL,
-              result.value());
-
-    // Amplitude control disabled.
-    result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, result.value());
-
-    // External control disabled.
-    result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
-
-    // Both disabled.
-    result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
     ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
 }
 
@@ -178,6 +190,73 @@
     ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
 }
 
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesCachesResult) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+        EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+            return hardware::Return<bool>(false);
+        });
+    }
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getCapabilities();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+        }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() {
+                    return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+                });
+
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+        EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() {
+                    return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+                });
+
+        EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+        EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+                .Times(Exactly(1))
+                .WillRepeatedly([]() { return hardware::Return<bool>(false); });
+    }
+
+    // Call to supportsAmplitudeControl failed.
+    auto result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isFailed());
+
+    // Call to supportsExternalControl failed.
+    result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isFailed());
+
+    // Returns successful result from third call.
+    result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+
+    // Returns cached successful result.
+    result = mWrapper->getCapabilities();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
 TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
     EXPECT_CALL(*mMockHal.get(),
                 perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))