Merge "Use sequence numbers to synchronize enabling Pointer Capture (1/2)"
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 74be7ce..5082eb0 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -307,6 +307,8 @@
         }
     }
 
+    out << "is_dexopt_blocked:" << android::installd::is_dexopt_blocked() << endl;
+
     out << endl;
     out.flush();
 
@@ -2401,7 +2403,8 @@
         const std::optional<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,
         const std::optional<std::string>& profileName,
         const std::optional<std::string>& dexMetadataPath,
-        const std::optional<std::string>& compilationReason) {
+        const std::optional<std::string>& compilationReason,
+        bool* aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PATH(apkPath);
@@ -2429,12 +2432,20 @@
     const char* dm_path = getCStr(dexMetadataPath);
     const char* compilation_reason = getCStr(compilationReason);
     std::string error_msg;
+    bool completed = false; // not necessary but for compiler
     int res = android::installd::dexopt(apk_path, uid, pkgname, instruction_set, dexoptNeeded,
             oat_dir, dexFlags, compiler_filter, volume_uuid, class_loader_context, se_info,
-            downgrade, targetSdkVersion, profile_name, dm_path, compilation_reason, &error_msg);
+            downgrade, targetSdkVersion, profile_name, dm_path, compilation_reason, &error_msg,
+            &completed);
+    *aidl_return = completed;
     return res ? error(res, error_msg) : ok();
 }
 
+binder::Status InstalldNativeService::controlDexOptBlocking(bool block) {
+    android::installd::control_dexopt_blocking(block);
+    return ok();
+}
+
 binder::Status InstalldNativeService::compileLayouts(const std::string& apkPath,
                                                      const std::string& packageName,
                                                      const std ::string& outDexFile, int uid,
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index ea0c945..ae257df 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -117,7 +117,10 @@
             const std::optional<std::string>& seInfo, bool downgrade,
             int32_t targetSdkVersion, const std::optional<std::string>& profileName,
             const std::optional<std::string>& dexMetadataPath,
-            const std::optional<std::string>& compilationReason);
+            const std::optional<std::string>& compilationReason,
+            bool* aidl_return);
+
+    binder::Status controlDexOptBlocking(bool block);
 
     binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName,
                                   const std::string& outDexFile, int uid, bool* _aidl_return);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 3d32f61..637a9f2 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -55,7 +55,8 @@
             @utf8InCpp String packageName, int appId,
             @utf8InCpp String seInfo, int targetSdkVersion, @utf8InCpp String fromCodePath);
 
-    void dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
+    // Returns false if it is cancelled. Returns true if it is completed or have other errors.
+    boolean dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
             @utf8InCpp String instructionSet, int dexoptNeeded,
             @nullable @utf8InCpp String outputPath, int dexFlags,
             @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid,
@@ -64,6 +65,9 @@
             @nullable @utf8InCpp String profileName,
             @nullable @utf8InCpp String dexMetadataPath,
             @nullable @utf8InCpp String compilationReason);
+    // Blocks (when block is true) or unblock (when block is false) dexopt.
+    // Blocking also invloves cancelling the currently running dexopt.
+    void controlDexOptBlocking(boolean block);
     boolean compileLayouts(@utf8InCpp String apkPath, @utf8InCpp String packageName,
             @utf8InCpp String outDexFile, int uid);
 
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index d678281..f3ec63f 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -15,8 +15,8 @@
  */
 #define LOG_TAG "installd"
 
-#include <array>
 #include <fcntl.h>
+#include <signal.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/capability.h>
@@ -28,10 +28,14 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <array>
 #include <iomanip>
+#include <mutex>
+#include <unordered_set>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/no_destructor.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -47,6 +51,7 @@
 #include <selinux/android.h>
 #include <server_configurable_flags/get_flags.h>
 #include <system/thread_defs.h>
+#include <utils/Mutex.h>
 
 #include "dexopt.h"
 #include "dexopt_return_codes.h"
@@ -69,6 +74,76 @@
 using android::base::WriteFully;
 using android::base::unique_fd;
 
+namespace {
+
+class DexOptStatus {
+ public:
+    // Check if dexopt is cancelled and fork if it is not cancelled.
+    // cancelled is set to true if cancelled. Otherwise it will be set to false.
+    // If it is not cancelled, it will return the return value of fork() call.
+    // If cancelled, fork will not happen and it will return -1.
+    pid_t check_cancellation_and_fork(/* out */ bool *cancelled) {
+        std::lock_guard<std::mutex> lock(dexopt_lock_);
+        if (dexopt_blocked_) {
+            *cancelled = true;
+            return -1;
+        }
+        pid_t pid = fork();
+        *cancelled = false;
+        if (pid > 0) { // parent
+            dexopt_pids_.insert(pid);
+        }
+        return pid;
+    }
+
+    // Returns true if pid was killed (is in killed list). It could have finished if killing
+    // happened after the process is finished.
+    bool check_if_killed_and_remove_dexopt_pid(pid_t pid) {
+        std::lock_guard<std::mutex> lock(dexopt_lock_);
+        dexopt_pids_.erase(pid);
+        if (dexopt_killed_pids_.erase(pid) == 1) {
+            return true;
+        }
+        return false;
+    }
+
+    // Tells whether dexopt is blocked or not.
+    bool is_dexopt_blocked() {
+        std::lock_guard<std::mutex> lock(dexopt_lock_);
+        return dexopt_blocked_;
+    }
+
+    // Enable or disable dexopt blocking.
+    void control_dexopt_blocking(bool block) {
+        std::lock_guard<std::mutex> lock(dexopt_lock_);
+        dexopt_blocked_ = block;
+        if (!block) {
+            return;
+        }
+        // Blocked, also kill currently running tasks
+        for (auto pid : dexopt_pids_) {
+            LOG(INFO) << "control_dexopt_blocking kill pid:" << pid;
+            kill(pid, SIGKILL);
+            dexopt_killed_pids_.insert(pid);
+        }
+        dexopt_pids_.clear();
+    }
+
+ private:
+    std::mutex dexopt_lock_;
+    // when true, dexopt is blocked and will not run.
+    bool dexopt_blocked_ GUARDED_BY(dexopt_lock_) = false;
+    // PIDs of child process while runinng dexopt.
+    // If the child process is finished, it should be removed.
+    std::unordered_set<pid_t> dexopt_pids_ GUARDED_BY(dexopt_lock_);
+    // PIDs of child processes killed by cancellation.
+    std::unordered_set<pid_t> dexopt_killed_pids_ GUARDED_BY(dexopt_lock_);
+};
+
+android::base::NoDestructor<DexOptStatus> dexopt_status_;
+
+} // namespace
+
 namespace android {
 namespace installd {
 
@@ -1525,23 +1600,46 @@
     return ss.str();
 }
 
+void control_dexopt_blocking(bool block) {
+    dexopt_status_->control_dexopt_blocking(block);
+}
+
+bool is_dexopt_blocked() {
+    return dexopt_status_->is_dexopt_blocked();
+}
+
+enum SecondaryDexOptProcessResult {
+    kSecondaryDexOptProcessOk = 0,
+    kSecondaryDexOptProcessCancelled = 1,
+    kSecondaryDexOptProcessError = 2
+};
+
 // Processes the dex_path as a secondary dex files and return true if the path dex file should
-// be compiled. Returns false for errors (logged) or true if the secondary dex path was process
-// successfully.
-// When returning true, the output parameters will be:
+// be compiled.
+// Returns: kSecondaryDexOptProcessError for errors (logged).
+//          kSecondaryDexOptProcessOk if the secondary dex path was process successfully.
+//          kSecondaryDexOptProcessCancelled if the processing was cancelled.
+//
+// When returning kSecondaryDexOptProcessOk, the output parameters will be:
 //   - is_public_out: whether or not the oat file should not be made public
 //   - dexopt_needed_out: valid OatFileAsssitant::DexOptNeeded
 //   - oat_dir_out: the oat dir path where the oat file should be stored
-static bool process_secondary_dex_dexopt(const std::string& dex_path, const char* pkgname,
-        int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set,
-        const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out,
-        std::string* oat_dir_out, bool downgrade, const char* class_loader_context,
-        const std::vector<std::string>& context_dex_paths, /* out */ std::string* error_msg) {
+static SecondaryDexOptProcessResult process_secondary_dex_dexopt(const std::string& dex_path,
+        const char* pkgname, int dexopt_flags, const char* volume_uuid, int uid,
+        const char* instruction_set, const char* compiler_filter, bool* is_public_out,
+        int* dexopt_needed_out, std::string* oat_dir_out, bool downgrade,
+        const char* class_loader_context, const std::vector<std::string>& context_dex_paths,
+        /* out */ std::string* error_msg) {
     LOG(DEBUG) << "Processing secondary dex path " << dex_path;
+
+    if (dexopt_status_->is_dexopt_blocked()) {
+        return kSecondaryDexOptProcessCancelled;
+    }
+
     int storage_flag;
     if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag, error_msg)) {
         LOG(ERROR) << *error_msg;
-        return false;
+        return kSecondaryDexOptProcessError;
     }
     // Compute the oat dir as it's not easy to extract it from the child computation.
     char oat_path[PKG_PATH_MAX];
@@ -1550,11 +1648,15 @@
     if (!create_secondary_dex_oat_layout(
             dex_path, instruction_set, oat_dir, oat_isa_dir, oat_path, error_msg)) {
         LOG(ERROR) << "Could not create secondary odex layout: " << *error_msg;
-        return false;
+        return kSecondaryDexOptProcessError;
     }
     oat_dir_out->assign(oat_dir);
 
-    pid_t pid = fork();
+    bool cancelled = false;
+    pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled);
+    if (cancelled) {
+        return kSecondaryDexOptProcessCancelled;
+    }
     if (pid == 0) {
         // child -- drop privileges before continuing.
         drop_capabilities(uid);
@@ -1623,12 +1725,17 @@
 
     /* parent */
     int result = wait_child(pid);
+    cancelled = dexopt_status_->check_if_killed_and_remove_dexopt_pid(pid);
     if (!WIFEXITED(result)) {
+        if ((WTERMSIG(result) == SIGKILL) && cancelled) {
+            LOG(INFO) << "dexoptanalyzer cancelled for path:" << dex_path;
+            return kSecondaryDexOptProcessCancelled;
+        }
         *error_msg = StringPrintf("dexoptanalyzer failed for path %s: 0x%04x",
                                   dex_path.c_str(),
                                   result);
         LOG(ERROR) << *error_msg;
-        return false;
+        return kSecondaryDexOptProcessError;
     }
     result = WEXITSTATUS(result);
     // Check that we successfully executed dexoptanalyzer.
@@ -1656,7 +1763,7 @@
     // It is ok to check this flag outside in the parent process.
     *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) && is_file_public(dex_path);
 
-    return success;
+    return success ? kSecondaryDexOptProcessOk : kSecondaryDexOptProcessError;
 }
 
 static std::string format_dexopt_error(int status, const char* dex_path) {
@@ -1670,17 +1777,29 @@
   return StringPrintf("Dex2oat invocation for %s failed with 0x%04x", dex_path, status);
 }
 
+
 int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* instruction_set,
         int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
         const char* volume_uuid, const char* class_loader_context, const char* se_info,
         bool downgrade, int target_sdk_version, const char* profile_name,
-        const char* dex_metadata_path, const char* compilation_reason, std::string* error_msg) {
+        const char* dex_metadata_path, const char* compilation_reason, std::string* error_msg,
+        /* out */ bool* completed) {
     CHECK(pkgname != nullptr);
     CHECK(pkgname[0] != 0);
     CHECK(error_msg != nullptr);
     CHECK_EQ(dexopt_flags & ~DEXOPT_MASK, 0)
         << "dexopt flags contains unknown fields: " << dexopt_flags;
 
+    bool local_completed; // local placeholder for nullptr case
+    if (completed == nullptr) {
+        completed = &local_completed;
+    }
+    *completed = true;
+    if (dexopt_status_->is_dexopt_blocked()) {
+        *completed = false;
+        return 0;
+    }
+
     if (!validate_dex_path_size(dex_path)) {
         *error_msg = StringPrintf("Failed to validate %s", dex_path);
         return -1;
@@ -1712,14 +1831,19 @@
             *error_msg = "Failed acquiring context dex paths";
             return -1;  // We had an error, logged in the process method.
         }
-
-        if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid,
-                instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str,
-                downgrade, class_loader_context, context_dex_paths, error_msg)) {
+        SecondaryDexOptProcessResult sec_dex_result = process_secondary_dex_dexopt(dex_path,
+                pkgname, dexopt_flags, volume_uuid, uid,instruction_set, compiler_filter,
+                &is_public, &dexopt_needed, &oat_dir_str, downgrade, class_loader_context,
+                context_dex_paths, error_msg);
+        if (sec_dex_result == kSecondaryDexOptProcessOk) {
             oat_dir = oat_dir_str.c_str();
             if (dexopt_needed == NO_DEXOPT_NEEDED) {
                 return 0;  // Nothing to do, report success.
             }
+        } else if (sec_dex_result == kSecondaryDexOptProcessCancelled) {
+            // cancelled, not an error.
+            *completed = false;
+            return 0;
         } else {
             if (error_msg->empty()) {  // TODO: Make this a CHECK.
                 *error_msg = "Failed processing secondary.";
@@ -1849,7 +1973,11 @@
                       use_jitzygote_image,
                       compilation_reason);
 
-    pid_t pid = fork();
+    bool cancelled = false;
+    pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled);
+    if (cancelled) {
+        return 0;
+    }
     if (pid == 0) {
         // Need to set schedpolicy before dropping privileges
         // for cgroup migration. See details at b/175178520.
@@ -1867,9 +1995,16 @@
         runner.Exec(DexoptReturnCodes::kDex2oatExec);
     } else {
         int res = wait_child(pid);
+        bool cancelled = dexopt_status_->check_if_killed_and_remove_dexopt_pid(pid);
         if (res == 0) {
             LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' (success) ---";
         } else {
+            if ((WTERMSIG(res) == SIGKILL) && cancelled) {
+                LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- cancelled";
+                // cancelled, not an error
+                *completed = false;
+                return 0;
+            }
             LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- status=0x"
                          << std::hex << std::setw(4) << res << ", process failed";
             *error_msg = format_dexopt_error(res, dex_path);
@@ -1877,12 +2012,14 @@
         }
     }
 
+    // TODO(b/156537504) Implement SWAP of completed files
     // We've been successful, don't delete output.
     out_oat.DisableCleanup();
     out_vdex.DisableCleanup();
     out_image.DisableCleanup();
     reference_profile.DisableCleanup();
 
+    *completed = true;
     return 0;
 }
 
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 5a637b1..12579b0 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -121,11 +121,18 @@
         const std::string& pkgname, int uid, const std::optional<std::string>& volume_uuid,
         int storage_flag, std::vector<uint8_t>* out_secondary_dex_hash);
 
+// completed pass false if it is canceled. Otherwise it will be true even if there is other
+// error.
 int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
         int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
         const char* volume_uuid, const char* class_loader_context, const char* se_info,
         bool downgrade, int target_sdk_version, const char* profile_name,
-        const char* dexMetadataPath, const char* compilation_reason, std::string* error_msg);
+        const char* dexMetadataPath, const char* compilation_reason, std::string* error_msg,
+        /* out */ bool* completed = nullptr);
+
+bool is_dexopt_blocked();
+
+void control_dexopt_blocking(bool block);
 
 bool calculate_oat_file_path_default(char path[PKG_PATH_MAX], const char *oat_dir,
         const char *apk_path, const char *instruction_set);
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 7e7e513..ea26955 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -232,6 +232,7 @@
 
     virtual void TearDown() {
         if (!kDebug) {
+            service_->controlDexOptBlocking(false);
             service_->destroyAppData(
                 volume_uuid_, package_name_, kTestUserId, kAppDataFlags, ce_data_inode_);
             run_cmd("rm -rf " + app_apk_dir_);
@@ -347,7 +348,7 @@
     void CompileSecondaryDex(const std::string& path, int32_t dex_storage_flag,
             bool should_binder_call_succeed, bool should_dex_be_compiled = true,
             /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1,
-            const char* class_loader_context = nullptr) {
+            const char* class_loader_context = nullptr, bool expect_completed = true) {
         if (uid == -1) {
             uid = kTestAppUid;
         }
@@ -364,6 +365,7 @@
         std::optional<std::string> dm_path;
         std::optional<std::string> compilation_reason;
 
+        bool completed = false;
         binder::Status result = service_->dexopt(path,
                                                  uid,
                                                  package_name_,
@@ -379,8 +381,10 @@
                                                  target_sdk_version,
                                                  profile_name,
                                                  dm_path,
-                                                 compilation_reason);
+                                                 compilation_reason,
+                                                 &completed);
         ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
+        ASSERT_EQ(expect_completed, completed);
         int expected_access = should_dex_be_compiled ? 0 : -1;
         std::string odex = GetSecondaryDexArtifact(path, "odex");
         std::string vdex = GetSecondaryDexArtifact(path, "vdex");
@@ -431,6 +435,11 @@
         ASSERT_EQ(mode, st.st_mode);
     }
 
+    void AssertNoFile(const std::string& file) {
+        struct stat st;
+        ASSERT_EQ(-1, stat(file.c_str(), &st));
+    }
+
     void CompilePrimaryDexOk(std::string compiler_filter,
                              int32_t dex_flags,
                              const char* oat_dir,
@@ -447,6 +456,7 @@
                           dm_path,
                           downgrade,
                           true,
+                          true,
                           binder_result);
     }
 
@@ -466,6 +476,27 @@
                           dm_path,
                           downgrade,
                           false,
+                          true,
+                          binder_result);
+    }
+
+    void CompilePrimaryDexCancelled(std::string compiler_filter,
+                               int32_t dex_flags,
+                               const char* oat_dir,
+                               int32_t uid,
+                               int32_t dexopt_needed,
+                               binder::Status* binder_result = nullptr,
+                               const char* dm_path = nullptr,
+                               bool downgrade = false) {
+        CompilePrimaryDex(compiler_filter,
+                          dex_flags,
+                          oat_dir,
+                          uid,
+                          dexopt_needed,
+                          dm_path,
+                          downgrade,
+                          true, // should_binder_call_succeed
+                          false, // expect_completed
                           binder_result);
     }
 
@@ -477,6 +508,7 @@
                            const char* dm_path,
                            bool downgrade,
                            bool should_binder_call_succeed,
+                           bool expect_completed,
                            /*out */ binder::Status* binder_result) {
         std::optional<std::string> out_path = oat_dir ? std::make_optional<std::string>(oat_dir) : std::nullopt;
         std::string class_loader_context = "PCL[]";
@@ -491,6 +523,7 @@
                 dm_path_opt, &prof_result));
         ASSERT_TRUE(prof_result);
 
+        bool completed = false;
         binder::Status result = service_->dexopt(apk_path_,
                                                  uid,
                                                  package_name_,
@@ -506,8 +539,10 @@
                                                  target_sdk_version,
                                                  profile_name,
                                                  dm_path_opt,
-                                                 compilation_reason);
+                                                 compilation_reason,
+                                                 &completed);
         ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
+        ASSERT_EQ(expect_completed, completed);
 
         if (!should_binder_call_succeed) {
             if (binder_result != nullptr) {
@@ -525,11 +560,20 @@
 
         bool is_public = (dex_flags & DEXOPT_PUBLIC) != 0;
         mode_t mode = S_IFREG | (is_public ? 0644 : 0640);
-        CheckFileAccess(odex, kSystemUid, uid, mode);
-        CheckFileAccess(vdex, kSystemUid, uid, mode);
+        if (expect_completed) {
+            CheckFileAccess(odex, kSystemUid, uid, mode);
+            CheckFileAccess(vdex, kSystemUid, uid, mode);
+        } else {
+            AssertNoFile(odex);
+            AssertNoFile(vdex);
+        }
 
         if (compiler_filter == "speed-profile") {
-            CheckFileAccess(art, kSystemUid, uid, mode);
+            if (expect_completed) {
+                CheckFileAccess(art, kSystemUid, uid, mode);
+            } else {
+                AssertNoFile(art);
+            }
         }
         if (binder_result != nullptr) {
             *binder_result = result;
@@ -750,6 +794,28 @@
                         empty_dm_file_.c_str());
 }
 
+TEST_F(DexoptTest, DexoptBlockPrimary) {
+    LOG(INFO) << "DexoptPrimaryPublic";
+    service_->controlDexOptBlocking(true);
+    CompilePrimaryDexCancelled("verify",
+                        DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC,
+                        app_oat_dir_.c_str(),
+                        kTestAppGid,
+                        DEX2OAT_FROM_SCRATCH, nullptr, nullptr);
+    service_->controlDexOptBlocking(false);
+}
+
+TEST_F(DexoptTest, DexoptUnblockPrimary) {
+    LOG(INFO) << "DexoptPrimaryPublic";
+    service_->controlDexOptBlocking(true);
+    service_->controlDexOptBlocking(false);
+    CompilePrimaryDexOk("verify",
+                        DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC,
+                        app_oat_dir_.c_str(),
+                        kTestAppGid,
+                        DEX2OAT_FROM_SCRATCH, nullptr, nullptr);
+}
+
 TEST_F(DexoptTest, DeleteDexoptArtifactsData) {
     LOG(INFO) << "DeleteDexoptArtifactsData";
     TestDeleteOdex(/*in_dalvik_cache=*/ false);
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 8d27eed..fe2da18 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -135,3 +135,23 @@
         "libbinder_ndk_sys",
     ],
 }
+
+rust_test {
+    name: "libbinder_ndk_bindgen_test",
+    srcs: [":libbinder_ndk_bindgen"],
+    crate_name: "binder_ndk_bindgen",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    clippy_lints: "none",
+    lints: "none",
+}
+
+rust_test {
+    name: "libbinder_rpc_unstable_bindgen_test",
+    srcs: [":libbinder_rpc_unstable_bindgen"],
+    crate_name: "binder_rpc_unstable_bindgen",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    clippy_lints: "none",
+    lints: "none",
+}
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index b3b726d..694bda6 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -424,13 +424,11 @@
         ASSERT_TRUE(result.valid());
         auto [status, fence] = result.get();
 
-        int fd = fence.release();
-        if (fd >= 0) {
-            sync_wait(fd, -1);
-            close(fd);
+        ASSERT_EQ(NO_ERROR, status);
+        if (fence.ok()) {
+            sync_wait(fence.get(), -1);
         }
 
-        ASSERT_EQ(NO_ERROR, status);
         if (layers.size() > 0 && mGLESRE != nullptr) {
             ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
         }
@@ -1317,31 +1315,9 @@
             mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd());
 
     ASSERT_TRUE(result.valid());
-    auto [status, _] = result.get();
+    auto [status, fence] = result.get();
     ASSERT_EQ(BAD_VALUE, status);
-}
-
-TEST_P(RenderEngineTest, drawLayers_nullOutputFence) {
-    initializeRenderEngine();
-
-    renderengine::DisplaySettings settings;
-    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
-    settings.physicalDisplay = fullscreenRect();
-    settings.clip = fullscreenRect();
-
-    std::vector<const renderengine::LayerSettings*> layers;
-    renderengine::LayerSettings layer;
-    layer.geometry.boundaries = fullscreenRect().toFloatRect();
-    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
-    layer.alpha = 1.0;
-    layers.push_back(&layer);
-
-    std::future<renderengine::RenderEngineResult> result =
-            mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
-    ASSERT_TRUE(result.valid());
-    auto [status, _] = result.get();
-    ASSERT_EQ(NO_ERROR, status);
-    expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+    ASSERT_FALSE(fence.ok());
 }
 
 TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
@@ -1369,8 +1345,13 @@
     std::future<renderengine::RenderEngineResult> result =
             mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd());
     ASSERT_TRUE(result.valid());
-    auto [status, _] = result.get();
+    auto [status, fence] = result.get();
+
     ASSERT_EQ(NO_ERROR, status);
+    if (fence.ok()) {
+        sync_wait(fence.get(), -1);
+    }
+
     ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
     expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
 }
@@ -1780,11 +1761,10 @@
     ASSERT_TRUE(resultTwo.valid());
     auto [statusTwo, fenceTwo] = resultTwo.get();
     ASSERT_EQ(NO_ERROR, statusTwo);
-
-    const int fd = fenceTwo.get();
-    if (fd >= 0) {
-        sync_wait(fd, -1);
+    if (fenceTwo.ok()) {
+        sync_wait(fenceTwo.get(), -1);
     }
+
     // Only cleanup the first time.
     EXPECT_FALSE(mRE->canSkipPostRenderCleanup());
     mRE->cleanupPostRender();
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 4cc8f90..ac5f6b6 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -470,6 +470,23 @@
     getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"),
                                                          mParameters.orientationAware);
 
+    mParameters.orientation = Parameters::Orientation::ORIENTATION_0;
+    String8 orientationString;
+    if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientation"),
+                                                             orientationString)) {
+        if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
+            ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
+        } else if (orientationString == "ORIENTATION_90") {
+            mParameters.orientation = Parameters::Orientation::ORIENTATION_90;
+        } else if (orientationString == "ORIENTATION_180") {
+            mParameters.orientation = Parameters::Orientation::ORIENTATION_180;
+        } else if (orientationString == "ORIENTATION_270") {
+            mParameters.orientation = Parameters::Orientation::ORIENTATION_270;
+        } else if (orientationString != "ORIENTATION_0") {
+            ALOGW("Invalid value for touch.orientation: '%s'", orientationString.string());
+        }
+    }
+
     mParameters.hasAssociatedDisplay = false;
     mParameters.associatedDisplayIsExternal = false;
     if (mParameters.orientationAware ||
@@ -508,6 +525,7 @@
                          toString(mParameters.associatedDisplayIsExternal),
                          mParameters.uniqueDisplayId.c_str());
     dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
+    dump += INDENT4 "Orientation: " + NamedEnum::string(mParameters.orientation) + "\n";
 }
 
 void TouchInputMapper::configureRawPointerAxes() {
@@ -669,7 +687,13 @@
             int32_t naturalPhysicalWidth, naturalPhysicalHeight;
             int32_t naturalPhysicalLeft, naturalPhysicalTop;
             int32_t naturalDeviceWidth, naturalDeviceHeight;
-            switch (mViewport.orientation) {
+
+            // Apply the inverse of the input device orientation so that the surface is configured
+            // in the same orientation as the device. The input device orientation will be
+            // re-applied to mSurfaceOrientation.
+            const int32_t naturalSurfaceOrientation =
+                    (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4;
+            switch (naturalSurfaceOrientation) {
                 case DISPLAY_ORIENTATION_90:
                     naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
                     naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
@@ -752,6 +776,10 @@
                 mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation
                                                                    : DISPLAY_ORIENTATION_0;
             }
+
+            // Apply the input device orientation for the device.
+            mSurfaceOrientation =
+                    (mSurfaceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
         } else {
             mPhysicalWidth = rawWidth;
             mPhysicalHeight = rawHeight;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 920f842..e104220 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -204,6 +204,15 @@
         bool hasAssociatedDisplay;
         bool associatedDisplayIsExternal;
         bool orientationAware;
+
+        enum class Orientation : int32_t {
+            ORIENTATION_0 = DISPLAY_ORIENTATION_0,
+            ORIENTATION_90 = DISPLAY_ORIENTATION_90,
+            ORIENTATION_180 = DISPLAY_ORIENTATION_180,
+            ORIENTATION_270 = DISPLAY_ORIENTATION_270,
+        };
+        Orientation orientation;
+
         bool hasButtonUnderPad;
         std::string uniqueDisplayId;
 
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 91d246a..5c430f5 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -4704,6 +4704,8 @@
     void prepareLocationCalibration();
     int32_t toRawX(float displayX);
     int32_t toRawY(float displayY);
+    int32_t toRotatedRawX(float displayX);
+    int32_t toRotatedRawY(float displayY);
     float toCookedX(float rawX, float rawY);
     float toCookedY(float rawX, float rawY);
     float toDisplayX(int32_t rawX);
@@ -4786,6 +4788,14 @@
     return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT + RAW_Y_MIN);
 }
 
+int32_t TouchInputMapperTest::toRotatedRawX(float displayX) {
+    return int32_t(displayX * (RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_HEIGHT + RAW_X_MIN);
+}
+
+int32_t TouchInputMapperTest::toRotatedRawY(float displayY) {
+    return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_WIDTH + RAW_Y_MIN);
+}
+
 float TouchInputMapperTest::toCookedX(float rawX, float rawY) {
     AFFINE_TRANSFORM.applyTo(rawX, rawY);
     return rawX;
@@ -5532,6 +5542,172 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 }
 
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation0_RotatesMotions) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "1");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_0");
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs args;
+
+    // Orientation 0.
+    processDown(mapper, toRawX(50), toRawY(75));
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation90_RotatesMotions) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "1");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_90");
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs args;
+
+    // Orientation 90.
+    processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50));
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation180_RotatesMotions) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "1");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_180");
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs args;
+
+    // Orientation 180.
+    processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation270_RotatesMotions) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "1");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_270");
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs args;
+
+    // Orientation 270.
+    processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN);
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationSpecified_RotatesMotionWithDisplay) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_90");
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+    NotifyMotionArgs args;
+
+    // Orientation 90, Rotation 0.
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50));
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+
+    // Orientation 90, Rotation 90.
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+    processDown(mapper, toRotatedRawX(50), toRotatedRawY(75));
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+
+    // Orientation 90, Rotation 180.
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_180);
+    processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN);
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+
+    // Orientation 90, Rotation 270.
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_270);
+    processDown(mapper, RAW_X_MAX - toRotatedRawX(50) + RAW_X_MIN,
+                RAW_Y_MAX - toRotatedRawY(75) + RAW_Y_MIN);
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
 TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);